Merge branch 'dev' of https://github.com/Kwoth/NadekoBot into dev
This commit is contained in:
@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Administration.Commands
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "autoassignrole")
|
||||
.Alias(Module.Prefix + "aar")
|
||||
.Description($"Automaticaly assigns a specified role to every user who joins the server. Type `{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
|
||||
.Description($"Automaticaly assigns a specified role to every user who joins the server. |`{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
|
||||
.Parameter("role", ParameterType.Unparsed)
|
||||
.AddCheck(new SimpleCheckers.ManageRoles())
|
||||
.Do(async e =>
|
||||
|
@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration.Commands
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "repeatinvoke")
|
||||
.Alias(Module.Prefix + "repinv")
|
||||
.Description("Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`")
|
||||
.Description($"Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`")
|
||||
.AddCheck(SimpleCheckers.ManageMessages())
|
||||
.Do(async e =>
|
||||
{
|
||||
|
@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Commands
|
||||
cgb.CreateCommand(Module.Prefix + "betray")
|
||||
.Description("BETRAY GAME. Betray nadeko next turn." +
|
||||
"If Nadeko cooperates - you get extra points, nadeko loses a LOT." +
|
||||
"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
|
||||
$"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
|
||||
.Do(async e =>
|
||||
{
|
||||
await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false);
|
||||
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games.Commands
|
||||
cgb.CreateCommand(Module.Prefix + "cooperate")
|
||||
.Description("BETRAY GAME. Cooperate with nadeko next turn." +
|
||||
"If Nadeko cooperates - you both get bonus points." +
|
||||
"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
|
||||
$"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
|
||||
.Do(async e =>
|
||||
{
|
||||
|
||||
|
@ -80,7 +80,7 @@ $@"######For more information and how to setup your own NadekoBot, go to: <http:
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "h")
|
||||
.Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h")
|
||||
.Description("Either shows a help for a single command, or PMs you help link if no arguments are specified. | `-h !m q` or just `-h` ")
|
||||
.Description($"Either shows a help for a single command, or PMs you help link if no arguments are specified. | `{Prefix}h !m q` or just `{Prefix}h` ")
|
||||
.Parameter("command", ParameterType.Unparsed)
|
||||
.Do(HelpFunc());
|
||||
cgb.CreateCommand(Module.Prefix + "hgit")
|
||||
@ -102,7 +102,7 @@ $@"######For more information and how to setup your own NadekoBot, go to: <http:
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "donate")
|
||||
.Alias("~donate")
|
||||
.Description("Instructions for helping the project! | `{Prefix}donate` or `~donate`")
|
||||
.Description($"Instructions for helping the project! | `{Prefix}donate` or `~donate`")
|
||||
.Do(async e =>
|
||||
{
|
||||
await e.Channel.SendMessage(
|
||||
|
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Help
|
||||
|
||||
cgb.CreateCommand(Prefix + "modules")
|
||||
.Alias(".modules")
|
||||
.Description("List all bot modules. | `{Prefix}modules` or `.modules`")
|
||||
.Description($"List all bot modules. | `{Prefix}modules` or `.modules`")
|
||||
.Do(async e =>
|
||||
{
|
||||
await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`")
|
||||
@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Help
|
||||
|
||||
cgb.CreateCommand(Prefix + "commands")
|
||||
.Alias(".commands")
|
||||
.Description("List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`")
|
||||
.Description($"List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`")
|
||||
.Parameter("module", ParameterType.Unparsed)
|
||||
.Do(async e =>
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
public IReadOnlyCollection<Song> Playlist => playlist;
|
||||
|
||||
public Song CurrentSong { get; private set; }
|
||||
private CancellationTokenSource SongCancelSource { get; set; }
|
||||
public CancellationTokenSource SongCancelSource { get; private set; }
|
||||
private CancellationToken cancelToken { get; set; }
|
||||
|
||||
public bool Paused { get; set; }
|
||||
|
@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
public SongInfo SongInfo { get; }
|
||||
public string QueuerName { get; set; }
|
||||
|
||||
private bool bufferingCompleted { get; set; } = false;
|
||||
public MusicPlayer MusicPlayer { get; set; }
|
||||
|
||||
public string PrettyCurrentTime()
|
||||
@ -73,78 +72,22 @@ namespace NadekoBot.Modules.Music.Classes
|
||||
return this;
|
||||
}
|
||||
|
||||
private Task BufferSong(string filename, CancellationToken cancelToken) =>
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
Process p = null;
|
||||
try
|
||||
{
|
||||
p = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "ffmpeg",
|
||||
Arguments = $"-ss {skipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = false,
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
var prebufferSize = 100ul.MiB();
|
||||
using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
|
||||
{
|
||||
byte[] buffer = new byte[81920];
|
||||
int bytesRead;
|
||||
while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
|
||||
while ((ulong)outStream.Length - bytesSent > prebufferSize)
|
||||
await Task.Delay(100, cancelToken);
|
||||
}
|
||||
}
|
||||
|
||||
bufferingCompleted = true;
|
||||
}
|
||||
catch (System.ComponentModel.Win32Exception) {
|
||||
var oldclr = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
|
||||
Please install and configure FFMPEG to play music.
|
||||
Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
Windows Guide: https://goo.gl/SCv72y
|
||||
Linux Guide: https://goo.gl/rRhjCp");
|
||||
Console.ForegroundColor = oldclr;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Buffering stopped: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Console.WriteLine($"Buffering done.");
|
||||
if (p != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
p.Kill();
|
||||
}
|
||||
catch { }
|
||||
p.Dispose();
|
||||
}
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
|
||||
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
|
||||
{
|
||||
var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
|
||||
|
||||
var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false);
|
||||
SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
|
||||
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
|
||||
|
||||
var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
|
||||
var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
|
||||
|
||||
bytesSent = 0;
|
||||
|
||||
try
|
||||
{
|
||||
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
|
||||
var attempt = 0;
|
||||
|
||||
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
|
||||
var sw = new Stopwatch();
|
||||
sw.Start();
|
||||
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
|
||||
@ -162,7 +105,6 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed);
|
||||
|
||||
const int blockSize = 3840;
|
||||
var attempt = 0;
|
||||
byte[] buffer = new byte[blockSize];
|
||||
while (!cancelToken.IsCancellationRequested)
|
||||
{
|
||||
@ -173,14 +115,31 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
{
|
||||
bytesSent += (ulong)read;
|
||||
}
|
||||
if (read == 0)
|
||||
if (attempt++ == 20)
|
||||
if (read < blockSize)
|
||||
{
|
||||
if (sb.IsNextFileReady())
|
||||
{
|
||||
voiceClient.Wait();
|
||||
break;
|
||||
inStream.Dispose();
|
||||
inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
|
||||
read += inStream.Read(buffer, read, buffer.Length - read);
|
||||
attempt = 0;
|
||||
}
|
||||
if (read == 0)
|
||||
{
|
||||
if (sb.BufferingCompleted)
|
||||
break;
|
||||
if (attempt++ == 20)
|
||||
{
|
||||
voiceClient.Wait();
|
||||
MusicPlayer.SongCancelSource.Cancel();
|
||||
break;
|
||||
}
|
||||
else
|
||||
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
||||
attempt = 0;
|
||||
}
|
||||
else
|
||||
attempt = 0;
|
||||
|
||||
@ -195,14 +154,16 @@ Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
{
|
||||
await bufferTask;
|
||||
await Task.Run(() => voiceClient.Clear());
|
||||
inStream.Dispose();
|
||||
try { File.Delete(filename); } catch { }
|
||||
if(inStream != null)
|
||||
inStream.Dispose();
|
||||
Console.WriteLine("l");
|
||||
sb.CleanFiles();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken)
|
||||
private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
|
||||
{
|
||||
while (!bufferingCompleted && inStream.Length < 2.MiB())
|
||||
while (!sb.BufferingCompleted && inStream.Length < 2.MiB())
|
||||
{
|
||||
await Task.Delay(100, cancelToken);
|
||||
}
|
||||
|
159
NadekoBot/Modules/Music/Classes/SongBuffer.cs
Normal file
159
NadekoBot/Modules/Music/Classes/SongBuffer.cs
Normal file
@ -0,0 +1,159 @@
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Music.Classes
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
|
||||
/// It also help for large music by deleting files that are already seen.
|
||||
/// </summary>
|
||||
class SongBuffer
|
||||
{
|
||||
|
||||
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
|
||||
{
|
||||
Basename = basename;
|
||||
SongInfo = songInfo;
|
||||
SkipTo = skipTo;
|
||||
}
|
||||
|
||||
private string Basename;
|
||||
|
||||
private SongInfo SongInfo;
|
||||
|
||||
private int SkipTo;
|
||||
|
||||
private static int MAX_FILE_SIZE = 20.MiB();
|
||||
|
||||
private long FileNumber = -1;
|
||||
|
||||
private long NextFileToRead = 0;
|
||||
|
||||
public bool BufferingCompleted { get; private set;} = false;
|
||||
|
||||
private ulong CurrentBufferSize = 0;
|
||||
|
||||
public Task BufferSong(CancellationToken cancelToken) =>
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
Process p = null;
|
||||
FileStream outStream = null;
|
||||
try
|
||||
{
|
||||
p = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = "ffmpeg",
|
||||
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = false,
|
||||
CreateNoWindow = true,
|
||||
});
|
||||
|
||||
byte[] buffer = new byte[81920];
|
||||
int currentFileSize = 0;
|
||||
ulong prebufferSize = 100ul.MiB();
|
||||
|
||||
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||
while (!p.HasExited) //Also fix low bandwidth
|
||||
{
|
||||
int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false);
|
||||
if (currentFileSize >= MAX_FILE_SIZE)
|
||||
{
|
||||
try
|
||||
{
|
||||
outStream.Dispose();
|
||||
}catch { }
|
||||
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||
currentFileSize = bytesRead;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFileSize += bytesRead;
|
||||
}
|
||||
CurrentBufferSize += Convert.ToUInt64(bytesRead);
|
||||
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
|
||||
while (CurrentBufferSize > prebufferSize)
|
||||
await Task.Delay(100, cancelToken);
|
||||
}
|
||||
BufferingCompleted = true;
|
||||
}
|
||||
catch (System.ComponentModel.Win32Exception)
|
||||
{
|
||||
var oldclr = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
|
||||
Please install and configure FFMPEG to play music.
|
||||
Check the guides for your platform on how to setup ffmpeg correctly:
|
||||
Windows Guide: https://goo.gl/SCv72y
|
||||
Linux Guide: https://goo.gl/rRhjCp");
|
||||
Console.ForegroundColor = oldclr;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Buffering stopped: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(outStream != null)
|
||||
outStream.Dispose();
|
||||
Console.WriteLine($"Buffering done.");
|
||||
if (p != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
p.Kill();
|
||||
}
|
||||
catch { }
|
||||
p.Dispose();
|
||||
}
|
||||
}
|
||||
}, TaskCreationOptions.LongRunning);
|
||||
|
||||
/// <summary>
|
||||
/// Return the next file to read, and delete the old one
|
||||
/// </summary>
|
||||
/// <returns>Name of the file to read</returns>
|
||||
public string GetNextFile()
|
||||
{
|
||||
string filename = Basename + "-" + NextFileToRead;
|
||||
|
||||
if (NextFileToRead != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentBufferSize -= Convert.ToUInt64(new FileInfo(Basename + "-" + (NextFileToRead - 1)).Length);
|
||||
File.Delete(Basename + "-" + (NextFileToRead - 1));
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
NextFileToRead++;
|
||||
return filename;
|
||||
}
|
||||
|
||||
public bool IsNextFileReady()
|
||||
{
|
||||
return NextFileToRead <= FileNumber;
|
||||
}
|
||||
|
||||
public void CleanFiles()
|
||||
{
|
||||
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(Basename + "-" + i);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Translator
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "translangs")
|
||||
.Description("List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
|
||||
.Description($"List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
|
||||
.Parameter("search", ParameterType.Optional)
|
||||
.Do(ListLanguagesFunc());
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Trello
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Prefix + "unbind")
|
||||
.Description("Unbinds a bot from the channel and board.")
|
||||
.Description($"Unbinds a bot from the channel and board. | `{Prefix}unbind`")
|
||||
.Do(async e =>
|
||||
{
|
||||
if (!NadekoBot.IsOwner(e.User.Id)) return;
|
||||
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Trello
|
||||
|
||||
cgb.CreateCommand(Prefix + "lists")
|
||||
.Alias(Prefix + "list")
|
||||
.Description("Lists all lists yo ;)")
|
||||
.Description($"Lists all lists yo ;) | {Prefix}list")
|
||||
.Do(async e =>
|
||||
{
|
||||
if (!NadekoBot.IsOwner(e.User.Id)) return;
|
||||
|
@ -231,6 +231,7 @@
|
||||
<Compile Include="Modules\Music\Classes\MusicControls.cs" />
|
||||
<Compile Include="Modules\Music\Classes\PoopyBuffer.cs" />
|
||||
<Compile Include="Modules\Music\Classes\Song.cs" />
|
||||
<Compile Include="Modules\Music\Classes\SongBuffer.cs" />
|
||||
<Compile Include="Modules\Music\Classes\SoundCloud.cs" />
|
||||
<Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" />
|
||||
<Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" />
|
||||
|
Reference in New Issue
Block a user