diff options
Diffstat (limited to 'downloader')
| -rw-r--r-- | downloader/download_manager.go | 112 | ||||
| -rw-r--r-- | downloader/downloader.go | 180 | ||||
| -rw-r--r-- | downloader/downloader_test.go | 22 | ||||
| -rw-r--r-- | downloader/util.go | 77 |
4 files changed, 0 insertions, 391 deletions
diff --git a/downloader/download_manager.go b/downloader/download_manager.go deleted file mode 100644 index 16054ae..0000000 --- a/downloader/download_manager.go +++ /dev/null @@ -1,112 +0,0 @@ -package downloader - -import ( - "time" - - "github.com/bwmarrin/discordgo" -) - -type DlMessage int - -const ( - Clear DlMessage = iota - Pause - Play - Skip -) - -type playBundle struct { - data <-chan []byte - conn *discordgo.VoiceConnection -} - -type DownloadManager struct { - session *discordgo.Session - guildID string - voiceID string - dls chan *downloader - - PlayState chan DlMessage -} - -func NewManager(s *discordgo.Session, guildID string, voiceChanID string) *DownloadManager { - dm := &DownloadManager{ - session: s, - dls: make(chan *downloader), - PlayState: make(chan DlMessage), - guildID: guildID, - voiceID: voiceChanID, - } - - go dm.playFromQueue() - - return dm -} - -func (m *DownloadManager) playFromQueue() { - for { - select { - case <-m.PlayState: // ignore play state updates while not playing anything - case dl := <-m.dls: - conn, err := m.session.ChannelVoiceJoin(m.guildID, m.voiceID, false, false) - if err != nil { - log.Errorf("unable to connect to the voice channel: %q", err) - time.Sleep(1 * time.Second) - break - } - - m.session.UpdateStatus(0, dl.info.Url.Host) - out := dl.Start() - - playState := Play - conn.Speaking(true) - - cleanup := func() { - conn.Speaking(false) - conn.Disconnect() - m.session.UpdateStatus(0, "literally nothing") - } - - inner: - for { - switch playState { - case Clear: - dl.Stop() - for { - select { - case dl := <-m.dls: - dl.Stop() - dl.Start() // this is a hacky way of ensuring all the wavs are closed and cleaned up - default: - } - } - break inner - case Skip: - break inner - case Pause: - playState = <-m.PlayState - case Play: - select { // first check if we have a state update message coming in - case playState = <-m.PlayState: - case elem, ok := <-out: - if !ok { - break inner - } - - conn.OpusSend <- elem - } - } - } - cleanup() - } - } -} - -func (m *DownloadManager) Enqueue(url string, startTime, duration time.Duration) error { - dl, err := newDownload(url, startTime, duration) - if err != nil { - return err - } - m.dls <- dl - return nil -} diff --git a/downloader/downloader.go b/downloader/downloader.go deleted file mode 100644 index b7645da..0000000 --- a/downloader/downloader.go +++ /dev/null @@ -1,180 +0,0 @@ -package downloader - -import ( - "io/ioutil" - "os" - "os/exec" - "strconv" - "sync" - "time" - - "github.com/mammothbane/thulani-go/wav" -) - -// downloader handles a download for a particular song. -type downloader struct { - Url string - - StartTime time.Duration - Duration time.Duration - EndTime time.Duration - - once sync.Once - done chan struct{} - pb chan *wavBundle - - info videoInfo -} - -const clipTime = 10 * time.Second -const preloadCount = 5 - -func newDownload(url string, startTime, dur time.Duration) (*downloader, error) { - vInfo, err := info(url) - if err != nil { - return nil, err - } - - if dur == 0 { - dur = vInfo.Duration - startTime - } - - dl := &downloader{ - Url: url, - - StartTime: startTime, - Duration: dur, - EndTime: startTime + dur, - - done: make(chan struct{}, 1), - pb: make(chan *wavBundle, preloadCount), - info: *vInfo, - } - - go dl.schedule() - - return dl, nil -} - -func (d *downloader) Stop() { - d.once.Do(func() { - close(d.done) - }) -} - -func (d *downloader) Start() <-chan []byte { - out := make(chan []byte, 1024) - - go func() { - defer close(out) - select { - case <-d.done: - for wavB := range d.pb { - wavB.wav.Stop() - wavB.cleanup() - } - return - default: - } - - for wavB := range d.pb { - wavB.wav.Start(out) - - select { - case <-d.done: - wavB.wav.Stop() - wavB.cleanup() - - case <-wavB.wav.Done: - } - } - }() - - return out -} - -func (d *downloader) schedule() { - defer close(d.pb) - for i := 0; ; i++ { - select { - case <-d.done: - return - default: - } - - clipStart := time.Duration(i)*clipTime + d.StartTime - clipEnd := time.Duration(i+1)*clipTime + d.StartTime - - if clipStart >= d.EndTime { - return - } - - dur := clipTime - if clipEnd > d.EndTime { - dur = d.EndTime - clipStart - } - - wavb, err := d.downloadSegment(clipStart, dur) - if err != nil { - log.Errorf("error setting up download: %q", err) - return - } - - select { - case d.pb <- wavb: - case <-d.done: - return - } - } -} - -func (d *downloader) downloadSegment(startTime, duration time.Duration) (*wavBundle, error) { - startSecond := int(startTime.Seconds()) - args := []string{ - "-ss", strconv.Itoa(startSecond), - "-i", d.info.Url.String(), - "-c:a", "pcm_s16le", - "-f", "wav", - "-ar", "48000", - "-ac", "2", - "-vn", "-y", - } - - dur := int(duration.Seconds()) - if dur > 0 && startTime+duration < d.info.Duration { - args = append(args, "-t", strconv.Itoa(dur)) - } - - file, err := ioutil.TempFile("", "thulani_") - if err != nil { - return nil, err - } - - clearTemp := func() { - if err := file.Close(); err != nil { - log.Errorf("error closing temp file: %q", err) - } - - if err := os.Remove(file.Name()); err != nil { - log.Errorf("unable to remove temp file: %q", err) - } - } - - args = append(args, file.Name()) - - dl := exec.Command(`ffmpeg`, args...) - b, err := dl.CombinedOutput() - if err != nil { - clearTemp() - log.Errorf("ffmpeg failed: \n%v", string(b)) - return nil, err - } - - wv, err := wav.New(file.Name()) - if err != nil { - clearTemp() - return nil, err - } - - return &wavBundle{wav: wv, cleanup: clearTemp}, err -} diff --git a/downloader/downloader_test.go b/downloader/downloader_test.go deleted file mode 100644 index f38e1d0..0000000 --- a/downloader/downloader_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package downloader - -import ( - "fmt" - "testing" - "time" -) - -func TestGetUrl(t *testing.T) { - u, err := getUrl("https://www.youtube.com/watch?v=_K13GJkGvDw") - if err != nil { - t.Fatal(err) - } - - fmt.Println(u) -} - -func TestDownload(t *testing.T) { - if _, err := Download("https://www.youtube.com/watch?v=_K13GJkGvDw", 10*time.Second, 10*time.Second); err != nil { - t.Fatal(err) - } -} diff --git a/downloader/util.go b/downloader/util.go deleted file mode 100644 index f744fad..0000000 --- a/downloader/util.go +++ /dev/null @@ -1,77 +0,0 @@ -package downloader - -import ( - "encoding/json" - "io/ioutil" - "net/url" - "os/exec" - "time" - - "github.com/mammothbane/thulani-go/wav" - "github.com/op/go-logging" -) - -var log = logging.MustGetLogger("downloader") - -// responsible for decoding from youtube -type videoInfo struct { - Title string `json:"fulltitle"` - UrlStr string `json:"url"` - DurationSec int `json:"duration"` - Url *url.URL `json:"-"` - Duration time.Duration `json:"-"` -} - -func info(inUrl string) (*videoInfo, error) { - dl := exec.Command("youtube-dl", "-f", "bestaudio", "-x", "-j", inUrl) - - outpipe, err := dl.StdoutPipe() - if err != nil { - return nil, err - } - - errpipe, err := dl.StderrPipe() - if err != nil { - return nil, err - } - - err = dl.Start() - if err != nil { - log.Errorf("starting youtube-dl failed") - return nil, err - } - - o, ierr := ioutil.ReadAll(outpipe) - if ierr != nil { - log.Errorf("unable to read from output pipe") - return nil, err - } - - e, ierr := ioutil.ReadAll(errpipe) - if ierr != nil { - log.Errorf("unable to read from error pipe") - return nil, err - } - - if err := dl.Wait(); err != nil { - log.Errorf("error:\n%v", string(e)) - return nil, err - } - - v := videoInfo{} - if err := json.Unmarshal(o, &v); err != nil { - return nil, err - } - - v.Duration = time.Duration(v.DurationSec) * time.Second - v.Url, err = url.Parse(v.UrlStr) - - //tgt, err := url.Parse(string(o)) - //out := tgt.Scheme + "://" + tgt.Host + tgt.Path + "?" + tgt.Query().Encode() - return &v, err -} - -type wavBundle struct { - wav *wav.Wav - cleanup func() -} |
