//Adds youtube clip to be played func Add(user *gumble.User, url string) { if url == "" { jew.SendPM(user, "There needs to be a link silly.") } else { //Accepted patterns for youtube links youtubePatterns := []string{ `https?:\/\/www\.youtube\.com\/watch\?v=([\w-]+)(\&t=\d*m?\d*s?)?`, `https?:\/\/youtube\.com\/watch\?v=([\w-]+)(\&t=\d*m?\d*s?)?`, `https?:\/\/youtu.be\/([\w-]+)(\?t=\d*m?\d*s?)?`, `https?:\/\/youtube.com\/v\/([\w-]+)(\?t=\d*m?\d*s?)?`, `https?:\/\/www.youtube.com\/v\/([\w-]+)(\?t=\d*m?\d*s?)?`, } matchFound := false shortURL := "" startOffset := "" //Checks if url fits accepted patterns for _, pattern := range youtubePatterns { if re, err := regexp.Compile(pattern); err == nil { if re.MatchString(url) { matchFound = true matches := re.FindAllStringSubmatch(url, -1) shortURL = matches[0][1] if len(matches[0]) == 3 { startOffset = matches[0][2] } break } } } //If url is accepted, send out JsonQuery to fetch title from google youtuble API if matchFound { var apiResponse *jsonq.JsonQuery var err error APIurl := fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s", shortURL, os.Getenv("YOUTUBE_API_KEY")) if apiResponse, err = PerformGetRequest(APIurl); err != nil { panic(err) } title, _ := apiResponse.String("items", "0", "snippet", "title") //Print title and play clip jew.client.Self.Channel.Send(fmt.Sprintf("<a href=\"%s\">%s</a> added by %s", url, title, user.Name), false) if !jew.audioStream.IsPlaying() { if err := DownloadYoutube(shortURL); err == nil { PlayYoutube(startOffset, shortURL) } else { jew.SendPM(user, "Error downloading") } } } else { jew.SendPM(user, "Url not parsable") } } }
func parse_collection(jq *jsonq.JsonQuery) *Collection { out := new(Collection) out.Count, _ = jq.Int("count") out.Hidden, _ = jq.Bool("hidden") out.Name, _ = jq.String("name") out.ResolveList, _ = jq.ArrayOfStrings("resolve_list") return out }
// NewPlaylist gathers the metadata for a YouTube playlist and returns it. func (yt YouTube) NewPlaylist(user *gumble.User, id string) ([]Song, error) { var apiResponse *jsonq.JsonQuery var songArray []Song var err error // Retrieve title of playlist url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s", id, dj.conf.ServiceKeys.Youtube) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } title, _ := apiResponse.String("items", "0", "snippet", "title") playlist := &AudioPlaylist{ id: id, title: title, } maxSongs := math.MaxInt32 if dj.conf.General.MaxSongPerPlaylist > 0 { maxSongs = dj.conf.General.MaxSongPerPlaylist } pageToken := "" for len(songArray) < maxSongs { //Iterate over the pages // Retrieve items in this page of the playlist url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=%s&key=%s&pageToken=%s", id, dj.conf.ServiceKeys.Youtube, pageToken) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } songs, _ := apiResponse.Array("items") for j := 0; j < len(songs) && len(songArray) < maxSongs; j++ { index := strconv.Itoa(j) videoID, _ := apiResponse.String("items", index, "snippet", "resourceId", "videoId") if song, err := yt.NewSong(user, videoID, "", playlist); err == nil { songArray = append(songArray, song) } } if pageToken, err = apiResponse.String("nextPageToken"); err != nil { break } } return songArray, nil }
func getStorageFromConfig(key string, jq *jsonq.JsonQuery) (gostorages.Storage, error) { storageType, err := jq.String("storage", key, "type") parameter, ok := Storages[storageType] if !ok { return nil, fmt.Errorf("Storage %s does not exist", key) } config, err := jq.Object("storage", key) if err != nil { return nil, err } storage, err := parameter(util.MapInterfaceToMapString(config)) if err != nil { return nil, err } return storage, err }
// NewRequest creates the requested song/playlist and adds to the queue func (sc SoundCloud) NewRequest(user *gumble.User, url string) ([]Song, error) { var apiResponse *jsonq.JsonQuery var songArray []Song var err error timesplit := strings.Split(url, "#t=") url = fmt.Sprintf("http://api.soundcloud.com/resolve?url=%s&client_id=%s", timesplit[0], dj.conf.ServiceKeys.SoundCloud) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, errors.New(fmt.Sprintf(INVALID_API_KEY, sc.ServiceName())) } tracks, err := apiResponse.ArrayOfObjects("tracks") if err == nil { // PLAYLIST // Create playlist title, _ := apiResponse.String("title") permalink, _ := apiResponse.String("permalink_url") playlist := &AudioPlaylist{ id: permalink, title: title, } if dj.conf.General.MaxSongPerPlaylist > 0 && len(tracks) > dj.conf.General.MaxSongPerPlaylist { tracks = tracks[:dj.conf.General.MaxSongPerPlaylist] } // Add all tracks for _, t := range tracks { if song, err := sc.NewSong(user, jsonq.NewQuery(t), 0, playlist); err == nil { songArray = append(songArray, song) } } return songArray, nil } else { // SONG // Calculate offset offset := 0 if len(timesplit) == 2 { timesplit = strings.Split(timesplit[1], ":") multiplier := 1 for i := len(timesplit) - 1; i >= 0; i-- { time, _ := strconv.Atoi(timesplit[i]) offset += time * multiplier multiplier *= 60 } } // Add the track if song, err := sc.NewSong(user, apiResponse, offset, nil); err == nil { return append(songArray, song), err } return nil, err } }
// NewYouTubeSong gathers the metadata for a song extracted from a YouTube video, and returns // the song. func NewYouTubeSong(user, id, offset string, playlist *YouTubePlaylist) (*YouTubeSong, error) { var apiResponse *jsonq.JsonQuery var err error url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails&id=%s&key=%s", id, os.Getenv("YOUTUBE_API_KEY")) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } var offsetDays, offsetHours, offsetMinutes, offsetSeconds int64 if offset != "" { offsetExp := regexp.MustCompile(`t\=(?P<days>\d+d)?(?P<hours>\d+h)?(?P<minutes>\d+m)?(?P<seconds>\d+s)?`) offsetMatch := offsetExp.FindStringSubmatch(offset) offsetResult := make(map[string]string) for i, name := range offsetExp.SubexpNames() { if i < len(offsetMatch) { offsetResult[name] = offsetMatch[i] } } if offsetResult["days"] != "" { offsetDays, _ = strconv.ParseInt(strings.TrimSuffix(offsetResult["days"], "d"), 10, 32) } if offsetResult["hours"] != "" { offsetHours, _ = strconv.ParseInt(strings.TrimSuffix(offsetResult["hours"], "h"), 10, 32) } if offsetResult["minutes"] != "" { offsetMinutes, _ = strconv.ParseInt(strings.TrimSuffix(offsetResult["minutes"], "m"), 10, 32) } if offsetResult["seconds"] != "" { offsetSeconds, _ = strconv.ParseInt(strings.TrimSuffix(offsetResult["seconds"], "s"), 10, 32) } } title, _ := apiResponse.String("items", "0", "snippet", "title") thumbnail, _ := apiResponse.String("items", "0", "snippet", "thumbnails", "high", "url") duration, _ := apiResponse.String("items", "0", "contentDetails", "duration") var days, hours, minutes, seconds int64 timestampExp := regexp.MustCompile(`P(?P<days>\d+D)?T(?P<hours>\d+H)?(?P<minutes>\d+M)?(?P<seconds>\d+S)?`) timestampMatch := timestampExp.FindStringSubmatch(duration) timestampResult := make(map[string]string) for i, name := range timestampExp.SubexpNames() { if i < len(timestampMatch) { timestampResult[name] = timestampMatch[i] } } if timestampResult["days"] != "" { days, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["days"], "D"), 10, 32) } if timestampResult["hours"] != "" { hours, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["hours"], "H"), 10, 32) } if timestampResult["minutes"] != "" { minutes, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["minutes"], "M"), 10, 32) } if timestampResult["seconds"] != "" { seconds, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["seconds"], "S"), 10, 32) } totalSeconds := int((days * 86400) + (hours * 3600) + (minutes * 60) + seconds) var durationString string if hours != 0 { if days != 0 { durationString = fmt.Sprintf("%d:%02d:%02d:%02d", days, hours, minutes, seconds) } else { durationString = fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds) } } else { durationString = fmt.Sprintf("%d:%02d", minutes, seconds) } if dj.conf.General.MaxSongDuration == 0 || totalSeconds <= dj.conf.General.MaxSongDuration { song := &YouTubeSong{ submitter: user, title: title, id: id, offset: int((offsetDays * 86400) + (offsetHours * 3600) + (offsetMinutes * 60) + offsetSeconds), filename: id + ".m4a", duration: durationString, thumbnail: thumbnail, skippers: make([]string, 0), playlist: nil, dontSkip: false, } dj.queue.AddSong(song) return song, nil } return nil, errors.New("Song exceeds the maximum allowed duration.") }
// NewYouTubePlaylist gathers the metadata for a YouTube playlist and returns it. func NewYouTubePlaylist(user, id string) (*YouTubePlaylist, error) { var apiResponse *jsonq.JsonQuery var err error // Retrieve title of playlist url := fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlists?part=snippet&id=%s&key=%s", id, os.Getenv("YOUTUBE_API_KEY")) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } title, _ := apiResponse.String("items", "0", "snippet", "title") playlist := &YouTubePlaylist{ id: id, title: title, } // Retrieve items in playlist url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=25&playlistId=%s&key=%s", id, os.Getenv("YOUTUBE_API_KEY")) if apiResponse, err = PerformGetRequest(url); err != nil { return nil, err } numVideos, _ := apiResponse.Int("pageInfo", "totalResults") if numVideos > 25 { numVideos = 25 } for i := 0; i < numVideos; i++ { index := strconv.Itoa(i) videoTitle, err := apiResponse.String("items", index, "snippet", "title") videoID, _ := apiResponse.String("items", index, "snippet", "resourceId", "videoId") videoThumbnail, _ := apiResponse.String("items", index, "snippet", "thumbnails", "high", "url") // A completely separate API call just to get the duration of a video in a // playlist? WHY GOOGLE, WHY?! var durationResponse *jsonq.JsonQuery url = fmt.Sprintf("https://www.googleapis.com/youtube/v3/videos?part=contentDetails&id=%s&key=%s", videoID, os.Getenv("YOUTUBE_API_KEY")) if durationResponse, err = PerformGetRequest(url); err != nil { return nil, err } videoDuration, _ := durationResponse.String("items", "0", "contentDetails", "duration") var days, hours, minutes, seconds int64 timestampExp := regexp.MustCompile(`P(?P<days>\d+D)?T(?P<hours>\d+H)?(?P<minutes>\d+M)?(?P<seconds>\d+S)?`) timestampMatch := timestampExp.FindStringSubmatch(videoDuration) timestampResult := make(map[string]string) for i, name := range timestampExp.SubexpNames() { if i < len(timestampMatch) { timestampResult[name] = timestampMatch[i] } } if timestampResult["days"] != "" { days, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["days"], "D"), 10, 32) } if timestampResult["hours"] != "" { hours, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["hours"], "H"), 10, 32) } if timestampResult["minutes"] != "" { minutes, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["minutes"], "M"), 10, 32) } if timestampResult["seconds"] != "" { seconds, _ = strconv.ParseInt(strings.TrimSuffix(timestampResult["seconds"], "S"), 10, 32) } totalSeconds := int((days * 86400) + (hours * 3600) + (minutes * 60) + seconds) var durationString string if hours != 0 { if days != 0 { durationString = fmt.Sprintf("%d:%02d:%02d:%02d", days, hours, minutes, seconds) } else { durationString = fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds) } } else { durationString = fmt.Sprintf("%d:%02d", minutes, seconds) } if dj.conf.General.MaxSongDuration == 0 || totalSeconds <= dj.conf.General.MaxSongDuration { playlistSong := &YouTubeSong{ submitter: user, title: videoTitle, id: videoID, filename: videoID + ".m4a", duration: durationString, thumbnail: videoThumbnail, skippers: make([]string, 0), playlist: playlist, dontSkip: false, } dj.queue.AddSong(playlistSong) } } return playlist, nil }
// NewSong creates a track and adds to the queue func (sc SoundCloud) NewSong(user *gumble.User, trackData *jsonq.JsonQuery, offset int, playlist Playlist) (Song, error) { title, _ := trackData.String("title") id, _ := trackData.Int("id") durationMS, _ := trackData.Int("duration") url, _ := trackData.String("permalink_url") thumbnail, err := trackData.String("artwork_url") if err != nil { // Song has no artwork, using profile avatar instead userObj, _ := trackData.Object("user") thumbnail, _ = jsonq.NewQuery(userObj).String("avatar_url") } song := &AudioTrack{ id: strconv.Itoa(id), title: title, url: url, thumbnail: thumbnail, submitter: user, duration: durationMS / 1000, offset: offset, format: "mp3", playlist: playlist, skippers: make([]string, 0), dontSkip: false, service: sc, } return song, nil }
// NewSong creates a track and adds to the queue func (mc Mixcloud) NewSong(user *gumble.User, trackData *jsonq.JsonQuery, offset int) (Song, error) { title, _ := trackData.String("name") id, _ := trackData.String("slug") duration, _ := trackData.Int("audio_length") url, _ := trackData.String("url") thumbnail, err := trackData.String("pictures", "large") if err != nil { // Song has no artwork, using profile avatar instead userObj, _ := trackData.Object("user") thumbnail, _ = jsonq.NewQuery(userObj).String("pictures", "thumbnail") } song := &AudioTrack{ id: id, title: title, url: url, thumbnail: thumbnail, submitter: user, duration: duration, offset: offset, format: "best", skippers: make([]string, 0), dontSkip: false, service: mc, } return song, nil }
func getString(j *jsonq.JsonQuery, s ...string) string { v, _ := j.String(s...) return v }