Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ jobs:
- 'docker'
before_install: './.travis/main.sh'
install: skip
script: make docker-build
script:
- make deps check test build
- make docker-build
- stage: deploy
if: (branch = master) AND (NOT type = pull_request)
services:
- 'docker'
before_install: './.travis/main.sh'
install: skip
script:
- make deps check test build
- make docker-build
- 'if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin; fi'
- 'if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then make docker-push; fi'
1 change: 1 addition & 0 deletions conf/config.json.sample
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"ytdl_path": "",
"save_videos": true,
"cache_dir": "video_cache",
"cache_size_mb": 1024,
"use_playlist": true,
"playlist_path": "conf/playlist.json",
"auto_pause": true,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/andybalholm/cascadia v0.0.0-20161224141413-349dd0209470 // indirect
github.com/bwmarrin/discordgo v0.18.0
github.com/gorilla/websocket v1.2.0 // indirect
github.com/jatgam/goutils v0.1.0
github.com/jatgam/goutils v0.1.1
github.com/jonas747/dca v0.0.0-20171004024810-01f9985f4a26
github.com/jonas747/ogg v0.0.0-20161220051205-b4f6f4cf3757 // indirect
github.com/rylio/ytdl v0.5.2-0.20190315183053-1f14ef2e151a
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/jatgam/goutils v0.1.0 h1:QDjXxvarLLME8xcxls+5gsACjLiDsQfhIPkxllULEX4=
github.com/jatgam/goutils v0.1.0/go.mod h1:1z730wgc18GOvqmZf8H95/sDnYgqvZCzz5EMsmLVQVM=
github.com/jatgam/goutils v0.1.1 h1:1yyLw5oneryzJ5iiB6L9fzgRDYvG9iplQB+iRaiBA8g=
github.com/jatgam/goutils v0.1.1/go.mod h1:1z730wgc18GOvqmZf8H95/sDnYgqvZCzz5EMsmLVQVM=
github.com/jonas747/dca v0.0.0-20171004024810-01f9985f4a26 h1:NKu1iYZ8bas0oQXjZYca7KpWDPyP+JZNUCt1gFoUJ5c=
github.com/jonas747/dca v0.0.0-20171004024810-01f9985f4a26/go.mod h1:rxjYX9OJU81unMxQDHChU/lAiOhlY9MV+faPX/NmwLk=
github.com/jonas747/ogg v0.0.0-20161220051205-b4f6f4cf3757 h1:Kyv+zTfWIGRNaz/4+lS+CxvuKVZSKFz/6G8E3BKKBRs=
Expand Down
27 changes: 24 additions & 3 deletions piccolo/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func init() {
cmdHandler.addCommand("skipSong", skipSong)
cmdHandler.addCommand("savePlaylist", savePlaylist)
cmdHandler.addCommand("showPlaylist", printPlaylist)
cmdHandler.addCommand("status", getStatus)
}

func (h commandHandler) addCommand(name string, c command) {
Expand Down Expand Up @@ -90,8 +91,17 @@ func play(b *Bot, m *discordgo.MessageCreate) {
b.reply(fmt.Sprintf("<@%s> - Sorry, couldn't find a result for: **%s**", m.Author.ID, song), m)
return
}
b.textChannelLookup[m.ChannelID].player.playlist.addSong(m.Author, m.ChannelID, result.ID.VideoID, result.Snippet.Title)
go b.textChannelLookup[m.ChannelID].player.downloadNextSong()
if result.ID.VideoID == b.textChannelLookup[m.ChannelID].player.currentSong.VideoID {
b.reply(fmt.Sprintf("<@%s> - You requested the already playing song: **%s**\nCasting your vote to the void.", m.Author.ID, result.Snippet.Title), m)
return
}
pEntry := &PlaylistEntry{
Requester: m.Author,
RequestChannelID: m.ChannelID,
Title: result.Snippet.Title,
VideoID: result.ID.VideoID,
}
go b.textChannelLookup[m.ChannelID].player.downloadNextSong(pEntry)
b.reply(fmt.Sprintf("<@%s> - Enqueued **%s** to be played.", m.Author.ID, result.Snippet.Title), m)
}

Expand Down Expand Up @@ -154,5 +164,16 @@ func printPlaylist(b *Bot, m *discordgo.MessageCreate) {
}).Error("Failed to find controller from channel id")
return
}
b.reply(fmt.Sprintf("<@%s> - **Current Playlist**\n\n%s", m.Author.ID, b.textChannelLookup[m.ChannelID].player.playlist), m)
currentVideoID := b.textChannelLookup[m.ChannelID].player.currentSong.VideoID
b.reply(fmt.Sprintf("<@%s> - **Current Playlist**\n\n%s", m.Author.ID, b.textChannelLookup[m.ChannelID].player.playlist.printPlaylist(currentVideoID)), m)
}

func getStatus(b *Bot, m *discordgo.MessageCreate) {
if _, ok := b.textChannelLookup[m.ChannelID]; !ok {
log.WithFields(log.Fields{
"channel": m.ChannelID,
}).Error("Failed to find controller from channel id")
return
}
b.reply(fmt.Sprintf("<@%s> - **Current Status**\n\n%s", m.Author.ID, b.textChannelLookup[m.ChannelID].player.getCurrentStatus()), m)
}
106 changes: 95 additions & 11 deletions piccolo/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -34,9 +35,9 @@ type (

currentSong *songAndPath

dg *discordgo.Session

downloadLock *sync.Mutex
dg *discordgo.Session
limitCacheSize bool
downloadLock *sync.Mutex
}

songAndPath struct {
Expand All @@ -53,6 +54,11 @@ func newPlayer(confpointer *utils.Config, guildID string, voiceChID string, yout
p := &player{conf: confpointer, guildID: guildID, voiceChannelID: voiceChID, yt: youtube, dg: discordSession}
p.playlist = newPlaylist(p.conf.Bot.UsePlaylist, p.conf.Bot.PlaylistPath)
p.downloadLock = downloadLock
if confpointer.Bot.CacheSizeMB > 0 {
p.limitCacheSize = true
} else {
p.limitCacheSize = false
}
p.streamDoneChan = make(chan error)
return p
}
Expand Down Expand Up @@ -81,7 +87,7 @@ func (p *player) JoinVoiceChannel() error {
return err
}
p.vc = vc
p.downloadNextSong()
p.downloadNextSong(nil)

go p.playLoop()
return nil
Expand All @@ -91,7 +97,7 @@ func (p *player) playLoop() {
for {
nextSong, err := p.getNextSongPath()
// Download the next song in the background
go p.downloadNextSong()
go p.downloadNextSong(nil)
if err == nil {
reader, err := os.Open(filepath.FromSlash(nextSong.fsPath))
if err != nil {
Expand Down Expand Up @@ -129,9 +135,28 @@ func (p *player) playLoop() {
}
p.vc.Speaking(false)
}
if p.limitCacheSize {
currentCacheSize, err := goutils.DirSizeMB(p.yt.YTCacheDir)
if err != nil {
log.WithFields(log.Fields{
"error": err,
}).Error("Error Determing Current Cache Size")
} else {
if currentCacheSize > float64(p.conf.Bot.CacheSizeMB) {
p.downloadLock.Lock()
deleteErr := goutils.DeleteOldestFileUntilDirUnderSize(p.yt.YTCacheDir, float64(p.conf.Bot.CacheSizeMB))
if deleteErr != nil {
log.WithFields(log.Fields{
"error": deleteErr,
}).Error("Error Deleting files from cache")
}
p.downloadLock.Unlock()
}
}
}
// Check if the next song is downloaded, if not block until it is. Catches
// additions to the request queue.
p.downloadNextSong()
p.downloadNextSong(nil)
}
}

Expand Down Expand Up @@ -189,35 +214,94 @@ func (p *player) Skip(numListeners int, requesterID string) string {
}

func (p *player) skipSong() {
p.downloadLock.Lock()
defer p.downloadLock.Unlock()
p.Pause()
p.streamDoneChan <- errSkip
}

func (p *player) downloadNextSong() {
func (p *player) getCurrentStatus() string {
var currentDuration time.Duration
var totalDuration time.Duration
var percentDur float64
if p.stream != nil {
currentDuration = p.stream.PlaybackPosition() * 1000000
totalDuration = p.currentSong.TrackDuration
}
if totalDuration > 0 {
percentDur = float64(currentDuration) / float64(totalDuration) * 100
numHashes := int(50 * (percentDur / 100))
percentLine := strings.Repeat("#", numHashes)
percentLine = fmt.Sprintf("|%s%s|", percentLine, strings.Repeat("-", 50-numHashes))

return fmt.Sprintf("**Playing:** %s\n**Time:** %s of %s\n```%s [%.2f%%]```", p.currentSong.Title, currentDuration, totalDuration, percentLine, percentDur)
}
return fmt.Sprintf("**Playing:** %s\n**Time:** %s of UNKNOWN", p.currentSong.Title, currentDuration)
}

func (p *player) downloadNextSong(pEntry *PlaylistEntry) {
p.downloadLock.Lock()
defer p.downloadLock.Unlock()

nextSong := p.playlist.peekNextSong()
var nextSong *PlaylistEntry
if pEntry == nil {
nextSong = p.playlist.peekNextSong()
} else {
nextSong = pEntry
}

if nextSong == nil {
log.Warn("Can't download next song, playlist is empty!")
log.Warn("Can't download next song, None Available!")
return
}
if p.currentSong != nil {
if nextSong.VideoID == p.currentSong.VideoID {
log.WithFields(log.Fields{
"videoID": nextSong.VideoID,
}).Warn("Attempted to download the current song.")
}
}

songFilePath := path.Join(p.yt.YTCacheDir, nextSong.VideoID+".dca")
if _, err := os.Stat(filepath.FromSlash(songFilePath)); os.IsNotExist(err) {
if _, err := os.Stat(filepath.FromSlash(songFilePath)); os.IsNotExist(err) || (nextSong.Requester != nil && nextSong.TrackDuration.String() == "0s") {
log.WithFields(log.Fields{
"song": filepath.FromSlash(songFilePath),
}).Debug("Downloading song")
_, dlErr := p.yt.DownloadDCAAudio(nextSong.VideoID)
_, encodeDuration, dlErr := p.yt.DownloadDCAAudio(nextSong.VideoID)
if dlErr != nil {
log.WithFields(log.Fields{
"song": nextSong.VideoID,
}).Error(dlErr)
} else {
log.WithFields(log.Fields{
"song": filepath.FromSlash(songFilePath),
"duration": encodeDuration,
}).Debug("Downloaded Song")
// Separate operations for playlist entry vs request
if nextSong.Requester == nil {
modifyError := p.playlist.modifyPlaylistEntry(nextSong.VideoID, PlaylistEntry{Title: nextSong.Title, VideoID: nextSong.VideoID, TrackDuration: encodeDuration})
if modifyError != nil {
log.WithFields(log.Fields{
"song": nextSong.VideoID,
}).Error(modifyError)
} else {
p.playlist.savePlaylist()
}
} else {
nextSong.TrackDuration = encodeDuration
p.playlist.addRequestedSong(nextSong)
}
}
} else {
log.WithFields(log.Fields{
"song": filepath.FromSlash(songFilePath),
}).Debug("Song already downloaded")
dateUpdateErr := goutils.UpdateFileModifiedTime(filepath.FromSlash(songFilePath), time.Now())
if dateUpdateErr != nil {
log.WithFields(log.Fields{
"song": filepath.FromSlash(songFilePath),
}).Warn("Failed to update song modified time")
}
}
}

Expand Down
Loading