func (d Downloader) getStream() (stream Stream, err error) { tok, err := api.API.Token(api.TokenLive, d.channel) if err != nil { return } u := api.Usher.Channel(d.channel, tok.Values()) req, err := http.NewRequest("GET", u.String(), nil) if err != nil { return } res, err := d.httpClient.Do(req) if err != nil { return } p, _, err := m3u8.DecodeFrom(res.Body, true) if err != nil { return stream, ErrStreamOffline } switch p := p.(type) { case *m3u8.MasterPlaylist: { for _, variant := range p.Variants { if variant.Video != targetVideo { continue } stream.URL = variant.URI return stream, nil } } } return stream, ErrTargetVideoNotFound }
func (d Downloader) DownloadChunks(stream Stream) error { log.Println("downloading chunks") req, err := http.NewRequest("GET", stream.URL, nil) if err != nil { return err } res, err := d.httpClient.Do(req) if err != nil { return err } defer res.Body.Close() p, _, err := m3u8.DecodeFrom(res.Body, true) if err != nil { return err } playlistURL, err := url.Parse(stream.URL) if err != nil { return err } var ( chunkURL string ) switch p := p.(type) { case *m3u8.MediaPlaylist: { for _, segment := range p.Segments { if segment == nil { continue } chunkURL = segment.URI if !strings.HasPrefix(chunkURL, "http") { u, err := playlistURL.Parse(segment.URI) if err != nil { log.Println("parse", err) continue } chunkURL = u.String() } _, hit := d.cache.Get(chunkURL) if hit { continue } if err := d.DownloadChunk(chunkURL); err != nil { d.notify("chunk download error", err) } else { d.cache.Add(chunkURL, nil) } } } default: return errors.New("Bad playlist type") } return nil }
func main() { GOPATH := os.Getenv("GOPATH") if GOPATH == "" { panic("$GOPATH is empty") } m3u8File := "github.com/grafov/m3u8/sample-playlists/media-playlist-with-byterange.m3u8" f, err := os.Open(path.Join(GOPATH, "src", m3u8File)) if err != nil { panic(err) } p, listType, err := m3u8.DecodeFrom(bufio.NewReader(f), true) if err != nil { panic(err) } switch listType { case m3u8.MEDIA: mediapl := p.(*m3u8.MediaPlaylist) fmt.Printf("%+v\n", mediapl) case m3u8.MASTER: masterpl := p.(*m3u8.MasterPlaylist) fmt.Printf("%+v\n", masterpl) } }
func Get(channelname string) (int, string) { cli := http.DefaultClient accessTokenurl := "http://api.twitch.tv/api/channels/" + channelname + "/access_token" req, err := http.NewRequest("GET", accessTokenurl, nil) if err != nil { fmt.Println(err) } req.Header.Set("User-Agent", "letr.it/twitchaudio 1.0") resp, err := cli.Do(req) if err != nil { fmt.Println("Looks like something went wrong with the request:\n", err) } defer resp.Body.Close() var accesstoken Accesstoken tempbody, err := ioutil.ReadAll(resp.Body) err = json.Unmarshal(tempbody, &accesstoken) if err != nil { fmt.Println("Something went wrong with the JSON:\n", err) } m3u8url := "http://usher.twitch.tv/api/channel/hls/" + channelname + ".m3u8?sig=" + accesstoken.Sig + "&token=" + accesstoken.Token + "&allow_source=true&allow_audio_only=true" req, err = http.NewRequest("GET", m3u8url, nil) if err != nil { fmt.Println(err) } req.Header.Set("User-Agent", "letr.it/twitchaudio 1.0") resp, err = cli.Do(req) if err != nil { fmt.Println("Looks like something went wrong with the request:\n", err) } defer resp.Body.Close() //tempbody, err = ioutil.ReadAll(resp.Body) //allstreamsraw := string(tempbody) p, listType, err := m3u8.DecodeFrom(bufio.NewReader(resp.Body), true) if err != nil { //panic(err) return 0, "no go" } switch listType { case m3u8.MEDIA: mediapl := p.(*m3u8.MediaPlaylist) fmt.Printf("%+v\n", mediapl) case m3u8.MASTER: masterpl := p.(*m3u8.MasterPlaylist) //fmt.Printf("%+v\n", masterpl.Variants[5]) for _, data := range masterpl.Variants { if data.Video == "audio_only" { return 1, data.URI } } } return 0, "no go" }
func getPlaylist(urlStr string, recTime time.Duration, useLocalTime bool, dlc chan *Download) { startTime := time.Now() var recDuration time.Duration = 0 cache := lru.New(64) playlistUrl, err := url.Parse(urlStr) if err != nil { log.Fatal(err) } for { req, err := http.NewRequest("GET", urlStr, nil) if err != nil { log.Fatal(err) } resp, err := doRequest(client, req) if err != nil { log.Print(err) time.Sleep(time.Duration(3) * time.Second) } playlist, listType, err := m3u8.DecodeFrom(resp.Body, true) if err != nil { log.Fatal(err) } resp.Body.Close() if listType == m3u8.MEDIA { mpl := playlist.(*m3u8.MediaPlaylist) for _, v := range mpl.Segments { if v != nil { var msURI string if strings.HasPrefix(v.URI, "http") { msURI, err = url.QueryUnescape(v.URI) if err != nil { log.Fatal(err) } } else { msUrl, err := playlistUrl.Parse(v.URI) if err != nil { log.Print(err) continue } msURI, err = url.QueryUnescape(msUrl.String()) if err != nil { log.Fatal(err) } } _, hit := cache.Get(msURI) if !hit { cache.Add(msURI, nil) if useLocalTime { recDuration = time.Now().Sub(startTime) } else { recDuration += time.Duration(int64(v.Duration * 1000000000)) } dlc <- &Download{msURI, recDuration} } if recDuration != 0 && recDuration >= recTime { close(dlc) return } } } if mpl.Closed { close(dlc) return } else { time.Sleep(time.Duration(int64(mpl.TargetDuration * 1000000000))) } } else { log.Fatal("Not a valid media playlist") } } }