Small refactor
This commit is contained in:
parent
ec94459722
commit
8f90410e2d
@ -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()
|
||||
.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);
|
||||
var embed = new EmbedBuilder().WithOkColor()
|
||||
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon())
|
||||
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
|
||||
.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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
22
src/NadekoBot/Services/Music/Extensions/Extensions.cs
Normal file
22
src/NadekoBot/Services/Music/Extensions/Extensions.cs
Normal 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)
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
//}
|
||||
}
|
||||
}
|
111
src/NadekoBot/Services/Music/OldSongResolver.cs
Normal file
111
src/NadekoBot/Services/Music/OldSongResolver.cs
Normal 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;
|
||||
// // }
|
||||
// // }
|
||||
// }
|
||||
//}
|
@ -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 "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Services.Music.SongResolver.Strategies
|
||||
{
|
||||
public interface IResolveStrategy
|
||||
{
|
||||
Task<SongInfo> ResolveSong(string query);
|
||||
}
|
||||
}
|
@ -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",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user