Small refactor

This commit is contained in:
Master Kwoth 2017-07-11 03:16:56 +02:00
parent ec94459722
commit 8f90410e2d
14 changed files with 539 additions and 427 deletions

View File

@ -15,6 +15,8 @@ using System.Collections.Concurrent;
using System.IO;
using System.Net.Http;
using Newtonsoft.Json.Linq;
using NadekoBot.Services.Music.Extensions;
using NadekoBot.Services.Impl;
namespace NadekoBot.Modules.Music
{
@ -122,12 +124,15 @@ namespace NadekoBot.Modules.Music
{
try
{
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon())
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
.WithThumbnailUrl(songInfo.Thumbnail)
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider)))
.ConfigureAwait(false);
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider));
if (Uri.IsWellFormedUriString(songInfo.Thumbnail, UriKind.Absolute))
embed.WithThumbnailUrl(songInfo.Thumbnail);
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
if (mp.Stopped)
{
(await ReplyErrorLocalized("queue_stopped", Format.Code(Prefix + "play")).ConfigureAwait(false)).DeleteAfter(10);
@ -375,6 +380,11 @@ namespace NadekoBot.Modules.Music
[Priority(0)]
public async Task SongRemove(int index)
{
if (index < 1)
{
await ReplyErrorLocalized("removed_song_error").ConfigureAwait(false);
return;
}
var mp = await _music.GetOrCreatePlayer(Context);
try
{
@ -399,7 +409,9 @@ namespace NadekoBot.Modules.Music
[Priority(1)]
public async Task SongRemove(All all)
{
var mp = await _music.GetOrCreatePlayer(Context);
var mp = _music.GetPlayerOrDefault(Context.Guild.Id);
if (mp == null)
return;
mp.Stop(true);
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
}
@ -586,7 +598,9 @@ namespace NadekoBot.Modules.Music
try
{
await Task.Yield();
await InternalQueue(mp, await _music.SongInfoFromSVideo(svideo, Context.User.ToString()), true);
var sinfo = await svideo.GetSongInfo();
sinfo.QueuerName = Context.User.ToString();
await InternalQueue(mp, sinfo, true);
}
catch (Exception ex)
{

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music
namespace NadekoBot.Services.Impl
{
public class SoundCloudApiService
{

View File

@ -0,0 +1,22 @@
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Impl;
using System;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.Extensions
{
public static class Extensions
{
public static Task<SongInfo> GetSongInfo(this SoundCloudVideo svideo) =>
Task.FromResult(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = () => svideo.StreamLink(),
ProviderType = MusicType.Soundcloud,
Query = svideo.TrackLink,
Thumbnail = svideo.artwork_url,
TotalTime = TimeSpan.FromMilliseconds(svideo.Duration)
});
}
}

View File

@ -7,15 +7,11 @@ using NadekoBot.Extensions;
using NadekoBot.Services.Database.Models;
using NLog;
using System.IO;
using VideoLibrary;
using System.Collections.Generic;
using Discord.Commands;
using Discord.WebSocket;
using System.Text.RegularExpressions;
using System.Net.Http;
using NadekoBot.Services.Music.SongResolver;
using NadekoBot.Services.Impl;
using System.Globalization;
using System.Threading;
namespace NadekoBot.Services.Music
{
@ -47,7 +43,6 @@ namespace NadekoBot.Services.Music
_sc = sc;
_creds = creds;
_log = LogManager.GetCurrentClassLogger();
_yt = YouTube.Default;
try { Directory.Delete(MusicDataPath, true); } catch { }
@ -199,91 +194,18 @@ namespace NadekoBot.Services.Music
{
query.ThrowIfNull(nameof(query));
SongInfo sinfo = null;
switch (musicType)
{
case MusicType.YouTube:
sinfo = await ResolveYoutubeSong(query, queuerName);
break;
case MusicType.Radio:
try { sinfo = ResolveRadioSong(IsRadioLink(query) ? await HandleStreamContainers(query) : query, queuerName); } catch { };
break;
case MusicType.Local:
sinfo = ResolveLocalSong(query, queuerName);
break;
case MusicType.Soundcloud:
sinfo = await ResolveSoundCloudSong(query, queuerName);
break;
case null:
if (_sc.IsSoundCloudLink(query))
sinfo = await ResolveSoundCloudSong(query, queuerName);
else if (IsRadioLink(query))
sinfo = ResolveRadioSong(await HandleStreamContainers(query), queuerName);
else
try
{
sinfo = await ResolveYoutubeSong(query, queuerName);
}
catch
{
sinfo = null;
}
break;
}
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<SongInfo> ResolveSoundCloudSong(string query, string queuerName)
{
var svideo = !_sc.IsSoundCloudLink(query) ?
await _sc.GetVideoByQueryAsync(query).ConfigureAwait(false) :
await _sc.ResolveVideoAsync(query).ConfigureAwait(false);
if (svideo == null)
return null;
return await SongInfoFromSVideo(svideo, queuerName);
}
public Task<SongInfo> SongInfoFromSVideo(SoundCloudVideo svideo, string queuerName) =>
Task.FromResult(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = () => svideo.StreamLink(),
ProviderType = MusicType.Soundcloud,
Query = svideo.TrackLink,
AlbumArt = svideo.artwork_url,
QueuerName = queuerName,
TotalTime = TimeSpan.FromMilliseconds(svideo.Duration)
});
public SongInfo ResolveLocalSong(string query, string queuerName)
{
return new SongInfo
{
Uri = () => Task.FromResult("\"" + Path.GetFullPath(query) + "\""),
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = MusicType.Local,
Query = query,
QueuerName = queuerName
};
}
public SongInfo ResolveRadioSong(string query, string queuerName)
{
return new SongInfo
{
Uri = () => Task.FromResult(query),
Title = query,
Provider = "Radio Stream",
ProviderType = MusicType.Radio,
Query = query,
QueuerName = queuerName
};
}
public async Task DestroyAllPlayers()
{
foreach (var key in MusicPlayers.Keys)
@ -292,214 +214,66 @@ namespace NadekoBot.Services.Music
}
}
public Task<SongInfo> ResolveYoutubeSong(string query, string queuerName)
{
_log.Info("Getting video");
//var (link, video) = await GetYoutubeVideo(query);
//if (video == null) // do something with this error
//{
// _log.Info("Could not load any video elements based on the query.");
// return null;
//}
////var m = Regex.Match(query, @"\?t=(?<t>\d*)");
////int gotoTime = 0;
////if (m.Captures.Count > 0)
//// int.TryParse(m.Groups["t"].ToString(), out gotoTime);
//_log.Info("Creating song info");
//var song = new SongInfo
//{
// Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
// Provider = "YouTube",
// Uri = async () => {
// var vid = await GetYoutubeVideo(query);
// if (vid.Item2 == null)
// throw new HttpRequestException();
// return await vid.Item2.GetUriAsync();
// },
// Query = link,
// ProviderType = MusicType.YouTube,
// QueuerName = queuerName
//};
return GetYoutubeVideo(query, queuerName);
}
private async Task<SongInfo> GetYoutubeVideo(string query, string queuerName)
{
_log.Info("Getting link");
string[] data;
try
{
using (var ytdl = new YtdlOperation())
{
data = (await ytdl.GetDataAsync(query)).Split('\n');
}
if (data.Length < 6)
{
_log.Info("No song found. Data less than 6");
return null;
}
TimeSpan time;
if (!TimeSpan.TryParseExact(data[4], new[] { "ss", "m\\:ss", "mm\\:ss", "h\\:mm\\:ss", "hh\\:mm\\:ss", "hhh\\:mm\\:ss" }, CultureInfo.InvariantCulture, out time))
time = TimeSpan.FromHours(24);
return new SongInfo()
{
Title = data[0],
VideoId = data[1],
Uri = async () => {
using (var ytdl = new YtdlOperation())
{
data = (await ytdl.GetDataAsync(query)).Split('\n');
}
if (data.Length < 6)
{
_log.Info("No song found. Data less than 6");
return null;
}
return data[2];
},
AlbumArt = data[3],
TotalTime = time,
QueuerName = queuerName,
Provider = "YouTube",
ProviderType = MusicType.YouTube,
Query = "https://youtube.com/watch?v=" + data[1],
};
}
catch (Exception ex)
{
_log.Warn(ex);
return null;
}
//if (string.IsNullOrWhiteSpace(link))
//{
// _log.Info("No song found.");
// return (null, null);
//}
//_log.Info("Getting all videos");
//var allVideos = await Task.Run(async () => { try { return await _yt.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
//var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
//var video = videos
// .Where(v => v.AudioBitrate < 256)
// .OrderByDescending(v => v.AudioBitrate)
// .FirstOrDefault();
//return (link, video);
}
private bool IsRadioLink(string query) =>
(query.StartsWith("http") ||
query.StartsWith("ww"))
&&
(query.Contains(".pls") ||
query.Contains(".m3u") ||
query.Contains(".asx") ||
query.Contains(".xspf"));
public async Task DestroyPlayer(ulong id)
{
if (MusicPlayers.TryRemove(id, out var mp))
await mp.Destroy();
}
private readonly Regex plsRegex = new Regex("File1=(?<url>.*?)\\n", RegexOptions.Compiled);
private readonly Regex m3uRegex = new Regex("(?<url>^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline);
private readonly Regex asxRegex = new Regex("<ref href=\"(?<url>.*?)\"", RegexOptions.Compiled);
private readonly Regex xspfRegex = new Regex("<location>(?<url>.*?)</location>", RegexOptions.Compiled);
private readonly YouTube _yt;
private readonly Timer _t;
private async Task<string> HandleStreamContainers(string query)
{
string file = null;
try
{
using (var http = new HttpClient())
{
file = await http.GetStringAsync(query).ConfigureAwait(false);
}
}
catch
{
return query;
}
if (query.Contains(".pls"))
{
//File1=http://armitunes.com:8000/
//Regex.Match(query)
try
{
var m = plsRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .pls:\n{file}");
return null;
}
}
if (query.Contains(".m3u"))
{
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
try
{
var m = m3uRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .m3u:\n{file}");
return null;
}
}
if (query.Contains(".asx"))
{
//<ref href="http://armitunes.com:8000"/>
try
{
var m = asxRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .asx:\n{file}");
return null;
}
}
if (query.Contains(".xspf"))
{
/*
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track><location>file:///mp3s/song_1.mp3</location></track>
*/
try
{
var m = xspfRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .xspf:\n{file}");
return null;
}
}
//public Task<SongInfo> ResolveYoutubeSong(string query, string queuerName)
//{
// _log.Info("Getting video");
// //var (link, video) = await GetYoutubeVideo(query);
return query;
}
// //if (video == null) // do something with this error
// //{
// // _log.Info("Could not load any video elements based on the query.");
// // return null;
// //}
// ////var m = Regex.Match(query, @"\?t=(?<t>\d*)");
// ////int gotoTime = 0;
// ////if (m.Captures.Count > 0)
// //// int.TryParse(m.Groups["t"].ToString(), out gotoTime);
// //_log.Info("Creating song info");
// //var song = new SongInfo
// //{
// // Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
// // Provider = "YouTube",
// // Uri = async () => {
// // var vid = await GetYoutubeVideo(query);
// // if (vid.Item2 == null)
// // throw new HttpRequestException();
// // return await vid.Item2.GetUriAsync();
// // },
// // Query = link,
// // ProviderType = MusicType.YouTube,
// // QueuerName = queuerName
// //};
// return GetYoutubeVideo(query, queuerName);
//}
//private async Task<SongInfo> GetYoutubeVideo(string query, string queuerName)
//{
// //if (string.IsNullOrWhiteSpace(link))
// //{
// // _log.Info("No song found.");
// // return (null, null);
// //}
// //_log.Info("Getting all videos");
// //var allVideos = await Task.Run(async () => { try { return await _yt.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
// //var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
// //var video = videos
// // .Where(v => v.AudioBitrate < 256)
// // .OrderByDescending(v => v.AudioBitrate)
// // .FirstOrDefault();
// //return (link, video);
//}
}
}

View File

@ -0,0 +1,111 @@
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//namespace NadekoBot.Services.Music
//{
// public class OldSongResolver
// {
// // public async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
// // {
// // if (string.IsNullOrWhiteSpace(query))
// // throw new ArgumentNullException(nameof(query));
// // if (musicType != MusicType.Local && IsRadioLink(query))
// // {
// // musicType = MusicType.Radio;
// // query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
// // }
// // try
// // {
// // switch (musicType)
// // {
// // case MusicType.Local:
// // return new Song(new SongInfo
// // {
// // Uri = "\"" + Path.GetFullPath(query) + "\"",
// // Title = Path.GetFileNameWithoutExtension(query),
// // Provider = "Local File",
// // ProviderType = musicType,
// // Query = query,
// // });
// // case MusicType.Radio:
// // return new Song(new SongInfo
// // {
// // Uri = query,
// // Title = $"{query}",
// // Provider = "Radio Stream",
// // ProviderType = musicType,
// // Query = query
// // })
// // { TotalTime = TimeSpan.MaxValue };
// // }
// // if (_sc.IsSoundCloudLink(query))
// // {
// // var svideo = await _sc.ResolveVideoAsync(query).ConfigureAwait(false);
// // return new Song(new SongInfo
// // {
// // Title = svideo.FullName,
// // Provider = "SoundCloud",
// // Uri = await svideo.StreamLink(),
// // ProviderType = musicType,
// // Query = svideo.TrackLink,
// // AlbumArt = svideo.artwork_url,
// // })
// // { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
// // }
// // if (musicType == MusicType.Soundcloud)
// // {
// // var svideo = await _sc.GetVideoByQueryAsync(query).ConfigureAwait(false);
// // return new Song(new SongInfo
// // {
// // Title = svideo.FullName,
// // Provider = "SoundCloud",
// // Uri = await svideo.StreamLink(),
// // ProviderType = MusicType.Soundcloud,
// // Query = svideo.TrackLink,
// // AlbumArt = svideo.artwork_url,
// // })
// // { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
// // }
// // var link = (await _google.GetVideoLinksByKeywordAsync(query).ConfigureAwait(false)).FirstOrDefault();
// // if (string.IsNullOrWhiteSpace(link))
// // throw new OperationCanceledException("Not a valid youtube query.");
// // var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
// // var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
// // var video = videos
// // .Where(v => v.AudioBitrate < 256)
// // .OrderByDescending(v => v.AudioBitrate)
// // .FirstOrDefault();
// // if (video == null) // do something with this error
// // throw new Exception("Could not load any video elements based on the query.");
// // var m = Regex.Match(query, @"\?t=(?<t>\d*)");
// // int gotoTime = 0;
// // if (m.Captures.Count > 0)
// // int.TryParse(m.Groups["t"].ToString(), out gotoTime);
// // var song = new Song(new SongInfo
// // {
// // Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
// // Provider = "YouTube",
// // Uri = await video.GetUriAsync().ConfigureAwait(false),
// // Query = link,
// // ProviderType = musicType,
// // });
// // song.SkipTo = gotoTime;
// // return song;
// // }
// // catch (Exception ex)
// // {
// // _log.Warn($"Failed resolving the link.{ex.Message}");
// // _log.Warn(ex);
// // return null;
// // }
// // }
// }
//}

View File

@ -15,7 +15,7 @@ namespace NadekoBot.Services.Music
public string Query { get; set; }
public string Title { get; set; }
public Func<Task<string>> Uri { get; set; }
public string AlbumArt { get; set; }
public string Thumbnail { get; set; }
public string QueuerName { get; set; }
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
@ -75,24 +75,5 @@ namespace NadekoBot.Services.Music
}
private readonly Regex videoIdRegex = new Regex("<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+", RegexOptions.Compiled);
public string Thumbnail
{
get
{
switch (ProviderType)
{
case MusicType.Radio:
return "https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links
case MusicType.YouTube:
return $"https://img.youtube.com/vi/{ VideoId }/0.jpg";
case MusicType.Local:
return "https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links
case MusicType.Soundcloud:
return AlbumArt;
default:
return "";
}
}
}
}
}

View File

@ -1,111 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music
{
public class SongResolver
{
// public async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
// {
// if (string.IsNullOrWhiteSpace(query))
// throw new ArgumentNullException(nameof(query));
// if (musicType != MusicType.Local && IsRadioLink(query))
// {
// musicType = MusicType.Radio;
// query = await HandleStreamContainers(query).ConfigureAwait(false) ?? query;
// }
// try
// {
// switch (musicType)
// {
// case MusicType.Local:
// return new Song(new SongInfo
// {
// Uri = "\"" + Path.GetFullPath(query) + "\"",
// Title = Path.GetFileNameWithoutExtension(query),
// Provider = "Local File",
// ProviderType = musicType,
// Query = query,
// });
// case MusicType.Radio:
// return new Song(new SongInfo
// {
// Uri = query,
// Title = $"{query}",
// Provider = "Radio Stream",
// ProviderType = musicType,
// Query = query
// })
// { TotalTime = TimeSpan.MaxValue };
// }
// if (_sc.IsSoundCloudLink(query))
// {
// var svideo = await _sc.ResolveVideoAsync(query).ConfigureAwait(false);
// return new Song(new SongInfo
// {
// Title = svideo.FullName,
// Provider = "SoundCloud",
// Uri = await svideo.StreamLink(),
// ProviderType = musicType,
// Query = svideo.TrackLink,
// AlbumArt = svideo.artwork_url,
// })
// { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
// }
// if (musicType == MusicType.Soundcloud)
// {
// var svideo = await _sc.GetVideoByQueryAsync(query).ConfigureAwait(false);
// return new Song(new SongInfo
// {
// Title = svideo.FullName,
// Provider = "SoundCloud",
// Uri = await svideo.StreamLink(),
// ProviderType = MusicType.Soundcloud,
// Query = svideo.TrackLink,
// AlbumArt = svideo.artwork_url,
// })
// { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) };
// }
// var link = (await _google.GetVideoLinksByKeywordAsync(query).ConfigureAwait(false)).FirstOrDefault();
// if (string.IsNullOrWhiteSpace(link))
// throw new OperationCanceledException("Not a valid youtube query.");
// var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty<YouTubeVideo>(); } }).ConfigureAwait(false);
// var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio);
// var video = videos
// .Where(v => v.AudioBitrate < 256)
// .OrderByDescending(v => v.AudioBitrate)
// .FirstOrDefault();
// if (video == null) // do something with this error
// throw new Exception("Could not load any video elements based on the query.");
// var m = Regex.Match(query, @"\?t=(?<t>\d*)");
// int gotoTime = 0;
// if (m.Captures.Count > 0)
// int.TryParse(m.Groups["t"].ToString(), out gotoTime);
// var song = new Song(new SongInfo
// {
// Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
// Provider = "YouTube",
// Uri = await video.GetUriAsync().ConfigureAwait(false),
// Query = link,
// ProviderType = musicType,
// });
// song.SkipTo = gotoTime;
// return song;
// }
// catch (Exception ex)
// {
// _log.Warn($"Failed resolving the link.{ex.Message}");
// _log.Warn(ex);
// return null;
// }
// }
}
}

View File

@ -0,0 +1,15 @@
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Music.SongResolver.Strategies;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver
{
public interface ISongResolverFactory
{
Task<IResolveStrategy> GetResolveStrategy(string query, MusicType? musicType);
}
}

View File

@ -0,0 +1,41 @@
using System.Threading.Tasks;
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Music.SongResolver.Strategies;
using NadekoBot.Services.Impl;
namespace NadekoBot.Services.Music.SongResolver
{
public class SongResolverFactory : ISongResolverFactory
{
private readonly SoundCloudApiService _sc;
public SongResolverFactory(SoundCloudApiService sc)
{
_sc = sc;
}
public async Task<IResolveStrategy> GetResolveStrategy(string query, MusicType? musicType)
{
await Task.Yield(); //for async warning
switch (musicType)
{
case MusicType.YouTube:
return new YoutubeResolveStrategy();
case MusicType.Radio:
return new RadioResolveStrategy();
case MusicType.Local:
return new LocalSongResolveStrategy();
case MusicType.Soundcloud:
return new SoundcloudResolveStrategy(_sc);
default:
if (_sc.IsSoundCloudLink(query))
return new SoundcloudResolveStrategy(_sc);
else if (RadioResolveStrategy.IsRadioLink(query))
return new RadioResolveStrategy();
// maybe add a check for local files in the future
else
return new YoutubeResolveStrategy();
}
}
}
}

View File

@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver.Strategies
{
public interface IResolveStrategy
{
Task<SongInfo> ResolveSong(string query);
}
}

View File

@ -0,0 +1,22 @@
using NadekoBot.Services.Database.Models;
using System.IO;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver.Strategies
{
public class LocalSongResolveStrategy : IResolveStrategy
{
public Task<SongInfo> ResolveSong(string query)
{
return Task.FromResult(new SongInfo
{
Uri = () => Task.FromResult("\"" + Path.GetFullPath(query) + "\""),
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = MusicType.Local,
Query = query,
Thumbnail = "https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png",
});
}
}
}

View File

@ -0,0 +1,138 @@
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver.Strategies
{
public class RadioResolveStrategy : IResolveStrategy
{
private readonly Regex plsRegex = new Regex("File1=(?<url>.*?)\\n", RegexOptions.Compiled);
private readonly Regex m3uRegex = new Regex("(?<url>^[^#].*)", RegexOptions.Compiled | RegexOptions.Multiline);
private readonly Regex asxRegex = new Regex("<ref href=\"(?<url>.*?)\"", RegexOptions.Compiled);
private readonly Regex xspfRegex = new Regex("<location>(?<url>.*?)</location>", RegexOptions.Compiled);
private readonly Logger _log;
public RadioResolveStrategy()
{
_log = LogManager.GetCurrentClassLogger();
}
public async Task<SongInfo> ResolveSong(string query)
{
if (IsRadioLink(query))
query = await HandleStreamContainers(query);
return new SongInfo
{
Uri = () => Task.FromResult(query),
Title = query,
Provider = "Radio Stream",
ProviderType = MusicType.Radio,
Query = query,
TotalTime = TimeSpan.MaxValue,
Thumbnail = "https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png",
};
}
public static bool IsRadioLink(string query) =>
(query.StartsWith("http") ||
query.StartsWith("ww"))
&&
(query.Contains(".pls") ||
query.Contains(".m3u") ||
query.Contains(".asx") ||
query.Contains(".xspf"));
private async Task<string> HandleStreamContainers(string query)
{
string file = null;
try
{
using (var http = new HttpClient())
{
file = await http.GetStringAsync(query).ConfigureAwait(false);
}
}
catch
{
return query;
}
if (query.Contains(".pls"))
{
//File1=http://armitunes.com:8000/
//Regex.Match(query)
try
{
var m = plsRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .pls:\n{file}");
return null;
}
}
if (query.Contains(".m3u"))
{
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
try
{
var m = m3uRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .m3u:\n{file}");
return null;
}
}
if (query.Contains(".asx"))
{
//<ref href="http://armitunes.com:8000"/>
try
{
var m = asxRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .asx:\n{file}");
return null;
}
}
if (query.Contains(".xspf"))
{
/*
<?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track><location>file:///mp3s/song_1.mp3</location></track>
*/
try
{
var m = xspfRegex.Match(file);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
}
catch
{
_log.Warn($"Failed reading .xspf:\n{file}");
return null;
}
}
return query;
}
}
}

View File

@ -0,0 +1,27 @@
using NadekoBot.Services.Impl;
using NadekoBot.Services.Music.Extensions;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver.Strategies
{
public class SoundcloudResolveStrategy : IResolveStrategy
{
private readonly SoundCloudApiService _sc;
public SoundcloudResolveStrategy(SoundCloudApiService sc)
{
_sc = sc;
}
public async Task<SongInfo> ResolveSong(string query)
{
var svideo = !_sc.IsSoundCloudLink(query) ?
await _sc.GetVideoByQueryAsync(query).ConfigureAwait(false) :
await _sc.ResolveVideoAsync(query).ConfigureAwait(false);
if (svideo == null)
return null;
return await svideo.GetSongInfo();
}
}
}

View File

@ -0,0 +1,69 @@
using NadekoBot.Services.Database.Models;
using NadekoBot.Services.Impl;
using NLog;
using System;
using System.Globalization;
using System.Threading.Tasks;
namespace NadekoBot.Services.Music.SongResolver.Strategies
{
public class YoutubeResolveStrategy : IResolveStrategy
{
private readonly Logger _log;
public YoutubeResolveStrategy()
{
_log = LogManager.GetCurrentClassLogger();
}
public async Task<SongInfo> ResolveSong(string query)
{
_log.Info("Getting link");
string[] data;
try
{
using (var ytdl = new YtdlOperation())
{
data = (await ytdl.GetDataAsync(query)).Split('\n');
}
if (data.Length < 6)
{
_log.Info("No song found. Data less than 6");
return null;
}
TimeSpan time;
if (!TimeSpan.TryParseExact(data[4], new[] { "ss", "m\\:ss", "mm\\:ss", "h\\:mm\\:ss", "hh\\:mm\\:ss", "hhh\\:mm\\:ss" }, CultureInfo.InvariantCulture, out time))
time = TimeSpan.FromHours(24);
return new SongInfo()
{
Title = data[0],
VideoId = data[1],
Uri = async () =>
{
using (var ytdl = new YtdlOperation())
{
data = (await ytdl.GetDataAsync(query)).Split('\n');
}
if (data.Length < 6)
{
_log.Info("No song found. Data less than 6");
return null;
}
return data[2];
},
Thumbnail = data[3],
TotalTime = time,
Provider = "YouTube",
ProviderType = MusicType.YouTube,
Query = "https://youtube.com/watch?v=" + data[1],
};
}
catch (Exception ex)
{
_log.Warn(ex);
return null;
}
}
}
}