Removed module projects because it can't work like that atm. Commented out package commands.
This commit is contained in:
239
NadekoBot.Core/Modules/Music/Services/MusicService.cs
Normal file
239
NadekoBot.Core/Modules/Music/Services/MusicService.cs
Normal file
@ -0,0 +1,239 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Discord;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Core.Services.Database.Models;
|
||||
using NLog;
|
||||
using System.IO;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
using NadekoBot.Common;
|
||||
using NadekoBot.Core.Services.Impl;
|
||||
using NadekoBot.Core.Services;
|
||||
using NadekoBot.Modules.Music.Common;
|
||||
using NadekoBot.Modules.Music.Common.Exceptions;
|
||||
using NadekoBot.Modules.Music.Common.SongResolver;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Services
|
||||
{
|
||||
public class MusicService : INService, IUnloadableService
|
||||
{
|
||||
public const string MusicDataPath = "data/musicdata";
|
||||
|
||||
private readonly IGoogleApiService _google;
|
||||
private readonly NadekoStrings _strings;
|
||||
private readonly ILocalization _localization;
|
||||
private readonly DbService _db;
|
||||
private readonly Logger _log;
|
||||
private readonly SoundCloudApiService _sc;
|
||||
private readonly IBotCredentials _creds;
|
||||
private readonly ConcurrentDictionary<ulong, float> _defaultVolumes;
|
||||
private readonly DiscordSocketClient _client;
|
||||
|
||||
public ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
|
||||
|
||||
public MusicService(DiscordSocketClient client, IGoogleApiService google,
|
||||
NadekoStrings strings, ILocalization localization, DbService db,
|
||||
SoundCloudApiService sc, IBotCredentials creds, NadekoBot bot)
|
||||
{
|
||||
_client = client;
|
||||
_google = google;
|
||||
_strings = strings;
|
||||
_localization = localization;
|
||||
_db = db;
|
||||
_sc = sc;
|
||||
_creds = creds;
|
||||
_log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
_client.LeftGuild += _client_LeftGuild;
|
||||
|
||||
try { Directory.Delete(MusicDataPath, true); } catch { }
|
||||
|
||||
_defaultVolumes = new ConcurrentDictionary<ulong, float>(
|
||||
bot.AllGuildConfigs
|
||||
.ToDictionary(x => x.GuildId, x => x.DefaultMusicVolume));
|
||||
|
||||
Directory.CreateDirectory(MusicDataPath);
|
||||
}
|
||||
|
||||
public Task Unload()
|
||||
{
|
||||
_client.LeftGuild -= _client_LeftGuild;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task _client_LeftGuild(SocketGuild arg)
|
||||
{
|
||||
var t = DestroyPlayer(arg.Id);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public float GetDefaultVolume(ulong guildId)
|
||||
{
|
||||
return _defaultVolumes.GetOrAdd(guildId, (id) =>
|
||||
{
|
||||
using (var uow = _db.UnitOfWork)
|
||||
{
|
||||
return uow.GuildConfigs.For(guildId, set => set).DefaultMusicVolume;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Task<MusicPlayer> GetOrCreatePlayer(ICommandContext context)
|
||||
{
|
||||
var gUsr = (IGuildUser)context.User;
|
||||
var txtCh = (ITextChannel)context.Channel;
|
||||
var vCh = gUsr.VoiceChannel;
|
||||
return GetOrCreatePlayer(context.Guild.Id, vCh, txtCh);
|
||||
}
|
||||
|
||||
public async Task<MusicPlayer> GetOrCreatePlayer(ulong guildId, IVoiceChannel voiceCh, ITextChannel textCh)
|
||||
{
|
||||
string GetText(string text, params object[] replacements) =>
|
||||
_strings.GetText(text, _localization.GetCultureInfo(textCh.Guild), "Music".ToLowerInvariant(), replacements);
|
||||
|
||||
_log.Info("Checks");
|
||||
if (voiceCh == null || voiceCh.Guild != textCh.Guild)
|
||||
{
|
||||
if (textCh != null)
|
||||
{
|
||||
await textCh.SendErrorAsync(GetText("must_be_in_voice")).ConfigureAwait(false);
|
||||
}
|
||||
throw new NotInVoiceChannelException();
|
||||
}
|
||||
_log.Info("Get or add");
|
||||
return MusicPlayers.GetOrAdd(guildId, _ =>
|
||||
{
|
||||
_log.Info("Getting default volume");
|
||||
var vol = GetDefaultVolume(guildId);
|
||||
_log.Info("Creating musicplayer instance");
|
||||
var mp = new MusicPlayer(this, _google, voiceCh, textCh, vol);
|
||||
|
||||
IUserMessage playingMessage = null;
|
||||
IUserMessage lastFinishedMessage = null;
|
||||
|
||||
_log.Info("Subscribing");
|
||||
mp.OnCompleted += async (s, song) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
lastFinishedMessage?.DeleteAfter(0);
|
||||
|
||||
try
|
||||
{
|
||||
lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("finished_song")).WithMusicIcon())
|
||||
.WithDescription(song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
mp.OnStarted += async (player, song) =>
|
||||
{
|
||||
//try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); }
|
||||
//catch
|
||||
//{
|
||||
// // ignored
|
||||
//}
|
||||
var sender = player;
|
||||
if (sender == null)
|
||||
return;
|
||||
try
|
||||
{
|
||||
playingMessage?.DeleteAfter(0);
|
||||
|
||||
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("playing_song", song.Index + 1)).WithMusicIcon())
|
||||
.WithDescription(song.Song.PrettyName)
|
||||
.WithFooter(ef => ef.WithText(mp.PrettyVolume + " | " + song.Song.PrettyInfo)))
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
mp.OnPauseChanged += async (player, paused) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
IUserMessage msg;
|
||||
if (paused)
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("paused")).ConfigureAwait(false);
|
||||
else
|
||||
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("resumed")).ConfigureAwait(false);
|
||||
|
||||
msg?.DeleteAfter(10);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
};
|
||||
_log.Info("Done creating");
|
||||
return mp;
|
||||
});
|
||||
}
|
||||
|
||||
public MusicPlayer GetPlayerOrDefault(ulong guildId)
|
||||
{
|
||||
if (MusicPlayers.TryGetValue(guildId, out var mp))
|
||||
return mp;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task TryQueueRelatedSongAsync(SongInfo song, ITextChannel txtCh, IVoiceChannel vch)
|
||||
{
|
||||
var related = (await _google.GetRelatedVideosAsync(song.VideoId, 4)).ToArray();
|
||||
if (!related.Any())
|
||||
return;
|
||||
|
||||
var si = await ResolveSong(related[new NadekoRandom().Next(related.Length)], _client.CurrentUser.ToString(), MusicType.YouTube);
|
||||
if (si == null)
|
||||
throw new SongNotFoundException();
|
||||
var mp = await GetOrCreatePlayer(txtCh.GuildId, vch, txtCh);
|
||||
mp.Enqueue(si);
|
||||
}
|
||||
|
||||
public async Task<SongInfo> ResolveSong(string query, string queuerName, MusicType? musicType = null)
|
||||
{
|
||||
query.ThrowIfNull(nameof(query));
|
||||
|
||||
ISongResolverFactory resolverFactory = new SongResolverFactory(_sc);
|
||||
var strategy = await resolverFactory.GetResolveStrategy(query, musicType);
|
||||
var sinfo = await strategy.ResolveSong(query);
|
||||
|
||||
if (sinfo == null)
|
||||
return null;
|
||||
|
||||
sinfo.QueuerName = queuerName;
|
||||
|
||||
return sinfo;
|
||||
}
|
||||
|
||||
public async Task DestroyAllPlayers()
|
||||
{
|
||||
foreach (var key in MusicPlayers.Keys)
|
||||
{
|
||||
await DestroyPlayer(key);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task DestroyPlayer(ulong id)
|
||||
{
|
||||
if (MusicPlayers.TryRemove(id, out var mp))
|
||||
await mp.Destroy();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user