Added ability to add YouTube playlists to queue
This commit is contained in:
parent
39f7b8dcab
commit
aa86b570a0
112
commands.go
112
commands.go
|
@ -22,8 +22,8 @@ import (
|
|||
// it contains a command.
|
||||
func parseCommand(user *gumble.User, username, command string) {
|
||||
var com, argument string
|
||||
if strings.Contains(command, " ") {
|
||||
sanitizedCommand := sanitize.HTML(command)
|
||||
sanitizedCommand := sanitize.HTML(command)
|
||||
if strings.Contains(sanitizedCommand, " ") {
|
||||
parsedCommand := strings.Split(sanitizedCommand, " ")
|
||||
com, argument = parsedCommand[0], parsedCommand[1]
|
||||
} else {
|
||||
|
@ -42,14 +42,28 @@ func parseCommand(user *gumble.User, username, command string) {
|
|||
// Skip command
|
||||
case dj.conf.Aliases.SkipAlias:
|
||||
if dj.HasPermission(username, dj.conf.Permissions.AdminSkip) {
|
||||
skip(username, false)
|
||||
skip(user, username, false, false)
|
||||
} else {
|
||||
user.Send(NO_PERMISSION_MSG)
|
||||
}
|
||||
// Skip playlist command
|
||||
case dj.conf.Aliases.SkipPlaylistAlias:
|
||||
if dj.HasPermission(username, dj.conf.Permissions.AdminAddPlaylists) {
|
||||
skip(user, username, false, true)
|
||||
} else {
|
||||
user.Send(NO_PERMISSION_MSG)
|
||||
}
|
||||
// Forceskip command
|
||||
case dj.conf.Aliases.AdminSkipAlias:
|
||||
if dj.HasPermission(username, true) {
|
||||
skip(username, true)
|
||||
skip(user, username, true, false)
|
||||
} else {
|
||||
user.Send(NO_PERMISSION_MSG)
|
||||
}
|
||||
// Playlist forceskip command
|
||||
case dj.conf.Aliases.AdminSkipPlaylistAlias:
|
||||
if dj.HasPermission(username, true) {
|
||||
skip(user, username, true, true)
|
||||
} else {
|
||||
user.Send(NO_PERMISSION_MSG)
|
||||
}
|
||||
|
@ -114,45 +128,93 @@ func add(user *gumble.User, username, url string) {
|
|||
|
||||
if matchFound {
|
||||
newSong := NewSong(username, shortUrl)
|
||||
if err := dj.queue.AddSong(newSong); err == nil {
|
||||
if err := dj.queue.AddItem(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() {
|
||||
dj.currentSong = dj.queue.NextSong()
|
||||
if err := dj.currentSong.Download(); err == nil {
|
||||
dj.currentSong.Play()
|
||||
if err := dj.queue.CurrentItem().(*Song).Download(); err == nil {
|
||||
dj.queue.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
user.Send(AUDIO_FAIL_MSG)
|
||||
dj.currentSong.Delete()
|
||||
dj.queue.CurrentItem().(*Song).Delete()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(errors.New("Could not add the Song to the queue."))
|
||||
}
|
||||
} else {
|
||||
user.Send(INVALID_URL_MSG)
|
||||
// Check to see if we have a playlist URL instead.
|
||||
youtubePlaylistPattern := `https?:\/\/www\.youtube\.com\/playlist\?list=(\w+)`
|
||||
if re, err := regexp.Compile(youtubePlaylistPattern); err == nil {
|
||||
if re.MatchString(url) {
|
||||
if dj.HasPermission(username, dj.conf.Permissions.AdminAddPlaylists) {
|
||||
shortUrl = re.FindStringSubmatch(url)[1]
|
||||
newPlaylist := NewPlaylist(username, shortUrl)
|
||||
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 {
|
||||
user.Send(AUDIO_FAIL_MSG)
|
||||
dj.queue.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
user.Send(NO_PLAYLIST_PERMISSION_MSG)
|
||||
}
|
||||
} else {
|
||||
user.Send(INVALID_URL_MSG)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Performs skip functionality. Adds a skip to the skippers slice for the current song, and then
|
||||
// evaluates if a skip should be performed. Both skip and forceskip are implemented here.
|
||||
func skip(user string, admin bool) {
|
||||
if err := dj.currentSong.AddSkip(user); 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, user), false)
|
||||
}
|
||||
if dj.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 {
|
||||
dj.OnSongFinished()
|
||||
func skip(user *gumble.User, username string, admin, playlistSkip bool) {
|
||||
if playlistSkip {
|
||||
if dj.queue.CurrentItem().ItemType() == "playlist" {
|
||||
if err := dj.queue.CurrentItem().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
|
||||
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."))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(errors.New("An error occurred while stopping the current song."))
|
||||
panic(errors.New("An error occurred while adding a skip to the current playlist."))
|
||||
}
|
||||
} else {
|
||||
user.Send(NO_PLAYLIST_PLAYING_MSG)
|
||||
}
|
||||
} else {
|
||||
panic(errors.New("An error occurred while adding a skip to the current song."))
|
||||
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 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 {
|
||||
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."))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(errors.New("An error occurred while adding a skip to the current song."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
25
main.go
25
main.go
|
@ -24,7 +24,6 @@ type mumbledj struct {
|
|||
defaultChannel string
|
||||
conf DjConfig
|
||||
queue *SongQueue
|
||||
currentSong *Song
|
||||
audioStream *gumble_ffmpeg.Stream
|
||||
homeDir string
|
||||
}
|
||||
|
@ -51,7 +50,7 @@ func (dj *mumbledj) OnConnect(e *gumble.ConnectEvent) {
|
|||
|
||||
if audioStream, err := gumble_ffmpeg.New(dj.client); err == nil {
|
||||
dj.audioStream = audioStream
|
||||
dj.audioStream.Done = dj.OnSongFinished
|
||||
dj.audioStream.Done = dj.queue.OnItemFinished
|
||||
dj.audioStream.SetVolume(dj.conf.Volume.DefaultVolume)
|
||||
} else {
|
||||
panic(err)
|
||||
|
@ -86,28 +85,6 @@ func (dj *mumbledj) HasPermission(username string, command bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// OnSongFinished event. Deletes song that just finished playing, then queues, downloads, and plays
|
||||
// the next song if it exists.
|
||||
func (dj *mumbledj) OnSongFinished() {
|
||||
if err := dj.currentSong.Delete(); err == nil {
|
||||
if dj.queue.Len() != 0 {
|
||||
dj.currentSong = dj.queue.NextSong()
|
||||
if dj.currentSong != nil {
|
||||
if err := dj.currentSong.Download(); err == nil {
|
||||
dj.currentSong.Play()
|
||||
} else {
|
||||
username := dj.currentSong.submitter
|
||||
user := dj.client.Self().Channel().Users().Find(username)
|
||||
user.Send(AUDIO_FAIL_MSG)
|
||||
dj.OnSongFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// dj variable declaration. This is done outside of main() to allow global use.
|
||||
var dj = mumbledj{
|
||||
keepAlive: make(chan bool),
|
||||
|
|
|
@ -13,6 +13,10 @@ CommandPrefix = "!"
|
|||
# DEFAULT VALUE: 0.5
|
||||
SkipRatio = 0.5
|
||||
|
||||
# Ratio that must be met or exceeded to trigger a playlist skip
|
||||
# DEFAULT VALUE: 0.5
|
||||
PlaylistSkipRatio = 0.5
|
||||
|
||||
|
||||
[Volume]
|
||||
|
||||
|
@ -39,6 +43,10 @@ AddAlias = "add"
|
|||
# DEFAULT VALUE: "skip"
|
||||
SkipAlias = "skip"
|
||||
|
||||
# Alias used for playlist skip command
|
||||
# DEFAULT VALUE: "skipplaylist"
|
||||
SkipPlaylistAlias = "skipplaylist"
|
||||
|
||||
# Alias used for admin skip command
|
||||
# DEFAULT VALUE: "forceskip"
|
||||
AdminSkipAlias = "forceskip"
|
||||
|
@ -79,6 +87,10 @@ Admins = "Matt"
|
|||
# DEFAULT VALUE: false
|
||||
AdminAdd = false
|
||||
|
||||
# Make playlist adds an admin only action?
|
||||
# DEFAULT VALUE: false
|
||||
AdminAddPlaylists = false
|
||||
|
||||
# Make skip an admin command?
|
||||
# DEFAULT VALUE: false
|
||||
AdminSkip = false
|
||||
|
|
|
@ -16,8 +16,9 @@ import (
|
|||
// Golang struct representation of mumbledj.gcfg file structure for parsing.
|
||||
type DjConfig struct {
|
||||
General struct {
|
||||
CommandPrefix string
|
||||
SkipRatio float32
|
||||
CommandPrefix string
|
||||
SkipRatio float32
|
||||
PlaylistSkipRatio float32
|
||||
}
|
||||
Volume struct {
|
||||
DefaultVolume float32
|
||||
|
@ -25,23 +26,26 @@ type DjConfig struct {
|
|||
HighestVolume float32
|
||||
}
|
||||
Aliases struct {
|
||||
AddAlias string
|
||||
SkipAlias string
|
||||
AdminSkipAlias string
|
||||
VolumeAlias string
|
||||
MoveAlias string
|
||||
ReloadAlias string
|
||||
KillAlias string
|
||||
AddAlias string
|
||||
SkipAlias string
|
||||
SkipPlaylistAlias string
|
||||
AdminSkipAlias string
|
||||
AdminSkipPlaylistAlias string
|
||||
VolumeAlias string
|
||||
MoveAlias string
|
||||
ReloadAlias string
|
||||
KillAlias string
|
||||
}
|
||||
Permissions struct {
|
||||
AdminsEnabled bool
|
||||
Admins []string
|
||||
AdminAdd bool
|
||||
AdminSkip bool
|
||||
AdminVolume bool
|
||||
AdminMove bool
|
||||
AdminReload bool
|
||||
AdminKill bool
|
||||
AdminsEnabled bool
|
||||
Admins []string
|
||||
AdminAdd bool
|
||||
AdminAddPlaylists bool
|
||||
AdminSkip bool
|
||||
AdminVolume bool
|
||||
AdminMove bool
|
||||
AdminReload bool
|
||||
AdminKill bool
|
||||
}
|
||||
}
|
||||
|
||||
|
|
122
playlist.go
Normal file
122
playlist.go
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* MumbleDJ
|
||||
* By Matthieu Grieger
|
||||
* playlist.go
|
||||
* Copyright (c) 2014 Matthieu Grieger (MIT License)
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jmoiron/jsonq"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Playlist type declaration.
|
||||
type Playlist struct {
|
||||
songs *SongQueue
|
||||
youtubeId string
|
||||
title string
|
||||
submitter string
|
||||
skippers []string
|
||||
skipped bool
|
||||
}
|
||||
|
||||
// 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 {
|
||||
queue := NewSongQueue()
|
||||
jsonUrl := fmt.Sprintf("http://gdata.youtube.com/feeds/api/playlists/%s?v=2&alt=jsonc&maxresults=25", id)
|
||||
jsonString := ""
|
||||
|
||||
if response, err := http.Get(jsonUrl); err == nil {
|
||||
defer response.Body.Close()
|
||||
if body, err := ioutil.ReadAll(response.Body); err == nil {
|
||||
jsonString = string(body)
|
||||
}
|
||||
}
|
||||
|
||||
jsonData := map[string]interface{}{}
|
||||
decoder := json.NewDecoder(strings.NewReader(jsonString))
|
||||
decoder.Decode(&jsonData)
|
||||
jq := jsonq.NewQuery(jsonData)
|
||||
|
||||
playlistTitle, _ := jq.String("data", "title")
|
||||
playlistItems, _ := jq.Int("data", "totalItems")
|
||||
if playlistItems > 25 {
|
||||
playlistItems = 25
|
||||
}
|
||||
|
||||
for i := 0; i < playlistItems; i++ {
|
||||
index := strconv.Itoa(i)
|
||||
songTitle, _ := jq.String("data", "items", index, "video", "title")
|
||||
songId, _ := jq.String("data", "items", index, "video", "id")
|
||||
songThumbnail, _ := jq.String("data", "items", index, "video", "thumbnail", "hqDefault")
|
||||
duration, _ := jq.Int("data", "items", index, "video", "duration")
|
||||
songDuration := fmt.Sprintf("%d:%02d", duration/60, duration%60)
|
||||
newSong := &Song{
|
||||
submitter: user,
|
||||
title: songTitle,
|
||||
youtubeId: songId,
|
||||
playlistId: id,
|
||||
duration: songDuration,
|
||||
thumbnailUrl: songThumbnail,
|
||||
}
|
||||
queue.AddItem(newSong)
|
||||
}
|
||||
|
||||
playlist := &Playlist{
|
||||
songs: queue,
|
||||
youtubeId: id,
|
||||
title: playlistTitle,
|
||||
submitter: user,
|
||||
skipped: false,
|
||||
}
|
||||
return playlist
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (p *Playlist) AddSkip(username string) error {
|
||||
for _, user := range p.skippers {
|
||||
if username == user {
|
||||
return errors.New("This user has already skipped the current song.")
|
||||
}
|
||||
}
|
||||
p.skippers = append(p.skippers, 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 {
|
||||
if username == user {
|
||||
p.skippers = append(p.skippers[:i], p.skippers[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("This user has not skipped the song.")
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Returns "playlist" as the item type. Used for differentiating Songs from Playlists.
|
||||
func (p *Playlist) ItemType() string {
|
||||
return "playlist"
|
||||
}
|
9
song.go
9
song.go
|
@ -24,8 +24,10 @@ type Song struct {
|
|||
submitter string
|
||||
title string
|
||||
youtubeId string
|
||||
playlistId string
|
||||
duration string
|
||||
thumbnailUrl string
|
||||
itemType string
|
||||
skippers []string
|
||||
}
|
||||
|
||||
|
@ -56,8 +58,10 @@ func NewSong(user, id string) *Song {
|
|||
submitter: user,
|
||||
title: videoTitle,
|
||||
youtubeId: id,
|
||||
playlistId: "",
|
||||
duration: videoDuration,
|
||||
thumbnailUrl: videoThumbnail,
|
||||
itemType: "song",
|
||||
}
|
||||
return song
|
||||
}
|
||||
|
@ -127,3 +131,8 @@ 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"
|
||||
}
|
||||
|
|
94
songqueue.go
94
songqueue.go
|
@ -11,38 +11,106 @@ 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.
|
||||
type SongQueue struct {
|
||||
queue []*Song
|
||||
queue []QueueItem
|
||||
}
|
||||
|
||||
// Initializes a new queue and returns the new SongQueue.
|
||||
func NewSongQueue() *SongQueue {
|
||||
return &SongQueue{
|
||||
queue: make([]*Song, 0),
|
||||
queue: make([]QueueItem, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a song to the SongQueue.
|
||||
func (q *SongQueue) AddSong(s *Song) error {
|
||||
beforeLen := len(q.queue)
|
||||
q.queue = append(q.queue, s)
|
||||
// Adds an item to the SongQueue.
|
||||
func (q *SongQueue) AddItem(i QueueItem) error {
|
||||
beforeLen := q.Len()
|
||||
q.queue = append(q.queue, i)
|
||||
if len(q.queue) == beforeLen+1 {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("Could not add Song to the SongQueue.")
|
||||
return errors.New("Could not add QueueItem to the SongQueue.")
|
||||
}
|
||||
}
|
||||
|
||||
// Moves to the next song in SongQueue. NextSong() pops the first value of the queue, and is stored
|
||||
// in dj.currentSong.
|
||||
func (q *SongQueue) NextSong() *Song {
|
||||
s, queue := q.queue[0], q.queue[1:]
|
||||
q.queue = queue
|
||||
return s
|
||||
// Returns the current QueueItem.
|
||||
func (q *SongQueue) CurrentItem() QueueItem {
|
||||
return q.queue[0]
|
||||
}
|
||||
|
||||
// Moves to the next item in SongQueue. NextItem() removes the first value in the queue.
|
||||
func (q *SongQueue) NextItem() {
|
||||
q.queue = q.queue[1:]
|
||||
}
|
||||
|
||||
// Returns the length of the SongQueue.
|
||||
func (q *SongQueue) Len() int {
|
||||
return len(q.queue)
|
||||
}
|
||||
|
||||
// OnItemFinished event. Deletes item that just finished playing, then queues the next item.
|
||||
func (q *SongQueue) OnItemFinished() {
|
||||
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[1:]
|
||||
}
|
||||
} 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 {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
if err := q.CurrentItem().(*Song).Delete(); err == nil {
|
||||
if q.Len() > 1 {
|
||||
q.NextItem()
|
||||
q.PrepareAndPlayNextItem()
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *SongQueue) PrepareAndPlayNextItem() {
|
||||
if q.CurrentItem().ItemType() == "playlist" {
|
||||
if err := q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Download(); err == nil {
|
||||
q.CurrentItem().(*Playlist).songs.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
username := q.CurrentItem().(*Playlist).submitter
|
||||
user := dj.client.Self().Channel().Users().Find(username)
|
||||
user.Send(AUDIO_FAIL_MSG)
|
||||
q.OnItemFinished()
|
||||
}
|
||||
} else {
|
||||
if err := q.CurrentItem().(*Song).Download(); err == nil {
|
||||
q.CurrentItem().(*Song).Play()
|
||||
} else {
|
||||
username := q.CurrentItem().(*Song).submitter
|
||||
user := dj.client.Self().Channel().Users().Find(username)
|
||||
user.Send(AUDIO_FAIL_MSG)
|
||||
q.OnItemFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
31
strings.go
31
strings.go
|
@ -10,6 +10,9 @@ package main
|
|||
// Message shown to users when they do not have permission to execute a command.
|
||||
const NO_PERMISSION_MSG = "You do not have permission to execute that command."
|
||||
|
||||
// Message shown to users when they try to add a playlist to the queue and do not have permission to do so.
|
||||
const NO_PLAYLIST_PERMISSION_MSG = "You do not have permission to add playlists to the queue."
|
||||
|
||||
// Message shown to users when they try to execute a command that doesn't exist.
|
||||
const COMMAND_DOESNT_EXIST_MSG = "The command you entered does not exist."
|
||||
|
||||
|
@ -23,6 +26,9 @@ const INVALID_URL_MSG = "The URL you submitted does not match the required forma
|
|||
// no song is playing.
|
||||
const NO_MUSIC_PLAYING_MSG = "There is no music playing at the moment."
|
||||
|
||||
// Message shown to users when they attempt to skip a playlist when there is no playlist playing.
|
||||
const NO_PLAYLIST_PLAYING_MSG = "There is no playlist playing at the moment."
|
||||
|
||||
// Message shown to users when they issue a command that requires an argument and one was not supplied.
|
||||
const NO_ARGUMENT_MSG = "The command you issued requires an argument and you did not provide one."
|
||||
|
||||
|
@ -32,11 +38,11 @@ const NOT_IN_VOLUME_RANGE_MSG = "Out of range. The volume must be between %f and
|
|||
// Message shown to user when a successful configuration reload finishes.
|
||||
const CONFIG_RELOAD_SUCCESS_MSG = "The configuration has been successfully reloaded."
|
||||
|
||||
// Message shown to user when an admin skips a song.
|
||||
// Message shown to users when an admin skips a song.
|
||||
const ADMIN_SONG_SKIP_MSG = "An admin has decided to skip the current song."
|
||||
|
||||
// Message shown to user when the kill command errors.
|
||||
const KILL_ERROR_MSG = "An error occurred while attempting to kill the bot."
|
||||
// Message shown to users when an admin skips a playlist.
|
||||
const ADMIN_PLAYLIST_SKIP_MSG = "An admin has decided to skip the current playlist."
|
||||
|
||||
// Message shown to users when the audio for a video could not be downloaded.
|
||||
const AUDIO_FAIL_MSG = "The audio download for this video failed. YouTube has likely not generated the audio files for this video yet."
|
||||
|
@ -61,14 +67,24 @@ const SONG_ADDED_HTML = `
|
|||
<b>%s</b> has added "%s" to the queue.
|
||||
`
|
||||
|
||||
// Message shown to channel when a playlist is added to the queue by a user.
|
||||
const PLAYLIST_ADDED_HTML = `
|
||||
<b>%s</b> has added the playlist "%s" to the queue.
|
||||
`
|
||||
|
||||
// Message shown to channel when a song has been skipped.
|
||||
const SONG_SKIPPED_HTML = `
|
||||
The number of votes required for a skip has been met. <b>Skipping song!</b>
|
||||
`
|
||||
|
||||
// Message shown to channel when a playlist has been skipped.
|
||||
const PLAYLIST_SKIPPED_HTML = `
|
||||
The number of votes required for a skip has been met. <b>Skipping playlist!</b>
|
||||
`
|
||||
|
||||
// Message shown to users when they ask for the current volume (volume command without argument)
|
||||
const CUR_VOLUME_HTML = `
|
||||
The current volume is <b>%f</b>.
|
||||
The current volume is <b>%.2f</b>.
|
||||
`
|
||||
|
||||
// Message shown to users when another user votes to skip the current song.
|
||||
|
@ -76,7 +92,12 @@ const SKIP_ADDED_HTML = `
|
|||
<b>%s</b> has voted to skip the current song.
|
||||
`
|
||||
|
||||
// Message shown to users when another user votes to skip the current playlist.
|
||||
const PLAYLIST_SKIP_ADDED_HTML = `
|
||||
<b>%s</b> has voted to skip the current playlist.
|
||||
`
|
||||
|
||||
// Message shown to users when they successfully change the volume.
|
||||
const VOLUME_SUCCESS_HTML = `
|
||||
<b>%s</b> has changed the volume to <b>%s</b>.
|
||||
<b>%s</b> has changed the volume to <b>%.2f</b>.
|
||||
`
|
||||
|
|
Reference in a new issue