private async Task WriteStreamToVoiceChannel(AudioClient audioClient, AudioOutStream discord, MemoryStream memoryStream) { try { await discord.WriteAsync(memoryStream.ToArray(), 0, (int)memoryStream.Length); } finally { await memoryStream.DisposeAsync(); await discord.FlushAsync(); await discord.DisposeAsync(); // Clear current stream audioClient.CurrentStream = null; // Play next AudioOnlyStreamInfo nextSong = audioClient.GetNextSong(); if (nextSong != null) { this.StartStream(youtube, audioClient, nextSong, null); } } }
private string GetFileNameWithExtension(AudioOnlyStreamInfo streamInfo) { string extension = streamInfo.IsOpus() && ExtractOpus ? "opus" : streamInfo.Container.Name; string fileName = GetSafeFileName(VideoMetaData?.Title ?? HttpUtility.ParseQueryString(new Uri(YouTubeUrl).Query)["v"] ?? Path.GetRandomFileName()); return($"{fileName}.{extension}"); }
public void SkipCurrentSong(IMessage message, ulong guildId) { if (this.connectedChannels.TryGetValue(guildId, out AudioClient audioClient)) { AudioOnlyStreamInfo nextSong = audioClient.GetNextSong(); if (nextSong != null) { this.StartStream(youtube, audioClient, nextSong, message); } else { message.DeleteAsync(); } } }
private string GetVideoUrl(string videoId, uint channelBitrate) { var manifest = Program.YouTubeClient.Videos.Streams.GetManifestAsync(videoId).Result; AudioOnlyStreamInfo bestStream = null; foreach (var stream in manifest.GetAudioOnlyStreams().OrderBy(s => s.Bitrate)) { if (bestStream == null || stream.Bitrate > bestStream.Bitrate) { bestStream = stream; if (stream.Bitrate.BitsPerSecond > channelBitrate) { break; } } } return(bestStream.Url); }
public async Task Play(ICoreHandler handler, Message message, string param) { Video vid = (await client.Search.GetVideosAsync(param)).FirstOrDefault(); if (vid != null) { if (vid.Duration <= TimeSpan.FromMinutes(10)) { StreamManifest streamManifest = await client.Videos.Streams.GetManifestAsync(vid.Id); AudioOnlyStreamInfo audio = streamManifest.GetAudioOnly().FirstOrDefault(); if (audio != null) { MemoryStream stream = new MemoryStream(); await client.Videos.Streams.CopyToAsync(audio, stream); Debug(audio.AudioCodec); unsafe { AVPacket *pkt = ffmpeg.av_packet_alloc(); AVCodec * codec = ffmpeg.avcodec_find_decoder_by_name(audio.AudioCodec); if (codec == null) { Error($"Codec {audio.AudioCodec} not found."); return; } AVCodecParserContext *parser = ffmpeg.av_parser_init((int)codec->id); if (parser == null) { Error("Could not allocate audio codec context."); return; } AVCodecContext *context = ffmpeg.avcodec_alloc_context3(codec); if (context == null) { Error("Could not allocate audio codec context."); return; } if (ffmpeg.avcodec_open2(context, codec, null) < 0) { Error("Could not open audio codec context."); return; } AVFrame *decoded_frame = null; while (stream.Length - stream.Position > 0) { if (decoded_frame == null) { decoded_frame = ffmpeg.av_frame_alloc(); } byte[] buffer = new byte[pkt->size]; stream.Read(buffer, 0, buffer.Length); IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length); ffmpeg.av_parser_parse2(parser, context, &pkt->data, &pkt->size, (byte *)unmanagedPointer, buffer.Length, ffmpeg.AV_NOPTS_VALUE, ffmpeg.AV_NOPTS_VALUE, 0); int ret = ffmpeg.avcodec_send_packet(context, pkt); while (ret > 0) { ret = ffmpeg.avcodec_receive_frame(context, decoded_frame); int data_size = ffmpeg.av_get_bytes_per_sample(context->sample_fmt); int current = 0; for (int i = 0; i < decoded_frame->nb_samples; i++) { for (uint ch = 0; ch < context->channels; ch++) { Marshal.Copy((IntPtr)decoded_frame->data[ch] + (data_size * i), buffer, current, data_size); current += data_size; } } message.TransferSound(buffer); } Marshal.FreeHGlobal(unmanagedPointer); } } } } else { Warn("Video too long."); } } else { Warn("No video by that term."); } /*DiscordGuildTextChannel Channel = client.GetChannel(e.Message.ChannelId).Result as DiscordGuildTextChannel; * Snowflake GuildId = Channel.GuildId; * DiscordGuild Guild = await client.GetGuild(GuildId); * Snowflake ChannelId = e.Message.ChannelId; * DiscordVoiceState voiceState = e.Shard.Cache.GetVoiceState(GuildId, e.Message.Author.Id); * if (voiceState == null) { * return; * } else { * if (!SharedObjectStorage.VoiceModuleObjects.Keys.Contains(voiceState.ChannelId.Value)) { * VoiceModule music = new VoiceModule(e.Shard, voiceState.ChannelId.Value); * SharedObjectStorage.VoiceModuleObjects.Add(voiceState.ChannelId.Value, music); * if (Tempnamelist.Count <= 2) { * Tempnamelist.Add($"Temp{i++}"); * } * string filename = Tempnamelist[0]; * Tempnamelist.RemoveAt(0); * voice = new YoutubeVoiceProvider(); * voice.DoQuery(query); * client.CreateMessage(e.Message.ChannelId, "" + String.Join("\n", voice.Result)); * this.GuildId = ((await client.GetChannel(e.Message.ChannelId)) as DiscordGuildTextChannel).GuildId; * UserId = e.Message.Author.Id; * e.Shard.Gateway.OnMessageCreated += Gateway_OnMessageCreated1; * stopsignal.WaitOne(); * e.Shard.Gateway.OnMessageCreated -= Gateway_OnMessageCreated1; * voice.DownloadToFileByQuery($"Temp/{filename}").Wait(); * if (new FileInfo($"Temp/{filename}").Length <= 100) { return; } * client.CreateMessage(e.Message.ChannelId, "Playing: " + voice.CurrentSelection); * Converter c = new FFmpegConverter(); * c.TempfileClosed += TempfileClosed; * music.Transfer(new FileInfo($"Temp/{filename}"), c, playCancellationTokenSource); * } * }*/ }
public static string GetTitle(this AudioOnlyStreamInfo info) { return($"{info.Container.Name} - {info.AudioCodec} - {info.Bitrate} - ({info.Size})"); }
public static bool IsOpus(this AudioOnlyStreamInfo info) { return(info.AudioCodec == AudioCodecOpus); }
public DownloadOption(string format, AudioOnlyStreamInfo audioOnlyStreamInfo, VideoOnlyStreamInfo videoOnlyStreamInfo) : this(format, videoOnlyStreamInfo.VideoQualityLabel, new IStreamInfo[] { audioOnlyStreamInfo, videoOnlyStreamInfo }) { }
public DownloadOption(string format, AudioOnlyStreamInfo audioOnlyStreamInfo) : this(format, "Audio", new[] { audioOnlyStreamInfo }) { }
private async ValueTask PopulateStreamInfosAsync( ICollection <IStreamInfo> streamInfos, IEnumerable <IStreamInfoExtractor> streamInfoExtractors, CancellationToken cancellationToken = default) { foreach (var streamInfoExtractor in streamInfoExtractors) { var itag = streamInfoExtractor.TryGetItag() ?? throw new YoutubeExplodeException("Could not extract stream itag."); var url = streamInfoExtractor.TryGetUrl() ?? throw new YoutubeExplodeException("Could not extract stream URL."); // Get content length var contentLength = streamInfoExtractor.TryGetContentLength() ?? await _http.TryGetContentLengthAsync(url, false, cancellationToken) ?? 0; if (contentLength <= 0) { continue; // broken stream URL? } var fileSize = new FileSize(contentLength); var container = streamInfoExtractor.TryGetContainer()?.Pipe(s => new Container(s)) ?? throw new YoutubeExplodeException("Could not extract stream container."); var bitrate = streamInfoExtractor.TryGetBitrate()?.Pipe(s => new Bitrate(s)) ?? throw new YoutubeExplodeException("Could not extract stream bitrate."); var audioCodec = streamInfoExtractor.TryGetAudioCodec(); var videoCodec = streamInfoExtractor.TryGetVideoCodec(); // Muxed or video-only stream if (!string.IsNullOrWhiteSpace(videoCodec)) { var framerate = streamInfoExtractor.TryGetFramerate() ?? 24; var videoQualityLabel = streamInfoExtractor.TryGetVideoQualityLabel(); var videoQuality = !string.IsNullOrWhiteSpace(videoQualityLabel) ? VideoQuality.FromLabel(videoQualityLabel, framerate) : VideoQuality.FromItag(itag, framerate); var videoWidth = streamInfoExtractor.TryGetVideoWidth(); var videoHeight = streamInfoExtractor.TryGetVideoHeight(); var videoResolution = videoWidth is not null && videoHeight is not null ? new Resolution(videoWidth.Value, videoHeight.Value) : videoQuality.GetDefaultVideoResolution(); // Muxed if (!string.IsNullOrWhiteSpace(audioCodec)) { var streamInfo = new MuxedStreamInfo( url, container, fileSize, bitrate, audioCodec, videoCodec, videoQuality, videoResolution ); streamInfos.Add(streamInfo); } // Video-only else { var streamInfo = new VideoOnlyStreamInfo( url, container, fileSize, bitrate, videoCodec, videoQuality, videoResolution ); streamInfos.Add(streamInfo); } } // Audio-only else if (!string.IsNullOrWhiteSpace(audioCodec)) { var streamInfo = new AudioOnlyStreamInfo( url, container, fileSize, bitrate, audioCodec ); streamInfos.Add(streamInfo); } else { Debug.Fail("Stream doesn't contain neither audio nor video codec information."); } } }
private async void StartStream(YoutubeClient youtube, AudioClient audioClient, AudioOnlyStreamInfo streamInfo, IMessage?message) { Stream ytStream = await youtube.Videos.Streams.GetAsync(streamInfo); // Convert yt stream MemoryStream memoryStream = new MemoryStream(); await Cli.Wrap("ffmpeg") .WithArguments(" -hide_banner -loglevel panic -i pipe:0 -ac 2 -f s16le -ar 48000 pipe:1") .WithStandardInputPipe(PipeSource.FromStream(ytStream)) .WithStandardOutputPipe(PipeTarget.ToStream(memoryStream)) .ExecuteAsync(); // Clear stream before beginning if (audioClient.CurrentStream != null) { audioClient.CurrentStream.Dispose(); audioClient.CurrentStream = null; } AudioOutStream discord = audioClient.Client.CreatePCMStream(AudioApplication.Mixed); audioClient.CurrentStream = discord; // Delete calling command if (message != null) { await message.DeleteAsync(); } // Start playing music await this.WriteStreamToVoiceChannel(audioClient, discord, memoryStream); }
public async Task SendYoutubeAudioAsync(IMessage message, IGuildUser user, string query) { // Check that we have a query if (string.IsNullOrWhiteSpace(query)) { return; } if (this.connectedChannels.TryGetValue(user.GuildId, out AudioClient audioClient)) { VideoInfo videoInfo = new VideoInfo(user.GetName()); // Try to parse the url given to return just the video id string id; if (query.Contains("v=") && query.Length >= 3) { if (query.Contains("&")) { id = Regex.Match(query, "v=(.*?)&").ToString(); id = id.Substring(2, id.Length - 3); } else { id = Regex.Match(query, "v=(.*?)$").ToString(); id = id.Substring(2, id.Length - 2); } YoutubeExplode.Videos.Video video = await youtube.Videos.GetAsync(id); videoInfo.Title = video.Title; videoInfo.Duration = video.Duration.ToString(); videoInfo.Thumbnail = video.Thumbnails.GetFirst().Url; } else { // Get the stream from YT via search IReadOnlyList <YoutubeExplode.Search.VideoSearchResult> searchResult = await youtube.Search.GetVideosAsync(query).CollectAsync(1); YoutubeExplode.Search.VideoSearchResult result = searchResult.GetFirst(); videoInfo.Title = result.Title; videoInfo.Duration = result.Duration.ToString(); videoInfo.Thumbnail = result.Thumbnails.GetFirst().Url; id = result.Id; } // Get the stream from YT via video id StreamManifest streamManifest = await youtube.Videos.Streams.GetManifestAsync(id); AudioOnlyStreamInfo streamInfo = streamManifest.GetAudioOnlyStreams().GetFirst(); videoInfo.Stream = streamInfo; // If song currently playing, add to playlist if (audioClient.CurrentStream != null) { audioClient.PlayList.Add(videoInfo); // Delete calling command await message.DeleteAsync(); } else { // Play song audioClient.CurrentSong = videoInfo; this.StartStream(youtube, audioClient, streamInfo, message); } } }
private static void SelectMediaStreamInfoSet(StreamManifest streamManifest, Settings settings, SettingDownload settingDownload, out AudioOnlyStreamInfo audioStreamInfo, out VideoOnlyStreamInfo videoStreamInfo) { //Todo make a better selection process //by largest container bitrate audioStreamInfo = streamManifest .GetAudioOnly() .Where(s => s.Container == Container.Mp4) .OrderByDescending(s => s.Bitrate) .First(); videoStreamInfo = streamManifest .GetVideoOnly() .Where(s => s.Container == Container.Mp4) .OrderByDescending(s => s.VideoQuality) .ThenByDescending(s => s.Framerate) .First(); if (settingDownload.MediaType == MediaType.Audio) { audioStreamInfo = streamManifest .GetAudioOnly() .OrderByDescending(s => s.Bitrate) .First(); } if (settingDownload.MediaType == MediaType.Video) { videoStreamInfo = streamManifest .GetVideoOnly() .Where(s => s.Container == Container.Mp4) .OrderByDescending(s => s.VideoQuality) .ThenByDescending(s => s.Framerate) .First(); } }