Example #1
0
        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,
            };
        }
Example #2
0
        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();
        }