small refactor
This commit is contained in:
parent
52923e2680
commit
450ca709b4
120
NadekoBot/Classes/Music/PoopyBuffer.cs
Normal file
120
NadekoBot/Classes/Music/PoopyBuffer.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace NadekoBot.Classes.Music
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 💩
|
||||||
|
/// </summary>
|
||||||
|
public class PoopyBuffer
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly byte[] ringBuffer;
|
||||||
|
|
||||||
|
public int WritePosition { get; private set; } = 0;
|
||||||
|
public int ReadPosition { get; private set; } = 0;
|
||||||
|
|
||||||
|
public int ContentLength => (WritePosition >= ReadPosition ?
|
||||||
|
WritePosition - ReadPosition :
|
||||||
|
(BufferSize - ReadPosition) + WritePosition);
|
||||||
|
|
||||||
|
public int BufferSize { get; }
|
||||||
|
|
||||||
|
private readonly object readWriteLock = new object();
|
||||||
|
|
||||||
|
public PoopyBuffer(int size)
|
||||||
|
{
|
||||||
|
if (size <= 0)
|
||||||
|
throw new ArgumentException();
|
||||||
|
BufferSize = size;
|
||||||
|
ringBuffer = new byte[size];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Read(byte[] buffer, int count)
|
||||||
|
{
|
||||||
|
if (buffer.Length < count)
|
||||||
|
throw new ArgumentException();
|
||||||
|
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
|
||||||
|
lock (readWriteLock)
|
||||||
|
{
|
||||||
|
//read as much as you can if you're reading too much
|
||||||
|
if (count > ContentLength)
|
||||||
|
count = ContentLength;
|
||||||
|
//if nothing to read, return 0
|
||||||
|
if (WritePosition == ReadPosition)
|
||||||
|
return 0;
|
||||||
|
// if buffer is in the "normal" state, just read
|
||||||
|
if (WritePosition > ReadPosition)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
|
||||||
|
ReadPosition += count;
|
||||||
|
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
//else ReadPos <Writepos
|
||||||
|
// buffer is in its inverted state
|
||||||
|
// A: if i can read as much as possible without hitting the buffer.length, read that
|
||||||
|
|
||||||
|
if (count + ReadPosition <= BufferSize)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
|
||||||
|
ReadPosition += count;
|
||||||
|
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
// B: if i can't read as much, read to the end,
|
||||||
|
var readNormaly = BufferSize - ReadPosition;
|
||||||
|
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
|
||||||
|
|
||||||
|
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
|
||||||
|
//then read the remaining amount from the start
|
||||||
|
|
||||||
|
var readFromStart = count - readNormaly;
|
||||||
|
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
|
||||||
|
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
|
||||||
|
ReadPosition = readFromStart;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken)
|
||||||
|
{
|
||||||
|
if (count > buffer.Length)
|
||||||
|
throw new ArgumentException();
|
||||||
|
while (ContentLength + count > BufferSize)
|
||||||
|
{
|
||||||
|
await Task.Delay(20, cancelToken);
|
||||||
|
if (cancelToken.IsCancellationRequested)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//the while above assures that i cannot write past readposition with my write, so i don't have to check
|
||||||
|
// *unless its multithreaded or task is not awaited
|
||||||
|
lock (readWriteLock)
|
||||||
|
{
|
||||||
|
// if i can just write without hitting buffer.length, do it
|
||||||
|
if (WritePosition + count < BufferSize)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
|
||||||
|
WritePosition += count;
|
||||||
|
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// otherwise, i have to write to the end, then write the rest from the start
|
||||||
|
|
||||||
|
var wroteNormaly = BufferSize - WritePosition;
|
||||||
|
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
|
||||||
|
|
||||||
|
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
|
||||||
|
|
||||||
|
var wroteFromStart = count - wroteNormaly;
|
||||||
|
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
|
||||||
|
|
||||||
|
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
|
||||||
|
|
||||||
|
WritePosition = wroteFromStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,116 +10,17 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using VideoLibrary;
|
using VideoLibrary;
|
||||||
|
|
||||||
namespace NadekoBot.Classes.Music {
|
namespace NadekoBot.Classes.Music
|
||||||
public class SongInfo {
|
{
|
||||||
|
public class SongInfo
|
||||||
|
{
|
||||||
public string Provider { get; internal set; }
|
public string Provider { get; internal set; }
|
||||||
public MusicType ProviderType { get; internal set; }
|
public MusicType ProviderType { get; internal set; }
|
||||||
public string Title { get; internal set; }
|
public string Title { get; internal set; }
|
||||||
public string Uri { get; internal set; }
|
public string Uri { get; internal set; }
|
||||||
}
|
}
|
||||||
/// <summary>
|
public class Song
|
||||||
/// 💩
|
{
|
||||||
/// </summary>
|
|
||||||
public class PoopyBuffer {
|
|
||||||
|
|
||||||
private readonly byte[] ringBuffer;
|
|
||||||
|
|
||||||
public int WritePosition { get; private set; } = 0;
|
|
||||||
public int ReadPosition { get; private set; } = 0;
|
|
||||||
|
|
||||||
public int ContentLength => (WritePosition >= ReadPosition ?
|
|
||||||
WritePosition - ReadPosition :
|
|
||||||
(BufferSize - ReadPosition) + WritePosition);
|
|
||||||
|
|
||||||
public int BufferSize { get; }
|
|
||||||
|
|
||||||
private readonly object readWriteLock = new object();
|
|
||||||
|
|
||||||
public PoopyBuffer(int size) {
|
|
||||||
if (size <= 0)
|
|
||||||
throw new ArgumentException();
|
|
||||||
BufferSize = size;
|
|
||||||
ringBuffer = new byte[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Read(byte[] buffer, int count) {
|
|
||||||
if (buffer.Length < count)
|
|
||||||
throw new ArgumentException();
|
|
||||||
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
|
|
||||||
lock (readWriteLock) {
|
|
||||||
//read as much as you can if you're reading too much
|
|
||||||
if (count > ContentLength)
|
|
||||||
count = ContentLength;
|
|
||||||
//if nothing to read, return 0
|
|
||||||
if (WritePosition == ReadPosition)
|
|
||||||
return 0;
|
|
||||||
// if buffer is in the "normal" state, just read
|
|
||||||
if (WritePosition > ReadPosition) {
|
|
||||||
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
|
|
||||||
ReadPosition += count;
|
|
||||||
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
//else ReadPos <Writepos
|
|
||||||
// buffer is in its inverted state
|
|
||||||
// A: if i can read as much as possible without hitting the buffer.length, read that
|
|
||||||
|
|
||||||
if (count + ReadPosition <= BufferSize) {
|
|
||||||
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
|
|
||||||
ReadPosition += count;
|
|
||||||
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
// B: if i can't read as much, read to the end,
|
|
||||||
var readNormaly = BufferSize - ReadPosition;
|
|
||||||
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
|
|
||||||
|
|
||||||
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
|
|
||||||
//then read the remaining amount from the start
|
|
||||||
|
|
||||||
var readFromStart = count - readNormaly;
|
|
||||||
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
|
|
||||||
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
|
|
||||||
ReadPosition = readFromStart;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken) {
|
|
||||||
if (count > buffer.Length)
|
|
||||||
throw new ArgumentException();
|
|
||||||
while (ContentLength + count > BufferSize) {
|
|
||||||
await Task.Delay(20, cancelToken);
|
|
||||||
if (cancelToken.IsCancellationRequested)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//the while above assures that i cannot write past readposition with my write, so i don't have to check
|
|
||||||
// *unless its multithreaded or task is not awaited
|
|
||||||
lock (readWriteLock) {
|
|
||||||
// if i can just write without hitting buffer.length, do it
|
|
||||||
if (WritePosition + count < BufferSize) {
|
|
||||||
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
|
|
||||||
WritePosition += count;
|
|
||||||
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// otherwise, i have to write to the end, then write the rest from the start
|
|
||||||
|
|
||||||
var wroteNormaly = BufferSize - WritePosition;
|
|
||||||
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
|
|
||||||
|
|
||||||
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
|
|
||||||
|
|
||||||
var wroteFromStart = count - wroteNormaly;
|
|
||||||
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
|
|
||||||
|
|
||||||
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
|
|
||||||
|
|
||||||
WritePosition = wroteFromStart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class Song {
|
|
||||||
public StreamState State { get; internal set; }
|
public StreamState State { get; internal set; }
|
||||||
public string PrettyName =>
|
public string PrettyName =>
|
||||||
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`";
|
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`";
|
||||||
@ -130,22 +31,27 @@ namespace NadekoBot.Classes.Music {
|
|||||||
private bool prebufferingComplete { get; set; } = false;
|
private bool prebufferingComplete { get; set; } = false;
|
||||||
public MusicPlayer MusicPlayer { get; set; }
|
public MusicPlayer MusicPlayer { get; set; }
|
||||||
|
|
||||||
public string PrettyCurrentTime() {
|
public string PrettyCurrentTime()
|
||||||
|
{
|
||||||
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
|
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
|
||||||
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
|
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong bytesSent { get; set; } = 0;
|
private ulong bytesSent { get; set; } = 0;
|
||||||
|
|
||||||
private Song(SongInfo songInfo) {
|
private Song(SongInfo songInfo)
|
||||||
|
{
|
||||||
this.SongInfo = songInfo;
|
this.SongInfo = songInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task BufferSong(CancellationToken cancelToken) =>
|
private Task BufferSong(CancellationToken cancelToken) =>
|
||||||
Task.Run(async () => {
|
Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
Process p = null;
|
Process p = null;
|
||||||
try {
|
try
|
||||||
p = Process.Start(new ProcessStartInfo {
|
{
|
||||||
|
p = Process.Start(new ProcessStartInfo
|
||||||
|
{
|
||||||
FileName = "ffmpeg",
|
FileName = "ffmpeg",
|
||||||
Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
|
Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
@ -156,11 +62,15 @@ namespace NadekoBot.Classes.Music {
|
|||||||
const int blockSize = 3840;
|
const int blockSize = 3840;
|
||||||
var buffer = new byte[blockSize];
|
var buffer = new byte[blockSize];
|
||||||
var attempt = 0;
|
var attempt = 0;
|
||||||
while (!cancelToken.IsCancellationRequested) {
|
while (!cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
var read = 0;
|
var read = 0;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken);
|
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken);
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (read == 0)
|
if (read == 0)
|
||||||
@ -168,7 +78,8 @@ namespace NadekoBot.Classes.Music {
|
|||||||
break;
|
break;
|
||||||
else
|
else
|
||||||
await Task.Delay(100, cancelToken);
|
await Task.Delay(100, cancelToken);
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
attempt = 0;
|
attempt = 0;
|
||||||
await Task.Delay(5, cancelToken);
|
await Task.Delay(5, cancelToken);
|
||||||
}
|
}
|
||||||
@ -176,24 +87,35 @@ namespace NadekoBot.Classes.Music {
|
|||||||
if (songBuffer.ContentLength > 2.MB())
|
if (songBuffer.ContentLength > 2.MB())
|
||||||
prebufferingComplete = true;
|
prebufferingComplete = true;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
Console.WriteLine($"Buffering errored: {ex.Message}");
|
Console.WriteLine($"Buffering errored: {ex.Message}");
|
||||||
} finally {
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
|
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
|
||||||
if (p != null) {
|
if (p != null)
|
||||||
try {
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
p.Kill();
|
p.Kill();
|
||||||
} catch { }
|
}
|
||||||
|
catch { }
|
||||||
p.Dispose();
|
p.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) {
|
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||||
|
{
|
||||||
var bufferTask = new ConfiguredTaskAwaitable();
|
var bufferTask = new ConfiguredTaskAwaitable();
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
|
bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
var clr = Console.ForegroundColor;
|
var clr = Console.ForegroundColor;
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}");
|
Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}");
|
||||||
@ -202,26 +124,31 @@ namespace NadekoBot.Classes.Music {
|
|||||||
var bufferAttempts = 0;
|
var bufferAttempts = 0;
|
||||||
const int waitPerAttempt = 500;
|
const int waitPerAttempt = 500;
|
||||||
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
|
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
|
||||||
while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) {
|
while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes)
|
||||||
|
{
|
||||||
await Task.Delay(waitPerAttempt, cancelToken);
|
await Task.Delay(waitPerAttempt, cancelToken);
|
||||||
}
|
}
|
||||||
cancelToken.ThrowIfCancellationRequested();
|
cancelToken.ThrowIfCancellationRequested();
|
||||||
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
|
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
|
||||||
const int blockSize = 3840;
|
const int blockSize = 3840;
|
||||||
var attempt = 0;
|
var attempt = 0;
|
||||||
while (!cancelToken.IsCancellationRequested) {
|
while (!cancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
|
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
|
||||||
byte[] buffer = new byte[blockSize];
|
byte[] buffer = new byte[blockSize];
|
||||||
var read = songBuffer.Read(buffer, blockSize);
|
var read = songBuffer.Read(buffer, blockSize);
|
||||||
unchecked {
|
unchecked
|
||||||
|
{
|
||||||
bytesSent += (ulong)read;
|
bytesSent += (ulong)read;
|
||||||
}
|
}
|
||||||
if (read == 0)
|
if (read == 0)
|
||||||
if (attempt++ == 20) {
|
if (attempt++ == 20)
|
||||||
|
{
|
||||||
voiceClient.Wait();
|
voiceClient.Wait();
|
||||||
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
|
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
|
||||||
break;
|
break;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
await Task.Delay(100, cancelToken);
|
await Task.Delay(100, cancelToken);
|
||||||
else
|
else
|
||||||
attempt = 0;
|
attempt = 0;
|
||||||
@ -239,11 +166,13 @@ namespace NadekoBot.Classes.Music {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//stackoverflow ftw
|
//stackoverflow ftw
|
||||||
private static byte[] AdjustVolume(byte[] audioSamples, float volume) {
|
private static byte[] AdjustVolume(byte[] audioSamples, float volume)
|
||||||
|
{
|
||||||
if (Math.Abs(volume - 1.0f) < 0.01f)
|
if (Math.Abs(volume - 1.0f) < 0.01f)
|
||||||
return audioSamples;
|
return audioSamples;
|
||||||
var array = new byte[audioSamples.Length];
|
var array = new byte[audioSamples.Length];
|
||||||
for (var i = 0; i < array.Length; i += 2) {
|
for (var i = 0; i < array.Length; i += 2)
|
||||||
|
{
|
||||||
|
|
||||||
// convert byte pair to int
|
// convert byte pair to int
|
||||||
short buf1 = audioSamples[i + 1];
|
short buf1 = audioSamples[i + 1];
|
||||||
@ -263,35 +192,43 @@ namespace NadekoBot.Classes.Music {
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal) {
|
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
|
||||||
|
{
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
throw new ArgumentNullException(nameof(query));
|
throw new ArgumentNullException(nameof(query));
|
||||||
|
|
||||||
if (musicType != MusicType.Local && IsRadioLink(query)) {
|
if (musicType != MusicType.Local && IsRadioLink(query))
|
||||||
|
{
|
||||||
musicType = MusicType.Radio;
|
musicType = MusicType.Radio;
|
||||||
query = await HandleStreamContainers(query) ?? query;
|
query = await HandleStreamContainers(query) ?? query;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try
|
||||||
switch (musicType) {
|
{
|
||||||
|
switch (musicType)
|
||||||
|
{
|
||||||
case MusicType.Local:
|
case MusicType.Local:
|
||||||
return new Song(new SongInfo {
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
Uri = "\"" + Path.GetFullPath(query) + "\"",
|
||||||
Title = Path.GetFileNameWithoutExtension(query),
|
Title = Path.GetFileNameWithoutExtension(query),
|
||||||
Provider = "Local File",
|
Provider = "Local File",
|
||||||
ProviderType = musicType,
|
ProviderType = musicType,
|
||||||
});
|
});
|
||||||
case MusicType.Radio:
|
case MusicType.Radio:
|
||||||
return new Song(new SongInfo {
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
Uri = query,
|
Uri = query,
|
||||||
Title = $"{query}",
|
Title = $"{query}",
|
||||||
Provider = "Radio Stream",
|
Provider = "Radio Stream",
|
||||||
ProviderType = musicType,
|
ProviderType = musicType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (SoundCloud.Default.IsSoundCloudLink(query)) {
|
if (SoundCloud.Default.IsSoundCloudLink(query))
|
||||||
|
{
|
||||||
var svideo = await SoundCloud.Default.GetVideoAsync(query);
|
var svideo = await SoundCloud.Default.GetVideoAsync(query);
|
||||||
return new Song(new SongInfo {
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
Title = svideo.FullName,
|
Title = svideo.FullName,
|
||||||
Provider = "SoundCloud",
|
Provider = "SoundCloud",
|
||||||
Uri = svideo.StreamLink,
|
Uri = svideo.StreamLink,
|
||||||
@ -310,76 +247,99 @@ namespace NadekoBot.Classes.Music {
|
|||||||
|
|
||||||
if (video == null) // do something with this error
|
if (video == null) // do something with this error
|
||||||
throw new Exception("Could not load any video elements based on the query.");
|
throw new Exception("Could not load any video elements based on the query.");
|
||||||
return new Song(new SongInfo {
|
return new Song(new SongInfo
|
||||||
|
{
|
||||||
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
|
||||||
Provider = "YouTube",
|
Provider = "YouTube",
|
||||||
Uri = video.Uri,
|
Uri = video.Uri,
|
||||||
ProviderType = musicType,
|
ProviderType = musicType,
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
Console.WriteLine($"Failed resolving the link.{ex.Message}");
|
Console.WriteLine($"Failed resolving the link.{ex.Message}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> HandleStreamContainers(string query) {
|
private static async Task<string> HandleStreamContainers(string query)
|
||||||
|
{
|
||||||
string file = null;
|
string file = null;
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
file = await SearchHelper.GetResponseStringAsync(query);
|
file = await SearchHelper.GetResponseStringAsync(query);
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
if (query.Contains(".pls")) {
|
if (query.Contains(".pls"))
|
||||||
|
{
|
||||||
//File1=http://armitunes.com:8000/
|
//File1=http://armitunes.com:8000/
|
||||||
//Regex.Match(query)
|
//Regex.Match(query)
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
|
||||||
var res = m.Groups["url"]?.ToString();
|
var res = m.Groups["url"]?.ToString();
|
||||||
return res?.Trim();
|
return res?.Trim();
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
Console.WriteLine($"Failed reading .pls:\n{file}");
|
Console.WriteLine($"Failed reading .pls:\n{file}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query.Contains(".m3u")) {
|
if (query.Contains(".m3u"))
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
# This is a comment
|
# This is a comment
|
||||||
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
|
||||||
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
|
||||||
*/
|
*/
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
|
||||||
var res = m.Groups["url"]?.ToString();
|
var res = m.Groups["url"]?.ToString();
|
||||||
return res?.Trim();
|
return res?.Trim();
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
Console.WriteLine($"Failed reading .m3u:\n{file}");
|
Console.WriteLine($"Failed reading .m3u:\n{file}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (query.Contains(".asx")) {
|
if (query.Contains(".asx"))
|
||||||
|
{
|
||||||
//<ref href="http://armitunes.com:8000"/>
|
//<ref href="http://armitunes.com:8000"/>
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
|
||||||
var res = m.Groups["url"]?.ToString();
|
var res = m.Groups["url"]?.ToString();
|
||||||
return res?.Trim();
|
return res?.Trim();
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
Console.WriteLine($"Failed reading .asx:\n{file}");
|
Console.WriteLine($"Failed reading .asx:\n{file}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (query.Contains(".xspf")) {
|
if (query.Contains(".xspf"))
|
||||||
|
{
|
||||||
/*
|
/*
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
||||||
<trackList>
|
<trackList>
|
||||||
<track><location>file:///mp3s/song_1.mp3</location></track>
|
<track><location>file:///mp3s/song_1.mp3</location></track>
|
||||||
*/
|
*/
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
|
||||||
var res = m.Groups["url"]?.ToString();
|
var res = m.Groups["url"]?.ToString();
|
||||||
return res?.Trim();
|
return res?.Trim();
|
||||||
} catch {
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
Console.WriteLine($"Failed reading .xspf:\n{file}");
|
Console.WriteLine($"Failed reading .xspf:\n{file}");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,41 @@
|
|||||||
using System;
|
using Discord.Commands;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Discord.Commands;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using Discord;
|
|
||||||
using NadekoBot.Modules;
|
using NadekoBot.Modules;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
|
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
|
||||||
|
|
||||||
namespace NadekoBot.Commands {
|
namespace NadekoBot.Commands
|
||||||
internal class Trivia : DiscordCommand {
|
{
|
||||||
|
internal class Trivia : DiscordCommand
|
||||||
|
{
|
||||||
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
|
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||||
|
|
||||||
public Func<CommandEventArgs, Task> DoFunc() => async e => {
|
public Func<CommandEventArgs, Task> DoFunc() => async e =>
|
||||||
|
{
|
||||||
TriviaGame trivia;
|
TriviaGame trivia;
|
||||||
if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
|
if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||||
|
{
|
||||||
var triviaGame = new TriviaGame(e);
|
var triviaGame = new TriviaGame(e);
|
||||||
if (RunningTrivias.TryAdd(e.Server.Id, triviaGame))
|
if (RunningTrivias.TryAdd(e.Server.Id, triviaGame))
|
||||||
await e.Channel.SendMessage("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
|
await e.Channel.SendMessage("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
|
||||||
else
|
else
|
||||||
await triviaGame.StopGame();
|
await triviaGame.StopGame();
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion);
|
await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion);
|
||||||
};
|
};
|
||||||
|
|
||||||
internal override void Init(CommandGroupBuilder cgb) {
|
internal override void Init(CommandGroupBuilder cgb)
|
||||||
|
{
|
||||||
cgb.CreateCommand(Module.Prefix + "t")
|
cgb.CreateCommand(Module.Prefix + "t")
|
||||||
.Description("Starts a game of trivia.")
|
.Description("Starts a game of trivia.")
|
||||||
.Do(DoFunc());
|
.Do(DoFunc());
|
||||||
|
|
||||||
cgb.CreateCommand(Module.Prefix + "tl")
|
cgb.CreateCommand(Module.Prefix + "tl")
|
||||||
.Description("Shows a current trivia leaderboard.")
|
.Description("Shows a current trivia leaderboard.")
|
||||||
.Do(async e=> {
|
.Do(async e =>
|
||||||
|
{
|
||||||
TriviaGame trivia;
|
TriviaGame trivia;
|
||||||
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||||
await e.Channel.SendMessage(trivia.GetLeaderboard());
|
await e.Channel.SendMessage(trivia.GetLeaderboard());
|
||||||
@ -39,11 +45,14 @@ namespace NadekoBot.Commands {
|
|||||||
|
|
||||||
cgb.CreateCommand(Module.Prefix + "tq")
|
cgb.CreateCommand(Module.Prefix + "tq")
|
||||||
.Description("Quits current trivia after current question.")
|
.Description("Quits current trivia after current question.")
|
||||||
.Do(async e=> {
|
.Do(async e =>
|
||||||
|
{
|
||||||
TriviaGame trivia;
|
TriviaGame trivia;
|
||||||
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
|
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||||
|
{
|
||||||
await trivia.StopGame();
|
await trivia.StopGame();
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
await e.Channel.SendMessage("No trivia is running on this server.");
|
await e.Channel.SendMessage("No trivia is running on this server.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user