Small refactor
This commit is contained in:
parent
ec94459722
commit
8f90410e2d
@ -15,6 +15,8 @@ using System.Collections.Concurrent;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NadekoBot.Services.Music.Extensions;
|
||||||
|
using NadekoBot.Services.Impl;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Music
|
namespace NadekoBot.Modules.Music
|
||||||
{
|
{
|
||||||
@ -122,12 +124,15 @@ namespace NadekoBot.Modules.Music
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
|
var embed = new EmbedBuilder().WithOkColor()
|
||||||
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon())
|
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (index)).WithMusicIcon())
|
||||||
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
|
.WithDescription($"{songInfo.PrettyName}\n{GetText("queue")} ")
|
||||||
.WithThumbnailUrl(songInfo.Thumbnail)
|
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider));
|
||||||
.WithFooter(ef => ef.WithText(songInfo.PrettyProvider)))
|
|
||||||
.ConfigureAwait(false);
|
if (Uri.IsWellFormedUriString(songInfo.Thumbnail, UriKind.Absolute))
|
||||||
|
embed.WithThumbnailUrl(songInfo.Thumbnail);
|
||||||
|
|
||||||
|
var queuedMessage = await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
|
||||||
if (mp.Stopped)
|
if (mp.Stopped)
|
||||||
{
|
{
|
||||||
(await ReplyErrorLocalized("queue_stopped", Format.Code(Prefix + "play")).ConfigureAwait(false)).DeleteAfter(10);
|
(await ReplyErrorLocalized("queue_stopped", Format.Code(Prefix + "play")).ConfigureAwait(false)).DeleteAfter(10);
|
||||||
@ -375,6 +380,11 @@ namespace NadekoBot.Modules.Music
|
|||||||
[Priority(0)]
|
[Priority(0)]
|
||||||
public async Task SongRemove(int index)
|
public async Task SongRemove(int index)
|
||||||
{
|
{
|
||||||
|
if (index < 1)
|
||||||
|
{
|
||||||
|
await ReplyErrorLocalized("removed_song_error").ConfigureAwait(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var mp = await _music.GetOrCreatePlayer(Context);
|
var mp = await _music.GetOrCreatePlayer(Context);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -399,7 +409,9 @@ namespace NadekoBot.Modules.Music
|
|||||||
[Priority(1)]
|
[Priority(1)]
|
||||||
public async Task SongRemove(All all)
|
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);
|
mp.Stop(true);
|
||||||
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@ -586,7 +598,9 @@ namespace NadekoBot.Modules.Music
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Impl
|
||||||
{
|
{
|
||||||
public class SoundCloudApiService
|
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 NadekoBot.Services.Database.Models;
|
||||||
using NLog;
|
using NLog;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using VideoLibrary;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
using System.Text.RegularExpressions;
|
using NadekoBot.Services.Music.SongResolver;
|
||||||
using System.Net.Http;
|
|
||||||
using NadekoBot.Services.Impl;
|
using NadekoBot.Services.Impl;
|
||||||
using System.Globalization;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace NadekoBot.Services.Music
|
namespace NadekoBot.Services.Music
|
||||||
{
|
{
|
||||||
@ -47,7 +43,6 @@ namespace NadekoBot.Services.Music
|
|||||||
_sc = sc;
|
_sc = sc;
|
||||||
_creds = creds;
|
_creds = creds;
|
||||||
_log = LogManager.GetCurrentClassLogger();
|
_log = LogManager.GetCurrentClassLogger();
|
||||||
_yt = YouTube.Default;
|
|
||||||
|
|
||||||
try { Directory.Delete(MusicDataPath, true); } catch { }
|
try { Directory.Delete(MusicDataPath, true); } catch { }
|
||||||
|
|
||||||
@ -199,91 +194,18 @@ namespace NadekoBot.Services.Music
|
|||||||
{
|
{
|
||||||
query.ThrowIfNull(nameof(query));
|
query.ThrowIfNull(nameof(query));
|
||||||
|
|
||||||
SongInfo sinfo = null;
|
ISongResolverFactory resolverFactory = new SongResolverFactory(_sc);
|
||||||
switch (musicType)
|
var strategy = await resolverFactory.GetResolveStrategy(query, musicType);
|
||||||
{
|
var sinfo = await strategy.ResolveSong(query);
|
||||||
case MusicType.YouTube:
|
|
||||||
sinfo = await ResolveYoutubeSong(query, queuerName);
|
if (sinfo == null)
|
||||||
break;
|
return null;
|
||||||
case MusicType.Radio:
|
|
||||||
try { sinfo = ResolveRadioSong(IsRadioLink(query) ? await HandleStreamContainers(query) : query, queuerName); } catch { };
|
sinfo.QueuerName = queuerName;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sinfo;
|
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()
|
public async Task DestroyAllPlayers()
|
||||||
{
|
{
|
||||||
foreach (var key in MusicPlayers.Keys)
|
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)
|
public async Task DestroyPlayer(ulong id)
|
||||||
{
|
{
|
||||||
if (MusicPlayers.TryRemove(id, out var mp))
|
if (MusicPlayers.TryRemove(id, out var mp))
|
||||||
await mp.Destroy();
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
//public Task<SongInfo> ResolveYoutubeSong(string query, string queuerName)
|
||||||
if (query.Contains(".asx"))
|
//{
|
||||||
{
|
// _log.Info("Getting video");
|
||||||
//<ref href="http://armitunes.com:8000"/>
|
// //var (link, video) = await GetYoutubeVideo(query);
|
||||||
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;
|
// //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 Query { get; set; }
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public Func<Task<string>> Uri { 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 string QueuerName { get; set; }
|
||||||
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
|
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);
|
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