add files
This commit is contained in:
69
TomatenMusicCore/Music/Entitites/FullTrackContext.cs
Normal file
69
TomatenMusicCore/Music/Entitites/FullTrackContext.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TomatenMusic.Services;
|
||||
using System.Linq;
|
||||
using SpotifyAPI.Web;
|
||||
using Lavalink4NET.Player;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Lavalink4NET;
|
||||
|
||||
namespace TomatenMusic.Music.Entitites
|
||||
{
|
||||
public class FullTrackContext
|
||||
{
|
||||
public bool IsFile { get; set; }
|
||||
public string YoutubeDescription { get; set; }
|
||||
public Uri YoutubeUri { get; set; }
|
||||
public IEnumerable<string> YoutubeTags { get; set; }
|
||||
public ulong YoutubeViews { get; set; }
|
||||
public ulong YoutubeLikes { get; set; }
|
||||
public Uri YoutubeThumbnail { get; set; }
|
||||
public DateTime YoutubeUploadDate { get; set; }
|
||||
//
|
||||
// Summary:
|
||||
// Gets the author of the track.
|
||||
public Uri YoutubeAuthorThumbnail { get; set; }
|
||||
public ulong YoutubeAuthorSubs { get; set; }
|
||||
public Uri YoutubeAuthorUri { get; set; }
|
||||
public ulong? YoutubeCommentCount { get; set; }
|
||||
public string SpotifyIdentifier { get; set; }
|
||||
public SimpleAlbum SpotifyAlbum { get; set; }
|
||||
public List<SimpleArtist> SpotifyArtists { get; set; }
|
||||
public int SpotifyPopularity { get; set; }
|
||||
public Uri SpotifyUri { get; set; }
|
||||
|
||||
public static async Task<LavalinkTrack> PopulateAsync(LavalinkTrack track, string spotifyIdentifier = null)
|
||||
{
|
||||
FullTrackContext context = (FullTrackContext)track.Context;
|
||||
|
||||
if (context == null)
|
||||
context = new FullTrackContext();
|
||||
|
||||
var spotifyService = TomatenMusicBot.ServiceProvider.GetRequiredService<ISpotifyService>();
|
||||
var youtubeService = TomatenMusicBot.ServiceProvider.GetRequiredService<YoutubeService>();
|
||||
context.SpotifyIdentifier = spotifyIdentifier;
|
||||
context.YoutubeUri = new Uri($"https://youtu.be/{track.TrackIdentifier}");
|
||||
track.Context = context;
|
||||
Console.WriteLine(context);
|
||||
await youtubeService.PopulateTrackInfoAsync(track);
|
||||
await spotifyService.PopulateTrackAsync(track);
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<LavalinkTrack>> PopulateTracksAsync(IEnumerable<LavalinkTrack> tracks)
|
||||
{
|
||||
foreach (var trackItem in tracks)
|
||||
{
|
||||
await PopulateAsync(trackItem);
|
||||
}
|
||||
|
||||
return tracks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
34
TomatenMusicCore/Music/Entitites/LavalinkPlaylist.cs
Normal file
34
TomatenMusicCore/Music/Entitites/LavalinkPlaylist.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using TomatenMusic.Util;
|
||||
using DSharpPlus.Entities;
|
||||
using Lavalink4NET.Player;
|
||||
|
||||
namespace TomatenMusic.Music.Entitites
|
||||
{
|
||||
public interface LavalinkPlaylist
|
||||
{
|
||||
public string Name { get; }
|
||||
public IEnumerable<LavalinkTrack> Tracks { get; }
|
||||
public Uri Url { get; }
|
||||
public string AuthorName { get; set; }
|
||||
public Uri AuthorUri { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Identifier { get; }
|
||||
public Uri AuthorThumbnail { get; set; }
|
||||
|
||||
public TimeSpan GetLength()
|
||||
{
|
||||
TimeSpan timeSpan = TimeSpan.FromTicks(0);
|
||||
|
||||
foreach (var track in Tracks)
|
||||
{
|
||||
timeSpan = timeSpan.Add(track.Duration);
|
||||
}
|
||||
|
||||
return timeSpan;
|
||||
}
|
||||
}
|
||||
}
|
29
TomatenMusicCore/Music/Entitites/SpotifyPlaylist.cs
Normal file
29
TomatenMusicCore/Music/Entitites/SpotifyPlaylist.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Lavalink4NET.Player;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace TomatenMusic.Music.Entitites
|
||||
{
|
||||
public class SpotifyPlaylist : LavalinkPlaylist
|
||||
{
|
||||
public string Name { get; }
|
||||
public IEnumerable<LavalinkTrack> Tracks { get; }
|
||||
public Uri Url { get; set; }
|
||||
public string AuthorName { get; set; }
|
||||
public Uri AuthorUri { get; set; }
|
||||
public string Description { get; set; }
|
||||
public int Followers { get; set; }
|
||||
public string Identifier { get; }
|
||||
public Uri AuthorThumbnail { get; set; }
|
||||
|
||||
|
||||
public SpotifyPlaylist(string name, string id, IEnumerable<LavalinkTrack> tracks, Uri uri)
|
||||
{
|
||||
Name = name;
|
||||
Identifier = id;
|
||||
Tracks = tracks;
|
||||
Url = uri;
|
||||
}
|
||||
}
|
||||
}
|
38
TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs
Normal file
38
TomatenMusicCore/Music/Entitites/YoutubePlaylist.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using Google.Apis.YouTube.v3.Data;
|
||||
using Lavalink4NET.Player;
|
||||
|
||||
namespace TomatenMusic.Music.Entitites
|
||||
{
|
||||
public class YoutubePlaylist : LavalinkPlaylist
|
||||
{
|
||||
public string Name { get; }
|
||||
|
||||
public IEnumerable<LavalinkTrack> Tracks { get; }
|
||||
|
||||
public int TrackCount { get; }
|
||||
|
||||
public Uri Url { get; }
|
||||
|
||||
public string AuthorName { get; set; }
|
||||
public Uri AuthorUri { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Uri Thumbnail { get; set; }
|
||||
public DateTime CreationTime { get; set; }
|
||||
public string Identifier { get; }
|
||||
public Playlist YoutubeItem { get; set; }
|
||||
public Uri AuthorThumbnail { get; set; }
|
||||
|
||||
public YoutubePlaylist(string name, IEnumerable<LavalinkTrack> tracks, Uri uri)
|
||||
{
|
||||
Identifier = uri.ToString().Replace("https://www.youtube.com/playlist?list=", "");
|
||||
Name = name;
|
||||
Tracks = tracks;
|
||||
Url = uri;
|
||||
TrackCount = tracks.Count();
|
||||
}
|
||||
}
|
||||
}
|
342
TomatenMusicCore/Music/GuildPlayer.cs
Normal file
342
TomatenMusicCore/Music/GuildPlayer.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
using DSharpPlus;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DSharpPlus.Entities;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using TomatenMusic.Music.Entitites;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using TomatenMusic.Services;
|
||||
using TomatenMusic.Prompt.Implementation;
|
||||
using Lavalink4NET.Player;
|
||||
using Lavalink4NET.Events;
|
||||
using Lavalink4NET;
|
||||
using Lavalink4NET.Rest;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Lavalink4NET.Decoding;
|
||||
|
||||
namespace TomatenMusic.Music
|
||||
{
|
||||
public class GuildPlayer : LavalinkPlayer
|
||||
{
|
||||
|
||||
ILogger<GuildPlayer> _logger { get; set; }
|
||||
public PlayerQueue PlayerQueue { get;} = new PlayerQueue();
|
||||
public DiscordClient _client { get; set; }
|
||||
public ISpotifyService _spotify { get; set; }
|
||||
public IAudioService _audioService { get; set; }
|
||||
|
||||
public bool Autoplay { get; set; } = false;
|
||||
|
||||
public GuildPlayer()
|
||||
{
|
||||
IServiceProvider serviceProvider = TomatenMusicBot.ServiceProvider;
|
||||
_logger = serviceProvider.GetRequiredService<ILogger<GuildPlayer>>();
|
||||
var client = serviceProvider.GetRequiredService<DiscordShardedClient>();
|
||||
_client = client.GetShard(GuildId);
|
||||
|
||||
_spotify = serviceProvider.GetRequiredService<ISpotifyService>();
|
||||
_audioService = serviceProvider.GetRequiredService<IAudioService>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async override Task PlayAsync(LavalinkTrack track, TimeSpan? startTime = null, TimeSpan? endTime = null, bool noReplace = true)
|
||||
{
|
||||
|
||||
EnsureConnected();
|
||||
EnsureNotDestroyed();
|
||||
|
||||
if (State == PlayerState.NotPlaying)
|
||||
{
|
||||
PlayerQueue.LastTrack = track;
|
||||
await base.PlayAsync(track, startTime, endTime, noReplace);
|
||||
_logger.LogInformation("Started playing Track {0} on Guild {1}", track.Title, (await GetGuildAsync()).Name);
|
||||
}else
|
||||
PlayerQueue.QueueTrack(track);
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task PlayNowAsync(LavalinkTrack track, TimeSpan? startTime = null, TimeSpan? endTime = null, bool withoutQueuePrepend = false)
|
||||
{
|
||||
|
||||
EnsureConnected();
|
||||
EnsureNotDestroyed();
|
||||
|
||||
if (!withoutQueuePrepend)
|
||||
PlayerQueue.Queue = new Queue<LavalinkTrack>(PlayerQueue.Queue.Prepend(PlayerQueue.LastTrack));
|
||||
|
||||
PlayerQueue.LastTrack = track;
|
||||
await base.PlayAsync(track, startTime, endTime);
|
||||
_logger.LogInformation("Started playing Track {0} now on Guild {1}", track.Title, (await GetGuildAsync()).Name);
|
||||
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task PlayTracksAsync(List<LavalinkTrack> tracks)
|
||||
{
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
_logger.LogInformation("Started playing TrackList {0} on Guild {1}", tracks.ToString(), (await GetGuildAsync()).Name);
|
||||
|
||||
await PlayerQueue.QueueTracksAsync(tracks);
|
||||
|
||||
if (State == PlayerState.NotPlaying)
|
||||
{
|
||||
LavalinkTrack nextTrack = PlayerQueue.NextTrack().Track;
|
||||
await base.PlayAsync(nextTrack);
|
||||
}
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
public async Task PlayTracksNowAsync(IEnumerable<LavalinkTrack> tracks)
|
||||
{
|
||||
|
||||
EnsureConnected();
|
||||
EnsureNotDestroyed();
|
||||
Queue<LavalinkTrack> reversedTracks = new Queue<LavalinkTrack>(tracks);
|
||||
|
||||
LavalinkTrack track = reversedTracks.Dequeue();
|
||||
PlayerQueue.LastTrack = track;
|
||||
await base.PlayAsync(track);
|
||||
_logger.LogInformation("Started playing Track {0} on Guild {1}", track.Title, (await GetGuildAsync()).Name);
|
||||
|
||||
reversedTracks.Reverse();
|
||||
|
||||
foreach (var item in reversedTracks)
|
||||
{
|
||||
PlayerQueue.Queue = new Queue<LavalinkTrack>(PlayerQueue.Queue.Prepend(PlayerQueue.LastTrack));
|
||||
}
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task PlayPlaylistAsync(LavalinkPlaylist playlist)
|
||||
{
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
_logger.LogInformation("Started playing Playlist {0} on Guild {1}", playlist.Name, (await GetGuildAsync()).Name);
|
||||
|
||||
await PlayerQueue.QueuePlaylistAsync(playlist);
|
||||
|
||||
|
||||
if (State == PlayerState.NotPlaying)
|
||||
{
|
||||
LavalinkTrack nextTrack = PlayerQueue.NextTrack().Track;
|
||||
await base.PlayAsync(nextTrack);
|
||||
}
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task PlayPlaylistNowAsync(LavalinkPlaylist playlist)
|
||||
{
|
||||
|
||||
EnsureConnected();
|
||||
EnsureNotDestroyed();
|
||||
if (!PlayerQueue.Queue.Any())
|
||||
PlayerQueue.CurrentPlaylist = playlist;
|
||||
|
||||
Queue<LavalinkTrack> reversedTracks = new Queue<LavalinkTrack>(playlist.Tracks);
|
||||
|
||||
LavalinkTrack track = reversedTracks.Dequeue();
|
||||
PlayerQueue.LastTrack = track;
|
||||
await base.PlayAsync(track);
|
||||
_logger.LogInformation("Started playing Track {0} on Guild {1}", track.Title, (await GetGuildAsync()).Name);
|
||||
|
||||
reversedTracks.Reverse();
|
||||
|
||||
foreach (var item in reversedTracks)
|
||||
{
|
||||
PlayerQueue.Queue = new Queue<LavalinkTrack>(PlayerQueue.Queue.Prepend(PlayerQueue.LastTrack));
|
||||
}
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task RewindAsync()
|
||||
{
|
||||
MusicActionResponse response = PlayerQueue.Rewind();
|
||||
|
||||
_logger.LogInformation($"Rewinded Track {CurrentTrack.Title} for Track {response.Track.Title}");
|
||||
await base.PlayAsync(response.Track);
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task SkipAsync()
|
||||
{
|
||||
MusicActionResponse response = PlayerQueue.NextTrack(true);
|
||||
|
||||
_logger.LogInformation($"Skipped Track {CurrentTrack.Title} for Track {response.Track.Title}");
|
||||
await base.PlayAsync(response.Track);
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task TogglePauseAsync()
|
||||
{
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
if (State == PlayerState.NotPlaying) throw new InvalidOperationException("Cant pause Song! Nothing is Playing.");
|
||||
|
||||
|
||||
if (State == PlayerState.Paused)
|
||||
await ResumeAsync();
|
||||
else
|
||||
await PauseAsync();
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
|
||||
public async Task SetLoopAsync(LoopType type)
|
||||
{
|
||||
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
if (State == PlayerState.NotPlaying) throw new InvalidOperationException("Cant change LoopType! Nothing is Playing.");
|
||||
|
||||
_ = PlayerQueue.SetLoopAsync(type);
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
|
||||
}
|
||||
|
||||
public async Task ShuffleAsync()
|
||||
{
|
||||
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
await PlayerQueue.ShuffleAsync();
|
||||
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
public async override Task ConnectAsync(ulong voiceChannelId, bool selfDeaf = true, bool selfMute = false)
|
||||
{
|
||||
EnsureNotDestroyed();
|
||||
|
||||
DiscordChannel channel = await _client.GetChannelAsync(voiceChannelId);
|
||||
|
||||
if (channel.Type != ChannelType.Voice && channel.Type != ChannelType.Stage) throw new ArgumentException("The channel Id provided was not a voice channel");
|
||||
|
||||
if (State != PlayerState.NotConnected)
|
||||
throw new InvalidOperationException("The Bot is already connected.");
|
||||
|
||||
await base.ConnectAsync(voiceChannelId, selfDeaf, selfMute);
|
||||
|
||||
if (channel.Type == ChannelType.Stage)
|
||||
{
|
||||
DiscordStageInstance stageInstance = await channel.GetStageInstanceAsync();
|
||||
|
||||
if (stageInstance == null)
|
||||
stageInstance = await channel.CreateStageInstanceAsync("Music");
|
||||
await stageInstance.Channel.UpdateCurrentUserVoiceStateAsync(false);
|
||||
}
|
||||
|
||||
_logger.LogInformation("Connected to Channel {0} on Guild {1}", channel.Name, channel.Guild.Name);
|
||||
}
|
||||
public override Task DisconnectAsync()
|
||||
{
|
||||
_logger.LogInformation("Disconnected from Channel {0} on Guild {1}", VoiceChannelId, GuildId);
|
||||
|
||||
QueuePrompt.InvalidateFor(GuildId);
|
||||
return base.DisconnectAsync();
|
||||
}
|
||||
|
||||
public override async Task SeekPositionAsync(TimeSpan timeSpan)
|
||||
{
|
||||
EnsureNotDestroyed();
|
||||
EnsureConnected();
|
||||
|
||||
if (State == PlayerState.NotPlaying) throw new InvalidOperationException("Cant change LoopType! Nothing is Playing.");
|
||||
|
||||
if (timeSpan.CompareTo(CurrentTrack.Duration) == 1) throw new ArgumentException("Please specify a TimeSpan shorter than the Track");
|
||||
|
||||
await base.SeekPositionAsync(timeSpan);
|
||||
QueuePrompt.UpdateFor(GuildId);
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
QueuePrompt.InvalidateFor(GuildId);
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
public async override Task OnTrackEndAsync(TrackEndEventArgs eventArgs)
|
||||
{
|
||||
DisconnectOnStop = false;
|
||||
YoutubeService youtube = TomatenMusicBot.ServiceProvider.GetRequiredService<YoutubeService>();
|
||||
var oldTrack = CurrentTrack;
|
||||
|
||||
if (eventArgs.Reason != TrackEndReason.Finished)
|
||||
return;
|
||||
|
||||
if (eventArgs.MayStartNext)
|
||||
{
|
||||
try
|
||||
{
|
||||
MusicActionResponse response = PlayerQueue.NextTrack();
|
||||
_ = PlayNowAsync(response.Track, withoutQueuePrepend: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!Autoplay)
|
||||
{
|
||||
_logger.LogInformation("Track has ended and Queue was Empty... Idling");
|
||||
await base.OnTrackEndAsync(eventArgs);
|
||||
return;
|
||||
}
|
||||
|
||||
LavalinkTrack newTrack = await youtube.GetRelatedTrackAsync(oldTrack.TrackIdentifier);
|
||||
_logger.LogInformation($"Autoplaying for track {oldTrack.TrackIdentifier} with Track {newTrack.TrackIdentifier}");
|
||||
await PlayNowAsync(newTrack, withoutQueuePrepend: true);
|
||||
|
||||
/* try
|
||||
{
|
||||
LavalinkTrack track = await youtube.GetRelatedTrackAsync(eventArgs.TrackIdentifier);
|
||||
_logger.LogInformation($"Autoplaying for track {eventArgs.TrackIdentifier} with Track {track.TrackIdentifier}");
|
||||
await PlayAsync(track);
|
||||
}
|
||||
catch (Exception ex2)
|
||||
{
|
||||
await base.OnTrackEndAsync(eventArgs);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public async Task<DiscordChannel> GetChannelAsync()
|
||||
{
|
||||
EnsureConnected();
|
||||
EnsureNotDestroyed();
|
||||
DiscordGuild guild = await GetGuildAsync();
|
||||
|
||||
return guild.GetChannel((ulong) VoiceChannelId);
|
||||
}
|
||||
|
||||
public async Task<DiscordGuild> GetGuildAsync()
|
||||
{
|
||||
return await _client.GetGuildAsync(GuildId);
|
||||
}
|
||||
public async Task<bool> AreActionsAllowedAsync(DiscordMember member)
|
||||
{
|
||||
if (member.VoiceState == null || member.VoiceState.Channel == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (await GetChannelAsync() != null && await GetChannelAsync() != member.VoiceState.Channel)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
17
TomatenMusicCore/Music/LoopType.cs
Normal file
17
TomatenMusicCore/Music/LoopType.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DSharpPlus.SlashCommands;
|
||||
|
||||
namespace TomatenMusic.Music
|
||||
{
|
||||
public enum LoopType
|
||||
{
|
||||
[ChoiceName("Track")]
|
||||
TRACK,
|
||||
[ChoiceName("Queue")]
|
||||
QUEUE,
|
||||
[ChoiceName("None")]
|
||||
NONE
|
||||
}
|
||||
}
|
23
TomatenMusicCore/Music/MusicActionResponse.cs
Normal file
23
TomatenMusicCore/Music/MusicActionResponse.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Lavalink4NET.Player;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using TomatenMusic.Music.Entitites;
|
||||
|
||||
namespace TomatenMusic.Music
|
||||
{
|
||||
public class MusicActionResponse
|
||||
{
|
||||
public LavalinkPlaylist Playlist { get; }
|
||||
public LavalinkTrack Track { get; }
|
||||
public IEnumerable<LavalinkTrack> Tracks { get; }
|
||||
public bool isPlaylist { get; }
|
||||
public MusicActionResponse(LavalinkTrack track = null, LavalinkPlaylist playlist = null, IEnumerable<LavalinkTrack> tracks = null)
|
||||
{
|
||||
Playlist = playlist;
|
||||
Track = track;
|
||||
isPlaylist = playlist != null;
|
||||
Tracks = tracks;
|
||||
}
|
||||
}
|
||||
}
|
161
TomatenMusicCore/Music/PlayerQueue.cs
Normal file
161
TomatenMusicCore/Music/PlayerQueue.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DSharpPlus;
|
||||
using TomatenMusic.Music.Entitites;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using TomatenMusic.Util;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Lavalink4NET.Player;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace TomatenMusic.Music
|
||||
{
|
||||
public class PlayerQueue
|
||||
{
|
||||
|
||||
public Queue<LavalinkTrack> Queue { get; set; } = new Queue<LavalinkTrack>();
|
||||
public Queue<LavalinkTrack> PlayedTracks { get; set; } = new Queue<LavalinkTrack>();
|
||||
public ILogger<PlayerQueue> _logger { get; set; } = TomatenMusicBot.ServiceProvider.GetRequiredService<ILogger<PlayerQueue>>();
|
||||
public LavalinkPlaylist CurrentPlaylist { get; set; }
|
||||
|
||||
public LoopType LoopType { get; private set; } = LoopType.NONE;
|
||||
|
||||
public LavalinkTrack LastTrack { get; set; }
|
||||
|
||||
public List<LavalinkTrack> QueueLoopList { get; private set; }
|
||||
|
||||
public void QueueTrack(LavalinkTrack track)
|
||||
{
|
||||
CurrentPlaylist = null;
|
||||
Queue.Enqueue(track);
|
||||
_logger.LogInformation("Queued Track {0}", track.Title);
|
||||
|
||||
if (LoopType == LoopType.QUEUE)
|
||||
QueueLoopList.Add(track);
|
||||
}
|
||||
|
||||
public Task QueuePlaylistAsync(LavalinkPlaylist playlist)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
if (CurrentPlaylist == null)
|
||||
CurrentPlaylist = playlist;
|
||||
|
||||
_logger.LogInformation("Queued Playlist {0}", playlist.Name);
|
||||
foreach (LavalinkTrack track in playlist.Tracks)
|
||||
{
|
||||
Queue.Enqueue(track);
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (LoopType == LoopType.QUEUE)
|
||||
QueueLoopList.AddRange(playlist.Tracks);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public Task QueueTracksAsync(List<LavalinkTrack> tracks)
|
||||
{
|
||||
return Task.Run(() =>
|
||||
{
|
||||
CurrentPlaylist = null;
|
||||
_logger.LogInformation("Queued TrackList {0}", tracks.ToString());
|
||||
foreach (LavalinkTrack track in tracks)
|
||||
{
|
||||
Queue.Enqueue(track);
|
||||
}
|
||||
if (LoopType == LoopType.QUEUE)
|
||||
QueueLoopList.AddRange(tracks);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Queue.Clear();
|
||||
PlayedTracks.Clear();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (Queue.Count == 0) throw new InvalidOperationException("Queue was Empty");
|
||||
List<LavalinkTrack> tracks = Queue.ToList();
|
||||
tracks.RemoveAt(index);
|
||||
Queue = new Queue<LavalinkTrack>(tracks);
|
||||
|
||||
}
|
||||
|
||||
public MusicActionResponse NextTrack(bool ignoreLoop = false)
|
||||
{
|
||||
if (LastTrack != null)
|
||||
PlayedTracks = new Queue<LavalinkTrack>(PlayedTracks.Prepend(LastTrack));
|
||||
|
||||
switch (LoopType)
|
||||
{
|
||||
case LoopType.NONE:
|
||||
if (Queue.Count == 0) throw new InvalidOperationException("Queue was Empty");
|
||||
|
||||
LastTrack = Queue.Dequeue();
|
||||
|
||||
return new MusicActionResponse(LastTrack);
|
||||
case LoopType.TRACK:
|
||||
if (ignoreLoop)
|
||||
{
|
||||
LastTrack = Queue.Dequeue();
|
||||
return new MusicActionResponse(LastTrack);
|
||||
}
|
||||
|
||||
return new MusicActionResponse(LastTrack);
|
||||
case LoopType.QUEUE:
|
||||
if (!Queue.Any())
|
||||
{
|
||||
if (CurrentPlaylist != null)
|
||||
Queue = new Queue<LavalinkTrack>(CurrentPlaylist.Tracks);
|
||||
else
|
||||
Queue = new Queue<LavalinkTrack>(QueueLoopList);
|
||||
}
|
||||
|
||||
LastTrack = Queue.Dequeue();
|
||||
|
||||
return new MusicActionResponse(LastTrack);
|
||||
default:
|
||||
throw new NullReferenceException("LoopType was null");
|
||||
}
|
||||
}
|
||||
|
||||
public MusicActionResponse Rewind()
|
||||
{
|
||||
|
||||
if (!PlayedTracks.Any()) throw new InvalidOperationException("There are no songs that could be rewinded to yet.");
|
||||
|
||||
Queue = new Queue<LavalinkTrack>(Queue.Prepend(LastTrack));
|
||||
LastTrack = PlayedTracks.Dequeue();
|
||||
|
||||
return new MusicActionResponse(LastTrack);
|
||||
}
|
||||
|
||||
public Task ShuffleAsync()
|
||||
{
|
||||
if (Queue.Count == 0) throw new InvalidOperationException("Queue is Empty");
|
||||
|
||||
List<LavalinkTrack> tracks = new List<LavalinkTrack>(Queue);
|
||||
tracks.Shuffle();
|
||||
Queue = new Queue<LavalinkTrack>(tracks);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task SetLoopAsync(LoopType type)
|
||||
{
|
||||
LoopType = type;
|
||||
|
||||
if (type == LoopType.QUEUE)
|
||||
{
|
||||
QueueLoopList = new List<LavalinkTrack>(Queue);
|
||||
QueueLoopList.Add(LastTrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
TomatenMusicCore/Music/TrackProvider.cs
Normal file
79
TomatenMusicCore/Music/TrackProvider.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Lavalink4NET;
|
||||
using Lavalink4NET.Rest;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TomatenMusic.Music.Entitites;
|
||||
using TomatenMusic.Services;
|
||||
|
||||
namespace TomatenMusic.Music
|
||||
{
|
||||
public class TrackProvider
|
||||
{
|
||||
public ISpotifyService _spotifyService { get; set; }
|
||||
public IAudioService _audioService { get; set; }
|
||||
|
||||
public TrackProvider(ISpotifyService spotify, IAudioService audioService)
|
||||
{
|
||||
_audioService = audioService;
|
||||
_spotifyService = spotify;
|
||||
}
|
||||
|
||||
public async Task<MusicActionResponse> SearchAsync(string query, bool withSearchResults = false)
|
||||
{
|
||||
|
||||
Uri uri;
|
||||
TrackLoadResponsePayload loadResult;
|
||||
bool isSearch = true;
|
||||
|
||||
if (query.StartsWith("https://open.spotify.com"))
|
||||
{
|
||||
return await _spotifyService.ConvertURL(query);
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(query, UriKind.Absolute, out uri))
|
||||
{
|
||||
loadResult = await _audioService.LoadTracksAsync(uri.ToString());
|
||||
isSearch = false;
|
||||
}
|
||||
else
|
||||
loadResult = await _audioService.LoadTracksAsync(query, SearchMode.YouTube);
|
||||
|
||||
|
||||
if (loadResult.LoadType == TrackLoadType.LoadFailed) throw new ArgumentException("Track loading failed");
|
||||
|
||||
if (loadResult.LoadType == TrackLoadType.NoMatches) throw new FileNotFoundException("Query resulted in no Matches");
|
||||
|
||||
if (withSearchResults && loadResult.LoadType == TrackLoadType.SearchResult)
|
||||
{
|
||||
return new MusicActionResponse(tracks: await FullTrackContext.PopulateTracksAsync(loadResult.Tracks));
|
||||
}
|
||||
|
||||
if (loadResult.LoadType == TrackLoadType.PlaylistLoaded && !isSearch)
|
||||
return new MusicActionResponse(
|
||||
playlist: new YoutubePlaylist(loadResult.PlaylistInfo.Name, await FullTrackContext.PopulateTracksAsync(loadResult.Tracks), uri));
|
||||
else
|
||||
return new MusicActionResponse(await FullTrackContext.PopulateAsync(loadResult.Tracks.First()));
|
||||
|
||||
}
|
||||
|
||||
public async Task<MusicActionResponse> SearchAsync(Uri fileUri)
|
||||
{
|
||||
|
||||
var loadResult = await _audioService.GetTrackAsync(fileUri.ToString());
|
||||
loadResult.Context = new FullTrackContext
|
||||
{
|
||||
IsFile = true
|
||||
};
|
||||
|
||||
if (loadResult == null)
|
||||
throw new FileNotFoundException("The file was not found");
|
||||
|
||||
return new MusicActionResponse(loadResult);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user