public NowPlayingMessage(UserSongRequest request, VoiceModuleConfig config, Func <SocketMessageComponent, UserSongRequest, Task> skipButton) { const string youtubeIconUrl = "https://cdn4.iconfinder.com/data/icons/social-messaging-ui-color-shapes-2-free/128/social-youtube-circle-512.png"; const string twitchIconUrl = "https://www.net-aware.org.uk/siteassets/images-and-icons/application-icons/app-icons-twitch.png?w=585&scale=down"; const string spotifyIconUrl = "https://www.techspot.com/images2/downloads/topdownload/2016/12/spotify-icon-18.png"; Request = request; Config = config; SkipButtonAction = skipButton; string iconUrl; if (Request.VideoMetadata.Extractor.StartsWith("twitch")) { iconUrl = twitchIconUrl; } else if (Request.VideoMetadata.Extractor.StartsWith("spotify")) { iconUrl = spotifyIconUrl; } else { iconUrl = youtubeIconUrl; } EmbedBuilder = new EmbedBuilder() { Author = new EmbedAuthorBuilder() { Name = "Now Playing", IconUrl = iconUrl, Url = Request.VideoMetadata.WebpageUrl, }, Footer = new EmbedFooterBuilder() { Text = Request.VideoMetadata.Extractor.StartsWith("youtube") || Request.VideoMetadata.Extractor.StartsWith("spotify") ? $"👁️ {Request.VideoMetadata.ViewCount?.Humanize() ?? "0"} | 👍 {Request.VideoMetadata.LikeCount?.Humanize() ?? "0"} | 🕓 {VoiceModule.DurationAsString(Request.VideoMetadata.Duration)}" : string.Empty, }, Title = (Request.VideoMetadata.Extractor == "twitch:stream" ? $"*(LIVE)* {Request.VideoMetadata.Description}" : Request.VideoMetadata.Title) + (request.VideoMetadata.Extractor == "spotify" ? string.Empty : $" *({Request.VideoMetadata.Uploader})*"), ThumbnailUrl = Request.VideoMetadata.Thumbnails.FirstOrDefault()?.url, Description = $"Requested {Request.TimeRequested.ToDiscordTimestamp("R")} by {Request.RequestedByMention}", Color = Color.LightGrey, }; }
private async Task PlayRequestAsync(UserSongRequest request, CancellationToken cancellationToken) { if (request.VideoMetadata.LookupTitleOnYoutube) { string searchTerm = request.VideoMetadata.Title; Logger.Log($"About to lookup spotify request {searchTerm} on youtube.", LogSeverity.Debug); try { var search = await YouTubeService.SearchForAsync(searchTerm, "video", true); var video = await YouTubeService.GetVideoAsync(search.Id.VideoId); request.VideoMetadata = new VideoMetadata(video) { // Remember what the original source was. Title = request.VideoMetadata.Title, Extractor = request.VideoMetadata.Extractor, DirectAudioUrl = await YouTubeService.GetYoutubeAudioUrlAsync(video.Id), }; } catch (Exception ex) { Logger.LogException(ex, $"Lookup failed for {request.VideoMetadata.Title}"); await new ErrorMessage($"```{request.VideoMetadata.Title}```", "Couldn't lookup song.") .SendAsync(Context.Channel); _ = ContinueAsync(); return; } } if (DateTime.Now + TimeSpan.FromSeconds(request.VideoMetadata.Duration) > request.VideoMetadata.DirectAudioExpiryDate) { request.VideoMetadata.DirectAudioUrl = await YouTubeService.GetYoutubeAudioUrlAsync(request.VideoMetadata.Id); } Config.CurrentlyPlayingSongRequest = request; if (Config.IsAutoNpOn) { await DisplayCurrentlyPlayingRequestAsync(); } do { await play(true); }while (Config.IsLoopEnabled); async Task play(bool retry) { using Process ffmpeg = DownloadService.CreateStream(request.VideoMetadata); using Stream output = ffmpeg.StandardOutput.BaseStream; using AudioOutStream discord = Config.AudioClient.CreatePCMStream(AudioApplication.Music); try { // Sometimes you would get an expired YouTube audio stream even though its expiry date is in the future. // This is a hacky workaround, retrying playback if the audio stream is empty (assumed by actual playback time) var stopwatch = new Stopwatch(); stopwatch.Start(); await output.CopyToAsync(discord, cancellationToken); stopwatch.Stop(); if (retry && stopwatch.ElapsedMilliseconds * 4 < Config.CurrentlyPlayingSongRequest.VideoMetadata.Duration * 1000) { Logger.Log($"Audio playback was too short for request '{Config.CurrentlyPlayingSongRequest.VideoMetadata.Title}' ({stopwatch.ElapsedMilliseconds}/{Config.CurrentlyPlayingSongRequest.VideoMetadata.Duration * 1000}ms) and the direct audio URL will be refetched.", LogSeverity.Info); Config.CurrentlyPlayingSongRequest.VideoMetadata.DirectAudioUrl = await YouTubeService.GetYoutubeAudioUrlAsync(Config.CurrentlyPlayingSongRequest.VideoMetadata.Id); await play(false); } } finally { await discord.FlushAsync(cancellationToken); } } _ = ContinueAsync(); }