package downloader import ( "errors" "regexp" "strconv" "strings" ) type VideoStream struct { URL string Bandwidth int Codecs string Resolution string FrameRate string AudioGroup string } func NewVideoStream(streamInfo, url string) (*VideoStream, error) { if !strings.HasPrefix(streamInfo, ExtXStreamInf) { return nil, errors.New("invalid stream info line") } reg := regexp.MustCompile(`([A-Z0-9-]+)=(".*?"|[^,]*)`) matches := reg.FindAllStringSubmatch(streamInfo, -1) if len(matches) < 5 { return nil, errors.New("insufficient stream attributes") } bandwidth, err := strconv.Atoi(matches[0][2]) if err != nil { return nil, err } return &VideoStream{ URL: url, Bandwidth: bandwidth, Codecs: StripQuotes(matches[1][2]), Resolution: StripQuotes(matches[2][2]), FrameRate: StripQuotes(matches[3][2]), AudioGroup: StripQuotes(matches[4][2]), }, nil } type AudioStream struct { URL string MediaType string GroupID string Name string IsDefault bool AutoSelect bool } func NewAudioStream(mediaInfo string) (*AudioStream, error) { if !strings.HasPrefix(mediaInfo, ExtXMedia) { return nil, errors.New("invalid downloader info line") } attributes := parseMediaAttributes(mediaInfo) return &AudioStream{ URL: StripQuotes(attributes["URI"]), MediaType: StripQuotes(attributes["TYPE"]), GroupID: StripQuotes(attributes["GROUP-ID"]), Name: StripQuotes(attributes["NAME"]), IsDefault: attributes["DEFAULT"] == "YES", AutoSelect: attributes["AUTOSELECT"] == "YES", }, nil } func (s *StreamSet) SelectBestQualityStreams() (*VideoStream, *AudioStream) { if len(s.VideoStreams) == 0 { return nil, nil } bestVideo := s.VideoStreams[0] maxPixels := ResolutionToPixels(bestVideo.Resolution) for _, video := range s.VideoStreams { pixels := ResolutionToPixels(video.Resolution) if video.Bandwidth > bestVideo.Bandwidth || pixels > maxPixels { bestVideo = video maxPixels = pixels } } return &bestVideo, s.FindAudioStreamByGroup(bestVideo.AudioGroup) } func (s *StreamSet) FindAudioStreamByGroup(groupID string) *AudioStream { for _, audio := range s.AudioStreams { if audio.GroupID == groupID { return &audio } } return nil }