Skip to content

Commit

Permalink
More robust URI parsing in M3U files (#286)
Browse files Browse the repository at this point in the history
* Add ffmpeg command to debug log

* More robust URI parsing in M3U files

* Exit if processProviderChannel returns error

That function nowadays doesn't ever return an error,
but that might change in the future

* Warn user if URI is not http or udp and ffmpeg is disabled

Also moved URI of Track to net/url for better URI handling
  • Loading branch information
mulbc authored Nov 2, 2021
1 parent f43e486 commit 6208fd1
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 16 deletions.
14 changes: 11 additions & 3 deletions internal/m3uplus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"fmt"
"io"
"net/url"
"regexp"
"strconv"
"strings"
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion internal/providers/area51.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion internal/providers/custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion internal/providers/iptv-epg.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion internal/providers/iris.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
17 changes: 16 additions & 1 deletion lineup.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand All @@ -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") {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
Expand Down
12 changes: 4 additions & 8 deletions routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 6208fd1

Please sign in to comment.