diff --git a/TomatenMusic/Controllers/PlayerController.cs b/TomatenMusic/Controllers/PlayerController.cs index df1975f..49d900b 100644 --- a/TomatenMusic/Controllers/PlayerController.cs +++ b/TomatenMusic/Controllers/PlayerController.cs @@ -1,9 +1,12 @@ using DSharpPlus.Entities; using Microsoft.AspNetCore.Mvc; using TomatenMusic; +using TomatenMusic.Music; using TomatenMusic_Api; using TomatenMusic_Api.Auth.Helpers; using TomatenMusic_Api.Models; +using TomatenMusic_Api.Models.EventArgs; +using static TomatenMusic_Api.InProcessEventBus; namespace TomatenMusic_Api.Controllers; @@ -54,8 +57,9 @@ public class PlayerController : ControllerBase } [HttpPost("connect")] - public async Task PostConnection(ChannelConnectRequest request) + public async Task PostConnect(ChannelConnectRequest request) { + try { await _tomatenMusicDataService.GetGuildAsync(request.Guild_Id); @@ -85,8 +89,58 @@ public class PlayerController : ControllerBase - _eventBus.OnConnectRequestEvent(new InProcessEventBus.ChannelConnectEventArgs(request.Guild_Id, channel)); + _eventBus.OnConnectRequestEvent(new ChannelConnectArgs(request.Guild_Id, channel)); return Ok(); } + + [HttpPost("disconnect")] + public async Task PostDisconnect(ChannelDisconnectRequest request) + { + try + { + await _tomatenMusicDataService.GetGuildAsync(request.GuildId); + } + catch (Exception ex) + { + return NotFound("That Guild was not found"); + } + + if (!await _tomatenMusicDataService.IsConnectedAsync(request.GuildId) == true) + return BadRequest("The Bot is not connected."); + + _eventBus.OnDisconnectRequestEvent(new ChannelDisconnectArgs(request.GuildId)); + return Ok(); + + } + + [HttpPost("play")] + public async Task PostPlay(TrackPlayRequest request) + { + try + { + await _tomatenMusicDataService.GetGuildAsync(request.GuildId); + } + catch (Exception ex) + { + return NotFound("That Guild was not found"); + } + + if (!await _tomatenMusicDataService.IsConnectedAsync(request.GuildId) == true) + return BadRequest("The Bot is not connected."); + + MusicActionResponse response; + + try + { + response = await _tomatenMusicDataService.TrackProvider.SearchAsync(request.TrackUri); + }catch (Exception ex) + { + return NotFound(ex.Message + "\n" + ex.StackTrace); + } + + _eventBus.OnPlayRequestEvent(new TrackPlayArgs(response, request.GuildId, TimeSpan.FromSeconds(request.StartTimeSeconds), request.Now)); + + return Ok(); + } } diff --git a/TomatenMusic/Models/ChannelDisconnectRequest.cs b/TomatenMusic/Models/ChannelDisconnectRequest.cs new file mode 100644 index 0000000..0e31e6d --- /dev/null +++ b/TomatenMusic/Models/ChannelDisconnectRequest.cs @@ -0,0 +1,7 @@ +namespace TomatenMusic_Api.Models.EventArgs +{ + public class ChannelDisconnectRequest + { + public ulong GuildId { get; set; } + } +} diff --git a/TomatenMusic/Models/EventArgs/ChannelConnectArgs.cs b/TomatenMusic/Models/EventArgs/ChannelConnectArgs.cs new file mode 100644 index 0000000..64d4d28 --- /dev/null +++ b/TomatenMusic/Models/EventArgs/ChannelConnectArgs.cs @@ -0,0 +1,18 @@ +using DSharpPlus.Entities; +using Emzi0767.Utilities; + +namespace TomatenMusic_Api.Models.EventArgs +{ + public class ChannelConnectArgs : AsyncEventArgs + { + public ulong Guild_Id { get; set; } + + public DiscordChannel Channel { get; set; } + + public ChannelConnectArgs(ulong guild_Id, DiscordChannel channel) + { + Guild_Id = guild_Id; + Channel = channel; + } + } +} diff --git a/TomatenMusic/Models/EventArgs/ChannelDisconnectArgs.cs b/TomatenMusic/Models/EventArgs/ChannelDisconnectArgs.cs new file mode 100644 index 0000000..42e4404 --- /dev/null +++ b/TomatenMusic/Models/EventArgs/ChannelDisconnectArgs.cs @@ -0,0 +1,13 @@ +using Emzi0767.Utilities; + +namespace TomatenMusic_Api.Models.EventArgs +{ + public class ChannelDisconnectArgs : AsyncEventArgs + { + public ulong GuildId { get; set; } + + public ChannelDisconnectArgs(ulong guildId) { GuildId = guildId; } + } + + +} diff --git a/TomatenMusic/Models/EventArgs/TrackPlayArgs.cs b/TomatenMusic/Models/EventArgs/TrackPlayArgs.cs new file mode 100644 index 0000000..cd4b43e --- /dev/null +++ b/TomatenMusic/Models/EventArgs/TrackPlayArgs.cs @@ -0,0 +1,22 @@ +using Emzi0767.Utilities; +using Lavalink4NET.Player; +using TomatenMusic.Music; + +namespace TomatenMusic_Api.Models.EventArgs +{ + public class TrackPlayArgs : AsyncEventArgs + { + public MusicActionResponse Response { get; set; } + public ulong GuildId { get; set; } + public TimeSpan StartTime { get; set; } + public bool Now { get; set; } + + public TrackPlayArgs(MusicActionResponse response, ulong guildId, TimeSpan startTime, bool now) + { + Response = response; + GuildId = guildId; + StartTime = startTime; + Now = now; + } + } +} diff --git a/TomatenMusic/Models/TrackPlayRequest.cs b/TomatenMusic/Models/TrackPlayRequest.cs new file mode 100644 index 0000000..598f3f2 --- /dev/null +++ b/TomatenMusic/Models/TrackPlayRequest.cs @@ -0,0 +1,10 @@ +namespace TomatenMusic_Api.Models +{ + public class TrackPlayRequest + { + public ulong GuildId { get; set; } + public string TrackUri { get; set; } + public bool Now { get; set; } + public int StartTimeSeconds { get; set; } + } +} diff --git a/TomatenMusic/Services/EventBus.cs b/TomatenMusic/Services/EventBus.cs index 9ec1f8a..c58224d 100644 --- a/TomatenMusic/Services/EventBus.cs +++ b/TomatenMusic/Services/EventBus.cs @@ -2,29 +2,30 @@ using Emzi0767.Utilities; using Microsoft.AspNetCore.Mvc; using TomatenMusic_Api.Models; +using TomatenMusic_Api.Models.EventArgs; namespace TomatenMusic_Api; public class InProcessEventBus { - public event AsyncEventHandler? OnConnectRequest; + public event AsyncEventHandler? OnConnectRequest; - public void OnConnectRequestEvent(ChannelConnectEventArgs e) + public event AsyncEventHandler? OnDisconnectRequest; + + public event AsyncEventHandler OnPlayRequest; + public void OnConnectRequestEvent(ChannelConnectArgs e) { _ = OnConnectRequest?.Invoke(this, e); } - public class ChannelConnectEventArgs : AsyncEventArgs - { - public ulong Guild_Id { get; set; } + public void OnDisconnectRequestEvent(ChannelDisconnectArgs e) + { + _ = OnDisconnectRequest?.Invoke(this, e); + } - public DiscordChannel Channel { get; set; } - - public ChannelConnectEventArgs(ulong guild_Id, DiscordChannel channel) - { - Guild_Id = guild_Id; - Channel = channel; - } + public void OnPlayRequestEvent(TrackPlayArgs e) + { + _ = OnPlayRequest?.Invoke(this, e); } } diff --git a/TomatenMusic/Services/TomatenMusicDataService.cs b/TomatenMusic/Services/TomatenMusicDataService.cs index 0389856..53fc3db 100644 --- a/TomatenMusic/Services/TomatenMusicDataService.cs +++ b/TomatenMusic/Services/TomatenMusicDataService.cs @@ -11,12 +11,14 @@ namespace TomatenMusic_Api public class TomatenMusicDataService : IHostedService { private ILogger _logger; - public IServiceProvider _serviceProvider { get; set; } = TomatenMusicBot.ServiceProvider; + private IServiceProvider _serviceProvider { get; set; } = TomatenMusicBot.ServiceProvider; public IAudioService _audioService { get; set; } + public TrackProvider TrackProvider { get; set; } public TomatenMusicDataService(ILogger logger) { _logger = logger; _audioService = _serviceProvider.GetRequiredService(); + TrackProvider = _serviceProvider.GetRequiredService(); } public async Task GetConnectionInfoAsync(ulong guild_id) diff --git a/TomatenMusic/Services/TomatenMusicService.cs b/TomatenMusic/Services/TomatenMusicService.cs index 28b6f2c..a8febdd 100644 --- a/TomatenMusic/Services/TomatenMusicService.cs +++ b/TomatenMusic/Services/TomatenMusicService.cs @@ -2,6 +2,7 @@ using TomatenMusic; using TomatenMusic.Music; using TomatenMusic_Api.Models; +using TomatenMusic_Api.Models.EventArgs; using static TomatenMusic_Api.InProcessEventBus; namespace TomatenMusic_Api @@ -24,11 +25,48 @@ namespace TomatenMusic_Api private void Initialize() { _inProcessEventBus.OnConnectRequest += _inProcessEventBus_OnConnectRequest; + _inProcessEventBus.OnDisconnectRequest += _inProcessEventBus_OnDisconnectRequest; + _inProcessEventBus.OnPlayRequest += _inProcessEventBus_OnPlayRequest; } - private async Task _inProcessEventBus_OnConnectRequest(InProcessEventBus sender, ChannelConnectEventArgs e) + private async Task _inProcessEventBus_OnPlayRequest(InProcessEventBus sender, TrackPlayArgs e) + { + GuildPlayer player = _audioService.GetPlayer(e.GuildId); + + if (e.Response.Tracks != null && e.Response.Tracks.Any()) + { + if (e.Now) + await player.PlayTracksNowAsync(e.Response.Tracks); + else + await player.PlayTracksAsync(e.Response.Tracks); + + return; + } + + if (e.Response.IsPlaylist) + { + if (e.Now) + await player.PlayPlaylistNowAsync(e.Response.Playlist); + else + await player.PlayPlaylistAsync(e.Response.Playlist); + }else + { + if (e.Now) + await player.PlayNowAsync(e.Response.Track, e.StartTime); + else + await player.PlayAsync(e.Response.Track, e.StartTime); + } + + } + + private async Task _inProcessEventBus_OnDisconnectRequest(InProcessEventBus sender, ChannelDisconnectArgs e) + { + GuildPlayer player = _audioService.GetPlayer(e.GuildId); + player.DisconnectAsync(); + } + + private async Task _inProcessEventBus_OnConnectRequest(InProcessEventBus sender, ChannelConnectArgs e) { - _logger.LogInformation("Channel Connected!"); GuildPlayer player = await _audioService.JoinAsync(e.Guild_Id, e.Channel.Id, true); } diff --git a/TomatenMusic/config.json b/TomatenMusic/config.json index ae2595d..5a3277c 100644 --- a/TomatenMusic/config.json +++ b/TomatenMusic/config.json @@ -1,5 +1,5 @@ { - "TOKEN": "Bot_Token", + "TOKEN": "TOKEN", "LavaLinkPassword": " ", "SpotifyClientId": " ", "SpotifyClientSecret": " ", diff --git a/TomatenMusicCore/Commands/PlayCommandGroup.cs b/TomatenMusicCore/Commands/PlayCommandGroup.cs index 07b79ab..cb79222 100644 --- a/TomatenMusicCore/Commands/PlayCommandGroup.cs +++ b/TomatenMusicCore/Commands/PlayCommandGroup.cs @@ -79,7 +79,7 @@ namespace TomatenMusic.Commands try { - if (response.isPlaylist) + if (response.IsPlaylist) { ILavalinkPlaylist playlist = response.Playlist; await player.PlayPlaylistNowAsync(playlist); @@ -232,7 +232,7 @@ namespace TomatenMusic.Commands try { - if (response.isPlaylist) + if (response.IsPlaylist) { ILavalinkPlaylist playlist = response.Playlist; await player.PlayPlaylistAsync(playlist); diff --git a/TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs b/TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs index 5699ab2..3ae2615 100644 --- a/TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs +++ b/TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs @@ -28,12 +28,12 @@ namespace TomatenMusic.Music.Entitites public Playlist YoutubeItem { get; set; } public Uri AuthorThumbnail { get; set; } - public YoutubePlaylist(string name, IEnumerable tracks, Uri uri) + public YoutubePlaylist(string name, IEnumerable tracks, string id) { - Identifier = uri.ToString().Replace("https://www.youtube.com/playlist?list=", "").Replace("https://youtube.com/playlist?list=", ""); + Identifier = id; Name = name; Tracks = tracks; - Url = uri; + Url = new Uri($"https://youtube.com/playlist?list={id}"); TrackCount = tracks.Count(); } diff --git a/TomatenMusicCore/Music/GuildPlayer.cs b/TomatenMusicCore/Music/GuildPlayer.cs index 91711a9..7bef9de 100644 --- a/TomatenMusicCore/Music/GuildPlayer.cs +++ b/TomatenMusicCore/Music/GuildPlayer.cs @@ -39,9 +39,6 @@ namespace TomatenMusic.Music _spotify = serviceProvider.GetRequiredService(); _audioService = serviceProvider.GetRequiredService(); } - - - public async override Task PlayAsync(LavalinkTrack track, TimeSpan? startTime = null, TimeSpan? endTime = null, bool noReplace = true) { @@ -76,7 +73,7 @@ namespace TomatenMusic.Music QueuePrompt.UpdateFor(GuildId); } - public async Task PlayTracksAsync(List tracks) + public async Task PlayTracksAsync(IEnumerable tracks) { EnsureNotDestroyed(); EnsureConnected(); diff --git a/TomatenMusicCore/Music/MusicActionResponse.cs b/TomatenMusicCore/Music/MusicActionResponse.cs index e4f9eef..87f8dbf 100644 --- a/TomatenMusicCore/Music/MusicActionResponse.cs +++ b/TomatenMusicCore/Music/MusicActionResponse.cs @@ -11,12 +11,12 @@ namespace TomatenMusic.Music public ILavalinkPlaylist Playlist { get; } public LavalinkTrack Track { get; } public IEnumerable Tracks { get; } - public bool isPlaylist { get; } + public bool IsPlaylist { get; } public MusicActionResponse(LavalinkTrack track = null, ILavalinkPlaylist playlist = null, IEnumerable tracks = null) { Playlist = playlist; Track = track; - isPlaylist = playlist != null; + IsPlaylist = playlist != null; Tracks = tracks; } } diff --git a/TomatenMusicCore/Music/PlayerQueue.cs b/TomatenMusicCore/Music/PlayerQueue.cs index 7bc84f5..bee1e55 100644 --- a/TomatenMusicCore/Music/PlayerQueue.cs +++ b/TomatenMusicCore/Music/PlayerQueue.cs @@ -59,7 +59,7 @@ namespace TomatenMusic.Music } - public Task QueueTracksAsync(List tracks) + public Task QueueTracksAsync(IEnumerable tracks) { return Task.Run(() => { diff --git a/TomatenMusicCore/Prompt/Model/DiscordPromptBase.cs b/TomatenMusicCore/Prompt/Model/DiscordPromptBase.cs index e3fa18b..a9db352 100644 --- a/TomatenMusicCore/Prompt/Model/DiscordPromptBase.cs +++ b/TomatenMusicCore/Prompt/Model/DiscordPromptBase.cs @@ -23,6 +23,7 @@ namespace TomatenMusic.Prompt.Model public List Options { get; protected set; } = new List(); public DiscordClient _client { get; set; } public DiscordPromptBase LastPrompt { get; protected set; } + public System.Timers.Timer TimeoutTimer { get; set; } protected ILogger _logger { get; set; } @@ -132,6 +133,16 @@ namespace TomatenMusic.Prompt.Model Interaction = interaction; Message = await interaction.CreateFollowupMessageAsync(builder); State = PromptState.OPEN; + + long timeoutTime = (Interaction.CreationTimestamp.ToUnixTimeMilliseconds() + 900000) - DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + if (TimeoutTimer != null) + TimeoutTimer.Close(); + + TimeoutTimer = new System.Timers.Timer(timeoutTime); + TimeoutTimer.Elapsed += OnTimeout; + TimeoutTimer.AutoReset = false; + TimeoutTimer.Start(); } public async Task UseAsync(DiscordMessage message) @@ -172,6 +183,22 @@ namespace TomatenMusic.Prompt.Model Message = message; await EditMessageAsync(builder); State = PromptState.OPEN; + + long timeoutTime = (Interaction.CreationTimestamp.ToUnixTimeMilliseconds() + 900000) - DateTimeOffset.Now.ToUnixTimeMilliseconds(); + + if (TimeoutTimer != null) + TimeoutTimer.Close(); + + TimeoutTimer = new System.Timers.Timer(timeoutTime); + TimeoutTimer.Elapsed += OnTimeout; + TimeoutTimer.AutoReset = false; + TimeoutTimer.Start(); + + } + + private void OnTimeout(object? sender, System.Timers.ElapsedEventArgs e) + { + _ = InvalidateAsync(); } private void AddGuids() diff --git a/TomatenMusicCore/Services/TrackProvider.cs b/TomatenMusicCore/Services/TrackProvider.cs index 1c47d38..6158b88 100644 --- a/TomatenMusicCore/Services/TrackProvider.cs +++ b/TomatenMusicCore/Services/TrackProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Web; using TomatenMusic.Music.Entitites; using TomatenMusic.Services; @@ -57,7 +58,8 @@ namespace TomatenMusic.Music if (loadResult.LoadType == TrackLoadType.PlaylistLoaded && !isSearch) return new MusicActionResponse( - playlist: await _youtubeService.PopulatePlaylistAsync(new YoutubePlaylist(loadResult.PlaylistInfo.Name, await FullTrackContext.PopulateTracksAsync(loadResult.Tracks), uri))); + playlist: await _youtubeService.PopulatePlaylistAsync( + new YoutubePlaylist(loadResult.PlaylistInfo.Name, await FullTrackContext.PopulateTracksAsync(loadResult.Tracks), ParseListId(query)))); else return new MusicActionResponse(await FullTrackContext.PopulateAsync(loadResult.Tracks.First())); @@ -79,5 +81,27 @@ namespace TomatenMusic.Music } + public string ParseListId(string url) + { + var uri = new Uri(url, UriKind.Absolute); + + // you can check host here => uri.Host <= "www.youtube.com" + + var query = HttpUtility.ParseQueryString(uri.Query); + + var videoId = string.Empty; + + if (query.AllKeys.Contains("list")) + { + videoId = query["list"]; + } + else + { + videoId = uri.Segments.Last(); + } + + return videoId; + } + } } diff --git a/TomatenMusicCore/TomatenMusicBot.cs b/TomatenMusicCore/TomatenMusicBot.cs index c460998..32ac443 100644 --- a/TomatenMusicCore/TomatenMusicBot.cs +++ b/TomatenMusicCore/TomatenMusicBot.cs @@ -99,7 +99,8 @@ namespace TomatenMusic .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton(SpotifyClientConfig.CreateDefault().WithAuthenticator(new ClientCredentialsAuthenticator(config.SpotifyClientId, config.SpotifyClientSecret)))) + .AddSingleton( + SpotifyClientConfig.CreateDefault().WithAuthenticator(new ClientCredentialsAuthenticator(config.SpotifyClientId, config.SpotifyClientSecret)))) .Build(); ServiceProvider = _host.Services; diff --git a/TomatenMusicCore/TomatenMusicCore.csproj b/TomatenMusicCore/TomatenMusicCore.csproj index 2a8bc65..620cb46 100644 --- a/TomatenMusicCore/TomatenMusicCore.csproj +++ b/TomatenMusicCore/TomatenMusicCore.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -11,17 +11,17 @@ - - - - + + + + - + - +