playlist and stuff

This commit is contained in:
José Henrique Ivanchechen 2023-02-07 22:40:08 -03:00
parent bf03a2e9af
commit 79b1a0e15e
2 changed files with 112 additions and 25 deletions

View File

@ -68,7 +68,7 @@ namespace TextCommandFramework.Modules
var user = Context.User; var user = Context.User;
if (user.IsBot) return; if (user.IsBot) return;
await PlayerService.Stop(Context.Guild.Id); await PlayerService.Leave(Context.Guild.Id);
} }
[Command("guild_only")] [Command("guild_only")]

View File

@ -5,7 +5,6 @@ using Discord.Rest;
using Discord.WebSocket; using Discord.WebSocket;
using System.Diagnostics; using System.Diagnostics;
using YoutubeExplode; using YoutubeExplode;
using YoutubeExplode.Search;
using YoutubeExplode.Videos; using YoutubeExplode.Videos;
using YoutubeExplode.Videos.Streams; using YoutubeExplode.Videos.Streams;
@ -20,24 +19,63 @@ namespace Kasbot.Services
Clients = new Dictionary<ulong, Connection>(); Clients = new Dictionary<ulong, Connection>();
} }
private async Task<MemoryStream?> DownloadAudioFromYoutube(Media media) private async Task<List<Media>> DownloadPlaylistMetadataFromYoutube(SocketUserMessage message, string search)
{ {
var memoryStream = new MemoryStream(); var list = new List<Media>();
var youtube = new YoutubeClient(); var youtube = new YoutubeClient();
IVideo? videoId = null; var playlistInfo = await youtube.Playlists.GetAsync(search);
await youtube.Playlists.GetVideosAsync(search).ForEachAsync(videoId =>
{
var media = new Media();
media.Name = videoId.Title;
media.Length = videoId.Duration ?? new TimeSpan(0);
media.VideoId = videoId.Id;
media.Message = message;
list.Add(media);
});
await message.Channel.SendMessageAsync($"Queued **{list.Count}** items from *{playlistInfo.Title}* playlist.");
return list;
}
private async Task<Media> DownloadMetadataFromYoutube(Media media)
{
var youtube = new YoutubeClient();
IVideo? videoId;
if (media.Search.StartsWith("http://") || media.Search.StartsWith("https://")) if (media.Search.StartsWith("http://") || media.Search.StartsWith("https://"))
videoId = await youtube.Videos.GetAsync(media.Search); videoId = await youtube.Videos.GetAsync(media.Search);
else else
videoId = await youtube.Search.GetVideosAsync(media.Search).FirstOrDefaultAsync(); videoId = await youtube.Search.GetVideosAsync(media.Search).FirstOrDefaultAsync();
if (videoId == null) if (videoId == null)
{
return media;
}
media.Name = videoId.Title;
media.Length = videoId.Duration ?? new TimeSpan(0);
media.VideoId = videoId.Id;
return media;
}
private async Task<MemoryStream?> DownloadAudioFromYoutube(Media media)
{
if (media.VideoId == null)
{ {
return null; return null;
} }
var streamInfoSet = await youtube.Videos.Streams.GetManifestAsync(videoId.Id); var memoryStream = new MemoryStream();
var youtube = new YoutubeClient();
var streamInfoSet = await youtube.Videos.Streams.GetManifestAsync((VideoId) media.VideoId);
var streamInfo = streamInfoSet.GetAudioOnlyStreams().GetWithHighestBitrate(); var streamInfo = streamInfoSet.GetAudioOnlyStreams().GetWithHighestBitrate();
var streamVideo = await youtube.Videos.Streams.GetAsync(streamInfo); var streamVideo = await youtube.Videos.Streams.GetAsync(streamInfo);
streamVideo.Position = 0; streamVideo.Position = 0;
@ -45,9 +83,6 @@ namespace Kasbot.Services
streamVideo.CopyTo(memoryStream); streamVideo.CopyTo(memoryStream);
memoryStream.Position = 0; memoryStream.Position = 0;
media.Name = videoId.Title;
media.Length = videoId.Duration.GetValueOrDefault();
return memoryStream; return memoryStream;
} }
@ -77,12 +112,7 @@ namespace Kasbot.Services
if (Clients.TryGetValue(Context.Guild.Id, out var conn)) if (Clients.TryGetValue(Context.Guild.Id, out var conn))
{ {
conn.Queue.Enqueue(media); await Enqueue(Context.Guild.Id, conn, media);
if (conn.Queue.Count == 1)
{
await PlayNext(Context.Guild.Id);
}
return; return;
} }
@ -92,11 +122,45 @@ namespace Kasbot.Services
audioClient.Disconnected += (ex) => AudioClient_ClientDisconnected(Context.Guild.Id); audioClient.Disconnected += (ex) => AudioClient_ClientDisconnected(Context.Guild.Id);
conn = CreateConnection(audioClient, Context.Guild.Id); conn = CreateConnection(audioClient, Context.Guild.Id);
await Enqueue(Context.Guild.Id, conn, media);
}
private async Task Enqueue(ulong guildId, Connection conn, Media media)
{
if (media.Search.StartsWith("https://") && media.Search.Contains("playlist?list="))
{
var startPlay = conn.Queue.Count == 0;
var medias = await DownloadPlaylistMetadataFromYoutube(media.Message, media.Search);
medias.ForEach(m => conn.Queue.Enqueue(m));
if (startPlay)
{
await PlayNext(guildId);
}
return;
}
media = await DownloadMetadataFromYoutube(media);
if (media.VideoId == null)
{
var message = await media.Message.Channel.SendMessageAsync($"No video found for \"{media.Search}\".");
await Task.Delay(3_000);
await message.DeleteAsync();
return;
}
conn.Queue.Enqueue(media); conn.Queue.Enqueue(media);
if (conn.Queue.Count == 1) if (conn.Queue.Count == 1)
{ {
await PlayNext(Context.Guild.Id); await PlayNext(guildId);
}
else
{
var message = $"Queued **{media.Name}** *({media.Length.TotalMinutes:00}:{media.Length.Seconds:00})*";
media.QueueMessage = await media.Message.Channel.SendMessageAsync(message);
} }
} }
@ -127,9 +191,14 @@ namespace Kasbot.Services
var audioClient = Clients[guildId].AudioClient; var audioClient = Clients[guildId].AudioClient;
var ffmpeg = CreateStream(); var ffmpeg = CreateStream();
var message = $"⏯ Playing: {nextMedia.Name} **({nextMedia.Length.TotalMinutes:00}:{nextMedia.Length.Seconds:00})**"; var message = $"⏯ Playing: **{nextMedia.Name}** *({nextMedia.Length.TotalMinutes:00}:{nextMedia.Length.Seconds:00})*";
nextMedia.PlayMessage = await nextMedia.Message.Channel.SendMessageAsync(message); nextMedia.PlayMessage = await nextMedia.Message.Channel.SendMessageAsync(message);
if (nextMedia.QueueMessage != null)
{
await nextMedia.QueueMessage.DeleteAsync();
}
Task stdin = new Task(() => Task stdin = new Task(() =>
{ {
using (var input = mp3Stream) using (var input = mp3Stream)
@ -174,10 +243,10 @@ namespace Kasbot.Services
ffmpeg.Close(); ffmpeg.Close();
await nextMedia.Message.DeleteAsync();
await nextMedia.PlayMessage.DeleteAsync(); await nextMedia.PlayMessage.DeleteAsync();
Clients[guildId].Queue.Dequeue(); if (Clients[guildId].Queue.Count > 0)
Clients[guildId].Queue.Dequeue();
await PlayNext(guildId); await PlayNext(guildId);
} }
@ -200,7 +269,7 @@ namespace Kasbot.Services
if (process == null || process.HasExited) if (process == null || process.HasExited)
{ {
throw new Exception("Sorry, ffmpeg killed himself in a tragic accident!"); throw new Exception("Sorry, ffmpeg killed itself in a tragic accident!");
} }
return process; return process;
@ -228,15 +297,15 @@ namespace Kasbot.Services
var media = Clients[guildId]; var media = Clients[guildId];
foreach (var v in media.Queue) foreach (var v in media.Queue.Skip(1))
{ {
await RemoveMediaMessages(v); await RemoveMediaMessages(v);
} }
if (media.AudioClient != null) media.Queue.Clear();
await media.AudioClient.StopAsync();
Clients.Remove(guildId); if (media.CurrentAudioStream != null)
media.CurrentAudioStream.Close();
} }
private async Task RemoveMediaMessages(Media media) private async Task RemoveMediaMessages(Media media)
@ -248,6 +317,20 @@ namespace Kasbot.Services
} }
catch { } catch { }
} }
public async Task Leave(ulong guildId)
{
if (!Clients.ContainsKey(guildId))
return;
await Stop(guildId);
var media = Clients[guildId];
if (media.AudioClient != null)
await media.AudioClient.StopAsync();
Clients.Remove(guildId);
}
} }
public class Connection public class Connection
@ -260,9 +343,13 @@ namespace Kasbot.Services
public class Media public class Media
{ {
public string Search { get; set; } public string Search { get; set; }
public string Name { get; set; } public string Name { get; set; }
public TimeSpan Length { get; set; } public TimeSpan Length { get; set; }
public VideoId? VideoId { get; set; }
public SocketUserMessage Message { get; set; } public SocketUserMessage Message { get; set; }
public RestUserMessage PlayMessage { get; set; } public RestUserMessage PlayMessage { get; set; }
public RestUserMessage? QueueMessage { get; set; }
} }
} }