diff --git a/internal/m3uplus/main.go b/internal/m3uplus/main.go index 62bf77a..b6abc12 100644 --- a/internal/m3uplus/main.go +++ b/internal/m3uplus/main.go @@ -5,6 +5,7 @@ import ( "bytes" "fmt" "io" + "net/url" "regexp" "strconv" "strings" @@ -21,7 +22,7 @@ type Playlist struct { type Track struct { Name string Length float64 - URI string + URI *url.URL Tags map[string]string Raw string LineNumber int @@ -96,13 +97,20 @@ func decodeLine(playlist *Playlist, line string, lineNumber int) error { playlist.Tracks = append(playlist.Tracks, track) - case strings.HasPrefix(line, "http") || strings.HasPrefix(line, "udp"): - playlist.Tracks[len(playlist.Tracks)-1].URI = line + case IsUrl(line): + uri, _ := url.Parse(line) + playlist.Tracks[len(playlist.Tracks)-1].URI = uri } return nil } +// From https://stackoverflow.com/questions/25747580/ensure-a-uri-is-valid/25747925#25747925 +func IsUrl(str string) bool { + u, err := url.Parse(str) + return err == nil && u.Scheme != "" && u.Host != "" +} + var infoRegex = regexp.MustCompile(`([^\s="]+)=(?:"(.*?)"|(\d+))(?:,([.*^,]))?|#EXTINF:(-?\d*\s*)|,(.*)`) func decodeInfoLine(line string) (float64, string, map[string]string) { diff --git a/internal/providers/area51.go b/internal/providers/area51.go index e2c2f87..5d8a5a4 100644 --- a/internal/providers/area51.go +++ b/internal/providers/area51.go @@ -47,7 +47,7 @@ func (i *area51) ParseTrack(track m3u.Track, channelMap map[string]xmltv.Channel Name: nameVal, Logo: logoVal, Number: 0, - StreamURL: track.URI, + StreamURL: track.URI.String(), StreamID: 0, HD: strings.Contains(strings.ToLower(track.Name), "hd"), StreamFormat: "Unknown", diff --git a/internal/providers/custom.go b/internal/providers/custom.go index 6e0b824..ebf0d31 100644 --- a/internal/providers/custom.go +++ b/internal/providers/custom.go @@ -59,7 +59,7 @@ func (i *customProvider) ParseTrack(track m3u.Track, channelMap map[string]xmltv Name: nameVal, Logo: logoVal, Number: chanNum, - StreamURL: track.URI, + StreamURL: track.URI.String(), StreamID: chanNum, HD: strings.Contains(strings.ToLower(track.Name), "hd"), StreamFormat: "Unknown", diff --git a/internal/providers/iptv-epg.go b/internal/providers/iptv-epg.go index 258239b..f34c496 100644 --- a/internal/providers/iptv-epg.go +++ b/internal/providers/iptv-epg.go @@ -58,7 +58,7 @@ func (i *iptvepg) ParseTrack(track m3u.Track, channelMap map[string]xmltv.Channe Name: nameVal, Logo: logoVal, Number: channelNumber, - StreamURL: track.URI, + StreamURL: track.URI.String(), StreamID: channelNumber, HD: strings.Contains(strings.ToLower(track.Name), "hd"), StreamFormat: "Unknown", diff --git a/internal/providers/iris.go b/internal/providers/iris.go index c05814a..7271336 100644 --- a/internal/providers/iris.go +++ b/internal/providers/iris.go @@ -47,7 +47,7 @@ func (i *iris) ParseTrack(track m3u.Track, channelMap map[string]xmltv.Channel) Name: nameVal, Logo: logoVal, Number: 0, - StreamURL: track.URI, + StreamURL: track.URI.String(), StreamID: 0, HD: strings.Contains(strings.ToLower(track.Name), "hd"), StreamFormat: "Unknown", diff --git a/lineup.go b/lineup.go index 4eaa15c..53b6715 100644 --- a/lineup.go +++ b/lineup.go @@ -14,7 +14,7 @@ import ( "time" "github.com/spf13/viper" - "github.com/tellytv/go.schedulesdirect" + schedulesdirect "github.com/tellytv/go.schedulesdirect" m3u "github.com/tellytv/telly/internal/m3uplus" "github.com/tellytv/telly/internal/providers" "github.com/tellytv/telly/internal/xmltv" @@ -70,6 +70,8 @@ type lineup struct { channels map[int]hdHomeRunLineupItem sd *schedulesdirect.Client + + FfmpegEnabled bool } // newLineup returns a new lineup for the given config struct. @@ -95,10 +97,16 @@ func newLineup() *lineup { }) } + useFFMpeg := viper.IsSet("iptv.ffmpeg") + if useFFMpeg { + useFFMpeg = viper.GetBool("iptv.ffmpeg") + } + lineup := &lineup{ assignedChannelNumber: viper.GetInt("iptv.starting-channel"), xmlTVChannelNumbers: viper.GetBool("iptv.xmltv-channels"), channels: make(map[int]hdHomeRunLineupItem), + FfmpegEnabled: useFFMpeg, } if viper.IsSet("schedulesdirect.username") && viper.IsSet("schedulesdirect.password") { @@ -191,6 +199,7 @@ func (l *lineup) processProvider(provider providers.Provider) (int, error) { channel, processErr := l.processProviderChannel(channel, programmeMap) if processErr != nil { log.WithError(processErr).Errorln("error processing track") + continue } else if channel == nil { log.Infof("Channel %s was returned empty from the provider (%s)", track.Name, provider.Name()) continue @@ -227,6 +236,12 @@ func (l *lineup) prepareProvider(provider providers.Provider) (*m3u.Playlist, ma return nil, nil, nil, err } + for _, playlistTrack := range rawPlaylist.Tracks { + if (playlistTrack.URI.Scheme == "http" || playlistTrack.URI.Scheme == "udp") && !l.FfmpegEnabled { + log.Errorf("The playlist you tried to add has at least one entry using a protocol other than http or udp and you have ffmpeg disabled in your config. This will most likely not work. Offending URI is %s", playlistTrack.URI) + } + } + if closeM3UErr := reader.Close(); closeM3UErr != nil { log.WithError(closeM3UErr).Panicln("error when closing m3u reader") } diff --git a/routes.go b/routes.go index aaef5dd..581868a 100644 --- a/routes.go +++ b/routes.go @@ -185,20 +185,16 @@ func stream(lineup *lineup) gin.HandlerFunc { log.Infof("Serving channel number %d", channelID) - useFFMpeg := viper.IsSet("iptv.ffmpeg") - if useFFMpeg { - useFFMpeg = viper.GetBool("iptv.ffmpeg") - } - - if !useFFMpeg { + if !lineup.FfmpegEnabled { log.Debugf("Redirecting caller to %s", channelURI) - c.Redirect(http.StatusMovedPermanently, channelURI) + c.Redirect(http.StatusMovedPermanently, channelURI.String()) return } log.Infoln("Remuxing stream with ffmpeg") - run := exec.Command("ffmpeg", "-i", channelURI, "-codec", "copy", "-f", "mpegts", "pipe:1") + run := exec.Command("ffmpeg", "-i", channelURI.String(), "-codec", "copy", "-f", "mpegts", "pipe:1") + log.Debugf("Executing ffmpeg as \"%s\"", strings.Join(run.Args, " ")) ffmpegout, err := run.StdoutPipe() if err != nil { log.WithError(err).Errorln("StdoutPipe Error")