Greatly simplified song queue data structure
This commit is contained in:
parent
24fb620e1f
commit
b6a6da718d
|
@ -1,8 +1,9 @@
|
|||
MumbleDJ Changelog
|
||||
==================
|
||||
|
||||
### February 11, 2015 -- `v2.4.4`
|
||||
### February 11, 2015 -- `v2.5.0`
|
||||
* Updated dependencies and fixed code to match `gumble` API changes.
|
||||
* Greatly simplified the song queue data structure. Some new bugs could potentially have arisen. Let me know if you find any!
|
||||
|
||||
### February 9, 2015 -- `v2.4.3`
|
||||
* Added configuration option in `mumbledj.gcfg` for default bot comment.
|
||||
|
|
74
commands.go
74
commands.go
|
@ -169,15 +169,15 @@ func add(user *gumble.User, username, url string) {
|
|||
}
|
||||
|
||||
if matchFound {
|
||||
if newSong, err := NewSong(username, shortUrl); err == nil {
|
||||
if err := dj.queue.AddItem(newSong); err == nil {
|
||||
if newSong, err := NewSong(username, shortUrl, nil); err == nil {
|
||||
if err := dj.queue.AddSong(newSong); err == nil {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(SONG_ADDED_HTML, username, newSong.title), false)
|
||||
if dj.queue.Len() == 1 && !dj.audioStream.IsPlaying() {
|
||||
if err := dj.queue.CurrentItem().(*Song).Download(); err == nil {
|
||||
dj.queue.CurrentItem().(*Song).Play()
|
||||
if err := dj.queue.CurrentSong().Download(); err == nil {
|
||||
dj.queue.CurrentSong().Play()
|
||||
} else {
|
||||
dj.SendPrivateMessage(user, AUDIO_FAIL_MSG)
|
||||
dj.queue.CurrentItem().(*Song).Delete()
|
||||
dj.queue.CurrentSong().Delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,16 +191,15 @@ func add(user *gumble.User, username, url string) {
|
|||
if re.MatchString(url) {
|
||||
if dj.HasPermission(username, dj.conf.Permissions.AdminAddPlaylists) {
|
||||
shortUrl = re.FindStringSubmatch(url)[1]
|
||||
oldLength := dj.queue.Len()
|
||||
if newPlaylist, err := NewPlaylist(username, shortUrl); err == nil {
|
||||
if dj.queue.AddItem(newPlaylist); err == nil {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(PLAYLIST_ADDED_HTML, username, newPlaylist.title), false)
|
||||
if dj.queue.Len() == 1 && !dj.audioStream.IsPlaying() {
|
||||
if err := dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Download(); err == nil {
|
||||
dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
dj.SendPrivateMessage(user, AUDIO_FAIL_MSG)
|
||||
dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Delete()
|
||||
}
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(PLAYLIST_ADDED_HTML, username, newPlaylist.title), false)
|
||||
if oldLength == 0 && !dj.audioStream.IsPlaying() {
|
||||
if err := dj.queue.CurrentSong().Download(); err == nil {
|
||||
dj.queue.CurrentSong().Play()
|
||||
} else {
|
||||
dj.SendPrivateMessage(user, AUDIO_FAIL_MSG)
|
||||
dj.queue.CurrentSong().Delete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -222,15 +221,28 @@ func add(user *gumble.User, username, url string) {
|
|||
func skip(user *gumble.User, username string, admin, playlistSkip bool) {
|
||||
if dj.audioStream.IsPlaying() {
|
||||
if playlistSkip {
|
||||
if dj.queue.CurrentItem().ItemType() == "playlist" {
|
||||
if err := dj.queue.CurrentItem().AddSkip(username); err == nil {
|
||||
if dj.queue.CurrentSong().playlist != nil {
|
||||
if err := dj.queue.CurrentSong().playlist.AddSkip(username); err == nil {
|
||||
if admin {
|
||||
dj.client.Self.Channel.Send(ADMIN_PLAYLIST_SKIP_MSG, false)
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(PLAYLIST_SKIP_ADDED_HTML, username), false)
|
||||
}
|
||||
if dj.queue.CurrentItem().SkipReached(len(dj.client.Self.Channel.Users)) || admin {
|
||||
dj.queue.CurrentItem().(*Playlist).skipped = true
|
||||
if dj.queue.CurrentSong().playlist.SkipReached(len(dj.client.Self.Channel.Users)) || admin {
|
||||
id := dj.queue.CurrentSong().playlist.id
|
||||
dj.queue.CurrentSong().playlist.DeleteSkippers()
|
||||
for i := 0; i < len(dj.queue.queue); i++ {
|
||||
if dj.queue.queue[i].playlist != nil {
|
||||
if dj.queue.queue[i].playlist.id == id {
|
||||
dj.queue.queue = append(dj.queue.queue[:i], dj.queue.queue[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
if dj.queue.Len() != 0 {
|
||||
// Set dontSkip to true to avoid audioStream.Stop() callback skipping the new first song.
|
||||
dj.queue.CurrentSong().dontSkip = true
|
||||
}
|
||||
dj.client.Self.Channel.Send(PLAYLIST_SKIPPED_HTML, false)
|
||||
if err := dj.audioStream.Stop(); err != nil {
|
||||
panic(errors.New("An error occurred while stopping the current song."))
|
||||
|
@ -241,19 +253,13 @@ func skip(user *gumble.User, username string, admin, playlistSkip bool) {
|
|||
dj.SendPrivateMessage(user, NO_PLAYLIST_PLAYING_MSG)
|
||||
}
|
||||
} else {
|
||||
var currentItem QueueItem
|
||||
if dj.queue.CurrentItem().ItemType() == "playlist" {
|
||||
currentItem = dj.queue.CurrentItem().(*Playlist).songs.CurrentItem()
|
||||
} else {
|
||||
currentItem = dj.queue.CurrentItem()
|
||||
}
|
||||
if err := currentItem.AddSkip(username); err == nil {
|
||||
if err := dj.queue.CurrentSong().AddSkip(username); err == nil {
|
||||
if admin {
|
||||
dj.client.Self.Channel.Send(ADMIN_SONG_SKIP_MSG, false)
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(SKIP_ADDED_HTML, username), false)
|
||||
}
|
||||
if currentItem.SkipReached(len(dj.client.Self.Channel.Users)) || admin {
|
||||
if dj.queue.CurrentSong().SkipReached(len(dj.client.Self.Channel.Users)) || admin {
|
||||
dj.client.Self.Channel.Send(SONG_SKIPPED_HTML, false)
|
||||
if err := dj.audioStream.Stop(); err != nil {
|
||||
panic(errors.New("An error occurred while stopping the current song."))
|
||||
|
@ -334,7 +340,7 @@ func reset(username string) {
|
|||
// the number of songs in the queue to chat.
|
||||
func numSongs() {
|
||||
songCount := 0
|
||||
dj.queue.Traverse(func(i int, item QueueItem) {
|
||||
dj.queue.Traverse(func(i int, song *Song) {
|
||||
songCount += 1
|
||||
})
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(NUM_SONGS_HTML, songCount), false)
|
||||
|
@ -355,17 +361,11 @@ func nextSong(user *gumble.User) {
|
|||
// information about the song currently playing.
|
||||
func currentSong(user *gumble.User) {
|
||||
if dj.audioStream.IsPlaying() {
|
||||
var currentItem *Song
|
||||
if dj.queue.CurrentItem().ItemType() == "playlist" {
|
||||
currentItem = dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song)
|
||||
if dj.queue.CurrentSong().playlist == nil {
|
||||
dj.SendPrivateMessage(user, fmt.Sprintf(CURRENT_SONG_HTML, dj.queue.CurrentSong().title, dj.queue.CurrentSong().submitter))
|
||||
} else {
|
||||
currentItem = dj.queue.CurrentItem().(*Song)
|
||||
}
|
||||
if currentItem.playlistTitle == "" {
|
||||
dj.SendPrivateMessage(user, fmt.Sprintf(CURRENT_SONG_HTML, currentItem.title, currentItem.submitter))
|
||||
} else {
|
||||
dj.SendPrivateMessage(user, fmt.Sprintf(CURRENT_SONG_PLAYLIST_HTML, currentItem.title,
|
||||
currentItem.submitter, currentItem.playlistTitle))
|
||||
dj.SendPrivateMessage(user, fmt.Sprintf(CURRENT_SONG_PLAYLIST_HTML, dj.queue.CurrentSong().title,
|
||||
dj.queue.CurrentSong().submitter, dj.queue.CurrentSong().playlist.title))
|
||||
}
|
||||
} else {
|
||||
dj.SendPrivateMessage(user, NO_MUSIC_PLAYING_MSG)
|
||||
|
|
14
main.go
14
main.go
|
@ -29,6 +29,7 @@ type mumbledj struct {
|
|||
queue *SongQueue
|
||||
audioStream *gumble_ffmpeg.Stream
|
||||
homeDir string
|
||||
playlistSkips map[string][]string
|
||||
}
|
||||
|
||||
// OnConnect event. First moves MumbleDJ into the default channel specified
|
||||
|
@ -84,12 +85,10 @@ func (dj *mumbledj) OnTextMessage(e *gumble.TextMessageEvent) {
|
|||
func (dj *mumbledj) OnUserChange(e *gumble.UserChangeEvent) {
|
||||
if e.Type.Has(gumble.UserChangeDisconnected) {
|
||||
if dj.audioStream.IsPlaying() {
|
||||
if dj.queue.CurrentItem().ItemType() == "playlist" {
|
||||
dj.queue.CurrentItem().(*Playlist).RemoveSkip(e.User.Name)
|
||||
dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).RemoveSkip(e.User.Name)
|
||||
} else {
|
||||
dj.queue.CurrentItem().(*Song).RemoveSkip(e.User.Name)
|
||||
if dj.queue.CurrentSong().playlist != nil {
|
||||
dj.queue.CurrentSong().playlist.RemoveSkip(e.User.Name)
|
||||
}
|
||||
dj.queue.CurrentSong().RemoveSkip(e.User.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,8 +118,9 @@ func (dj *mumbledj) SendPrivateMessage(user *gumble.User, message string) {
|
|||
|
||||
// dj variable declaration. This is done outside of main() to allow global use.
|
||||
var dj = mumbledj{
|
||||
keepAlive: make(chan bool),
|
||||
queue: NewSongQueue(),
|
||||
keepAlive: make(chan bool),
|
||||
queue: NewSongQueue(),
|
||||
playlistSkips: make(map[string][]string),
|
||||
}
|
||||
|
||||
// Main function, but only really performs startup tasks. Grabs and parses commandline
|
||||
|
|
60
playlist.go
60
playlist.go
|
@ -20,18 +20,13 @@ import (
|
|||
|
||||
// Playlist type declaration.
|
||||
type Playlist struct {
|
||||
songs *SongQueue
|
||||
youtubeId string
|
||||
title string
|
||||
submitter string
|
||||
skippers []string
|
||||
skipped bool
|
||||
id string
|
||||
title string
|
||||
}
|
||||
|
||||
// Returns a new Playlist type. Before returning the new type, the playlist's metadata is collected
|
||||
// via the YouTube Gdata API.
|
||||
func NewPlaylist(user, id string) (*Playlist, error) {
|
||||
queue := NewSongQueue()
|
||||
jsonUrl := fmt.Sprintf("http://gdata.youtube.com/feeds/api/playlists/%s?v=2&alt=jsonc&maxresults=25", id)
|
||||
jsonString := ""
|
||||
|
||||
|
@ -59,6 +54,11 @@ func NewPlaylist(user, id string) (*Playlist, error) {
|
|||
playlistItems = 25
|
||||
}
|
||||
|
||||
playlist := &Playlist{
|
||||
id: id,
|
||||
title: playlistTitle,
|
||||
}
|
||||
|
||||
for i := 0; i < playlistItems; i++ {
|
||||
index := strconv.Itoa(i)
|
||||
songTitle, _ := jq.String("data", "items", index, "video", "title")
|
||||
|
@ -67,63 +67,55 @@ func NewPlaylist(user, id string) (*Playlist, error) {
|
|||
duration, _ := jq.Int("data", "items", index, "video", "duration")
|
||||
songDuration := fmt.Sprintf("%d:%02d", duration/60, duration%60)
|
||||
newSong := &Song{
|
||||
submitter: user,
|
||||
title: songTitle,
|
||||
playlistTitle: playlistTitle,
|
||||
youtubeId: songId,
|
||||
playlistId: id,
|
||||
duration: songDuration,
|
||||
thumbnailUrl: songThumbnail,
|
||||
submitter: user,
|
||||
title: songTitle,
|
||||
youtubeId: songId,
|
||||
duration: songDuration,
|
||||
thumbnailUrl: songThumbnail,
|
||||
playlist: playlist,
|
||||
dontSkip: false,
|
||||
}
|
||||
queue.AddItem(newSong)
|
||||
dj.queue.AddSong(newSong)
|
||||
}
|
||||
|
||||
playlist := &Playlist{
|
||||
songs: queue,
|
||||
youtubeId: id,
|
||||
title: playlistTitle,
|
||||
submitter: user,
|
||||
skipped: false,
|
||||
}
|
||||
return playlist, nil
|
||||
}
|
||||
|
||||
// Adds a skip to the skippers slice. If the user is already in the slice AddSkip() returns
|
||||
// an error and does not add a duplicate skip.
|
||||
// Adds a skip to the skippers slice for the current playlist.
|
||||
func (p *Playlist) AddSkip(username string) error {
|
||||
for _, user := range p.skippers {
|
||||
for _, user := range dj.playlistSkips[p.id] {
|
||||
if username == user {
|
||||
return errors.New("This user has already skipped the current song.")
|
||||
}
|
||||
}
|
||||
p.skippers = append(p.skippers, username)
|
||||
dj.playlistSkips[p.id] = append(dj.playlistSkips[p.id], username)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes a skip from the skippers slice. If username is not in the slice, an error is
|
||||
// returned.
|
||||
func (p *Playlist) RemoveSkip(username string) error {
|
||||
for i, user := range p.skippers {
|
||||
for i, user := range dj.playlistSkips[p.id] {
|
||||
if username == user {
|
||||
p.skippers = append(p.skippers[:i], p.skippers[i+1:]...)
|
||||
dj.playlistSkips[p.id] = append(dj.playlistSkips[p.id][:i], dj.playlistSkips[p.id][i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("This user has not skipped the song.")
|
||||
}
|
||||
|
||||
// Removes skippers entry in dj.playlistSkips.
|
||||
func (p *Playlist) DeleteSkippers() {
|
||||
delete(dj.playlistSkips, p.id)
|
||||
}
|
||||
|
||||
// Calculates current skip ratio based on number of users within MumbleDJ's channel and the
|
||||
// amount of values in the skippers slice. If the value is greater than or equal to the skip ratio
|
||||
// defined in mumbledj.gcfg, the function returns true. Returns false otherwise.
|
||||
func (p *Playlist) SkipReached(channelUsers int) bool {
|
||||
if float32(len(p.skippers))/float32(channelUsers) >= dj.conf.General.PlaylistSkipRatio {
|
||||
if float32(len(dj.playlistSkips[p.id]))/float32(channelUsers) >= dj.conf.General.PlaylistSkipRatio {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns "playlist" as the item type. Used for differentiating Songs from Playlists.
|
||||
func (p *Playlist) ItemType() string {
|
||||
return "playlist"
|
||||
}
|
||||
|
|
45
song.go
45
song.go
|
@ -21,20 +21,19 @@ import (
|
|||
|
||||
// Song type declaration.
|
||||
type Song struct {
|
||||
submitter string
|
||||
title string
|
||||
playlistTitle string
|
||||
youtubeId string
|
||||
playlistId string
|
||||
duration string
|
||||
thumbnailUrl string
|
||||
itemType string
|
||||
skippers []string
|
||||
submitter string
|
||||
title string
|
||||
youtubeId string
|
||||
duration string
|
||||
thumbnailUrl string
|
||||
skippers []string
|
||||
playlist *Playlist
|
||||
dontSkip bool
|
||||
}
|
||||
|
||||
// Returns a new Song type. Before returning the new type, the song's metadata is collected
|
||||
// via the YouTube Gdata API.
|
||||
func NewSong(user, id string) (*Song, error) {
|
||||
func NewSong(user, id string, playlist *Playlist) (*Song, error) {
|
||||
jsonUrl := fmt.Sprintf("http://gdata.youtube.com/feeds/api/videos/%s?v=2&alt=jsonc", id)
|
||||
jsonString := ""
|
||||
|
||||
|
@ -62,14 +61,13 @@ func NewSong(user, id string) (*Song, error) {
|
|||
videoDuration := fmt.Sprintf("%d:%02d", duration/60, duration%60)
|
||||
|
||||
song := &Song{
|
||||
submitter: user,
|
||||
title: videoTitle,
|
||||
playlistTitle: "",
|
||||
youtubeId: id,
|
||||
playlistId: "",
|
||||
duration: videoDuration,
|
||||
thumbnailUrl: videoThumbnail,
|
||||
itemType: "song",
|
||||
submitter: user,
|
||||
title: videoTitle,
|
||||
youtubeId: id,
|
||||
duration: videoDuration,
|
||||
thumbnailUrl: videoThumbnail,
|
||||
playlist: playlist,
|
||||
dontSkip: false,
|
||||
}
|
||||
return song, nil
|
||||
}
|
||||
|
@ -87,15 +85,15 @@ func (s *Song) Download() error {
|
|||
// Plays the song. Once the song is playing, a notification is displayed in a text message that features the video thumbnail, URL, title,
|
||||
// duration, and submitter.
|
||||
func (s *Song) Play() {
|
||||
if err := dj.audioStream.Play(fmt.Sprintf("%s/.mumbledj/songs/%s.m4a", dj.homeDir, s.youtubeId), dj.queue.OnItemFinished); err != nil {
|
||||
if err := dj.audioStream.Play(fmt.Sprintf("%s/.mumbledj/songs/%s.m4a", dj.homeDir, s.youtubeId), dj.queue.OnSongFinished); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if s.playlistTitle == "" {
|
||||
if s.playlist == nil {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(NOW_PLAYING_HTML, s.thumbnailUrl, s.youtubeId, s.title,
|
||||
s.duration, s.submitter), false)
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(fmt.Sprintf(NOW_PLAYING_PLAYLIST_HTML, s.thumbnailUrl, s.youtubeId,
|
||||
s.title, s.duration, s.submitter, s.playlistTitle), false)
|
||||
s.title, s.duration, s.submitter, s.playlist.title), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +146,3 @@ func (s *Song) SkipReached(channelUsers int) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns "song" as the item type. Used for differentiating Songs from Playlists.
|
||||
func (s *Song) ItemType() string {
|
||||
return "song"
|
||||
}
|
||||
|
|
138
songqueue.go
138
songqueue.go
|
@ -11,64 +11,54 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// QueueItem type declaration. QueueItem is an interface that groups together Song and Playlist
|
||||
// types in a queue.
|
||||
type QueueItem interface {
|
||||
AddSkip(string) error
|
||||
RemoveSkip(string) error
|
||||
SkipReached(int) bool
|
||||
ItemType() string
|
||||
}
|
||||
|
||||
// SongQueue type declaration. Serves as a wrapper around the queue structure defined in queue.go.
|
||||
// SongQueue type declaration.
|
||||
type SongQueue struct {
|
||||
queue []QueueItem
|
||||
queue []*Song
|
||||
}
|
||||
|
||||
// Initializes a new queue and returns the new SongQueue.
|
||||
func NewSongQueue() *SongQueue {
|
||||
return &SongQueue{
|
||||
queue: make([]QueueItem, 0),
|
||||
queue: make([]*Song, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Adds an item to the SongQueue.
|
||||
func (q *SongQueue) AddItem(i QueueItem) error {
|
||||
// Adds a Song to the SongQueue.
|
||||
func (q *SongQueue) AddSong(s *Song) error {
|
||||
beforeLen := q.Len()
|
||||
q.queue = append(q.queue, i)
|
||||
q.queue = append(q.queue, s)
|
||||
if len(q.queue) == beforeLen+1 {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Could not add QueueItem to the SongQueue.")
|
||||
return errors.New("Could not add Song to the SongQueue.")
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current QueueItem.
|
||||
func (q *SongQueue) CurrentItem() QueueItem {
|
||||
// Returns the current Song.
|
||||
func (q *SongQueue) CurrentSong() *Song {
|
||||
return q.queue[0]
|
||||
}
|
||||
|
||||
// Moves to the next item in SongQueue. NextItem() removes the first value in the queue.
|
||||
func (q *SongQueue) NextItem() {
|
||||
// Moves to the next Song in SongQueue. NextSong() removes the first Song in the queue.
|
||||
func (q *SongQueue) NextSong() {
|
||||
if q.CurrentSong().playlist != nil {
|
||||
if s, err := q.PeekNext(); err == nil {
|
||||
if q.CurrentSong().playlist.id != s.playlist.id {
|
||||
q.CurrentSong().playlist.DeleteSkippers()
|
||||
}
|
||||
} else {
|
||||
q.CurrentSong().playlist.DeleteSkippers()
|
||||
}
|
||||
}
|
||||
q.queue = q.queue[1:]
|
||||
}
|
||||
|
||||
// Peeks at the next Song and returns it.
|
||||
func (q *SongQueue) PeekNext() (*Song, error) {
|
||||
if q.Len() != 0 {
|
||||
if q.CurrentItem().ItemType() == "playlist" {
|
||||
return q.CurrentItem().(*Playlist).songs.queue[1].(*Song), nil
|
||||
} else if q.Len() > 1 {
|
||||
if q.queue[1].ItemType() == "playlist" {
|
||||
return q.queue[1].(*Playlist).songs.queue[0].(*Song), nil
|
||||
} else {
|
||||
return q.queue[1].(*Song), nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("There is no song coming up next.")
|
||||
}
|
||||
if q.Len() > 1 {
|
||||
return q.queue[1], nil
|
||||
} else {
|
||||
return nil, errors.New("There are no items in the queue.")
|
||||
return nil, errors.New("There isn't a Song coming up next.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,77 +68,35 @@ func (q *SongQueue) Len() int {
|
|||
}
|
||||
|
||||
// A traversal function for SongQueue. Allows a visit function to be passed in which performs
|
||||
// the specified action on each queue item. Traverses all individual songs, and all songs
|
||||
// within playlists.
|
||||
func (q *SongQueue) Traverse(visit func(i int, item QueueItem)) {
|
||||
for iQueue, queueItem := range q.queue {
|
||||
if queueItem.ItemType() == "playlist" {
|
||||
for iPlaylist, playlistItem := range q.queue[iQueue].(*Playlist).songs.queue {
|
||||
visit(iPlaylist, playlistItem)
|
||||
}
|
||||
} else {
|
||||
visit(iQueue, queueItem)
|
||||
}
|
||||
// the specified action on each queue item.
|
||||
func (q *SongQueue) Traverse(visit func(i int, s *Song)) {
|
||||
for sQueue, queueSong := range q.queue {
|
||||
visit(sQueue, queueSong)
|
||||
}
|
||||
}
|
||||
|
||||
// OnItemFinished event. Deletes item that just finished playing, then queues the next item.
|
||||
func (q *SongQueue) OnItemFinished() {
|
||||
// OnSongFinished event. Deletes Song that just finished playing, then queues the next Song (if exists).
|
||||
func (q *SongQueue) OnSongFinished() {
|
||||
if q.Len() != 0 {
|
||||
if q.CurrentItem().ItemType() == "playlist" {
|
||||
if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Delete(); err == nil {
|
||||
if q.CurrentItem().(*Playlist).skipped == true {
|
||||
if q.Len() > 1 {
|
||||
q.NextItem()
|
||||
q.PrepareAndPlayNextItem()
|
||||
} else {
|
||||
q.queue = q.queue[:0]
|
||||
}
|
||||
} else if q.CurrentItem().(*Playlist).songs.Len() > 1 {
|
||||
q.CurrentItem().(*Playlist).songs.NextItem()
|
||||
q.PrepareAndPlayNextItem()
|
||||
} else {
|
||||
if q.Len() > 1 {
|
||||
q.NextItem()
|
||||
q.PrepareAndPlayNextItem()
|
||||
} else {
|
||||
q.queue = q.queue[:0]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
if dj.queue.CurrentSong().dontSkip == true {
|
||||
dj.queue.CurrentSong().dontSkip = false
|
||||
q.PrepareAndPlayNextSong()
|
||||
} else {
|
||||
if err := q.CurrentItem().(*Song).Delete(); err == nil {
|
||||
if q.Len() > 1 {
|
||||
q.NextItem()
|
||||
q.PrepareAndPlayNextItem()
|
||||
} else {
|
||||
q.queue = q.queue[:0]
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
q.NextSong()
|
||||
if q.Len() != 0 {
|
||||
q.PrepareAndPlayNextSong()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *SongQueue) PrepareAndPlayNextItem() {
|
||||
if q.Len() != 0 {
|
||||
if q.CurrentItem().ItemType() == "playlist" {
|
||||
if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Download(); err == nil {
|
||||
q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(AUDIO_FAIL_MSG, false)
|
||||
q.OnItemFinished()
|
||||
}
|
||||
} else {
|
||||
if err := q.CurrentItem().(*Song).Download(); err == nil {
|
||||
q.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(AUDIO_FAIL_MSG, false)
|
||||
q.OnItemFinished()
|
||||
}
|
||||
}
|
||||
// Prepares next song and plays it if the download succeeds. Otherwise the function will print an error message
|
||||
// to the channel and skip to the next song.
|
||||
func (q *SongQueue) PrepareAndPlayNextSong() {
|
||||
if err := q.CurrentSong().Download(); err == nil {
|
||||
q.CurrentSong().Play()
|
||||
} else {
|
||||
dj.client.Self.Channel.Send(AUDIO_FAIL_MSG, false)
|
||||
q.OnSongFinished()
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue