Added way to handle disabled audio. Moved to channels/goroutines

This commit is contained in:
kacarmichael 2025-07-21 00:44:32 -05:00
parent b37a312b18
commit 354bbbf7df
6 changed files with 79 additions and 48 deletions

View File

@ -1,14 +1,15 @@
package main package main
import ( import (
"fmt"
"m3u8-downloader/pkg/downloader" "m3u8-downloader/pkg/downloader"
"m3u8-downloader/pkg/media"
"sync"
) )
func main() { func main() {
//Stream URL //Stream URL
masterUrl := "https://d17cyqyz9yhmep.cloudfront.net/streams/234945/playlist_1752291107574_1752292056713.m3u8" masterUrl := "https://d17cyqyz9yhmep.cloudfront.net/streams/234951/playlist_vo_1752978025523_1752978954944.m3u8"
//Download Service //Download Service
service := downloader.GetDownloadService() service := downloader.GetDownloadService()
@ -21,15 +22,17 @@ func main() {
} }
//Select best quality streams //Select best quality streams
audio, video := stream.Master.SelectBestQualityStreams() video, audio := stream.Master.SelectBestQualityStreams()
//Populate Segment Lists if !(audio == nil) {
audio_segments, err := service.ParseSegmentPlaylist(stream.BuildSegmentURL(audio.URL)) audio_segments, err := service.ParseSegmentPlaylist(stream.BuildSegmentURL(audio.URL))
if err != nil { if err != nil {
panic(err) panic(err)
} }
audio.Segments = *audio_segments audio.Segments = *audio_segments
}
//Populate Segment Lists
video_segments, err := service.ParseSegmentPlaylist(stream.BuildSegmentURL(video.URL)) video_segments, err := service.ParseSegmentPlaylist(stream.BuildSegmentURL(video.URL))
@ -38,45 +41,38 @@ func main() {
} }
video.Segments = *video_segments video.Segments = *video_segments
//Download Segment Playlists audioChan := make(chan media.Segment, 10)
for _, segment := range video.Segments.SegmentList { videoChan := make(chan media.Segment, 10)
err := service.DownloadFile(stream.BuildSegmentURL(segment.URL))
if err != nil { var wg sync.WaitGroup
fmt.Println(err)
return if !(audio == nil) {
for i := 1; i <= 2; i++ {
wg.Add(1)
go service.DownloadWorker(i, audioChan, &wg)
} }
} }
for i := 1; i <= 4; i++ {
wg.Add(1)
go service.DownloadWorker(i, videoChan, &wg)
}
if !(audio == nil) {
go func() {
for _, segment := range audio.Segments.SegmentList { for _, segment := range audio.Segments.SegmentList {
err := service.DownloadFile(stream.BuildSegmentURL(segment.URL)) audioChan <- segment
if err != nil {
fmt.Println(err)
return
} }
close(audioChan)
}()
} }
fmt.Println(stream)
//stream, err := media.ParseMasterPlaylist(masterUrl) go func() {
//if err != nil { for _, segment := range video.Segments.SegmentList {
// panic(err) videoChan <- segment
//} }
// close(videoChan)
//audio, video, err := stream.FetchSegmentPlaylists() }()
//if err != nil {
// panic(err) wg.Wait()
//}
//
//videoPlaylist := media.ParseMediaPlaylist(video)
//audioPlaylist := media.ParseMediaPlaylist(audio)
//
//for _, segment := range videoPlaylist.SegmentList {
// fmt.Println(segment.URL)
//}
//
//for _, segment := range audioPlaylist.SegmentList {
// err := http.DownloadFile(stream.BuildSegmentURL(segment.URL), media.OutputDirPath)
// if err != nil {
// return
// }
//}
} }

View File

@ -1,7 +1,8 @@
package constants package constants
const ( const (
HTTPUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0" HTTPUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36"
HTTPPrefix = "http://" HTTPPrefix = "http://"
HTTPSPrefix = "https://" HTTPSPrefix = "https://"
REFERRER = "https://www.flomarching.com"
) )

21
pkg/downloader/worker.go Normal file
View File

@ -0,0 +1,21 @@
package downloader
import (
"fmt"
"m3u8-downloader/pkg/media"
"sync"
)
func (s *DownloadService) DownloadWorker(id int, segmentChan <-chan media.Segment, wg *sync.WaitGroup) {
defer wg.Done()
for segment := range segmentChan {
fmt.Printf("[Worker %d] Downloading: %s\n", id, segment.URL)
err := s.DownloadFile(segment.URL)
if err != nil {
fmt.Printf("[Worker %d] Error: %s\n", id, err)
return
}
}
}

View File

@ -60,5 +60,6 @@ var DefaultClient = &HTTPWrapper{
client: &http.Client{}, client: &http.Client{},
headers: map[string]string{ headers: map[string]string{
"User-Agent": constants.HTTPUserAgent, "User-Agent": constants.HTTPUserAgent,
"Referer": constants.REFERRER,
}, },
} }

View File

@ -20,6 +20,7 @@ type VideoPlaylist struct {
} }
func NewVideoStream(streamInfo, url string) (*VideoPlaylist, error) { func NewVideoStream(streamInfo, url string) (*VideoPlaylist, error) {
hasAudio := strings.Contains(strings.ToLower(streamInfo), "audio")
if !strings.HasPrefix(streamInfo, constants.ExtXStreamInf) { if !strings.HasPrefix(streamInfo, constants.ExtXStreamInf) {
return nil, errors.New("invalid stream info line") return nil, errors.New("invalid stream info line")
} }
@ -28,7 +29,7 @@ func NewVideoStream(streamInfo, url string) (*VideoPlaylist, error) {
matches := reg.FindAllStringSubmatch(streamInfo, -1) matches := reg.FindAllStringSubmatch(streamInfo, -1)
if len(matches) < 5 { if len(matches) < 5 {
return nil, errors.New("insufficient stream attributes") fmt.Println("Less than 5 stream attributes found - audio likely disabled")
} }
bandwidth, err := strconv.Atoi(matches[0][2]) bandwidth, err := strconv.Atoi(matches[0][2])
@ -36,6 +37,7 @@ func NewVideoStream(streamInfo, url string) (*VideoPlaylist, error) {
return nil, err return nil, err
} }
if hasAudio {
return &VideoPlaylist{ return &VideoPlaylist{
URL: url, URL: url,
Bandwidth: bandwidth, Bandwidth: bandwidth,
@ -44,6 +46,16 @@ func NewVideoStream(streamInfo, url string) (*VideoPlaylist, error) {
FrameRate: StripQuotes(matches[3][2]), FrameRate: StripQuotes(matches[3][2]),
AudioGroup: StripQuotes(matches[4][2]), AudioGroup: StripQuotes(matches[4][2]),
}, nil }, nil
}
return &VideoPlaylist{
URL: url,
Bandwidth: bandwidth,
Codecs: StripQuotes(matches[1][2]),
Resolution: StripQuotes(matches[2][2]),
FrameRate: StripQuotes(matches[3][2]),
}, nil
} }
func (v *VideoPlaylist) BuildPlaylistURL(filename string) string { func (v *VideoPlaylist) BuildPlaylistURL(filename string) string {

View File

@ -46,7 +46,7 @@ type SegmentPlaylist struct {
//} //}
func (s *StreamSet) BuildSegmentURL(filename string) string { func (s *StreamSet) BuildSegmentURL(filename string) string {
return fmt.Sprintf("%s%s", constants.HTTPSPrefix, s.Metadata.Domain+"/streams/"+s.Metadata.StreamID+"/a/5000/"+filename) return fmt.Sprintf("%s%s", constants.HTTPSPrefix, s.Metadata.Domain+"/streams/"+s.Metadata.StreamID+"/"+filename)
} }
func ParseMediaPlaylist(content string) *SegmentPlaylist { func ParseMediaPlaylist(content string) *SegmentPlaylist {