diff --git a/ComprehensiveGuide.md b/ComprehensiveGuide.md index 1b2c3ea4..1666d345 100644 --- a/ComprehensiveGuide.md +++ b/ComprehensiveGuide.md @@ -1,65 +1,112 @@ ________________________________________________________________________________ -*Thanks to @Flatbread for making this guide* +*Thanks to @Flatbread and Mirai for making this guide* ________________________________________________________________________________ -#### Setting Up NadekoBot v0.98 -###### Prerequisites: -1) NET Framework 4.5.2 (or 4.6) -- Start with making a folder, lets name it `Nadeko` -- Make sure you have **7zip** installed, if not then head to http://www.7-zip.org/download.html and download/install it. -- Now head to https://github.com/Kwoth/NadekoUpdater/releases/tag/v1.0 and download `WINDOWS.-.nadeupdater.7z - - *Alternatively, you can download nadekobot from [releases](https://github.com/Kwoth/NadekoBot/releases) and extract the zip yourself. That is what updater does, except it makes it easier for you to update because it doesn't overwrite important files. If you are downloading releases you will have to be careful about your config, credentials, and other files you edited in order to preserve your data every time you update.* +### Setting Up NadekoBot on Windows +#### Prerequisites +- 1) [NET Framework][NET Framework] 4.5.2 (or 4.6) +- 2) [FFMPEG][FFMPEG] +- 3) Google Account +- 4) Soundcloud Account (if you want soundcloud support) +- 5) [7zip][7zip] (or whatever you are using, WinRar) +- 6) [Notepad++][Notepad++] + +####Guide: + +- Create a folder, name it `Nadeko`. +- Head to [Releases][Releases]* and download `WINDOWS.-.nadeupdater.7z`. - Copy `WINDOWS.-.nadeupdater.7z` to the `Nadeko` (folder we created before) and extract everything. - You will see a file `NadekoUpdater.bat ` and a folder `publish ` after extraction. - Run/Launch/Open the file `NadekoUpdater.bat ` and you will see it running in cmd.exe asking you with **3 options** *1-3*. -- You can try the stable release, but its better to get the newest release if you want all features/upgrades. So for that press `2 ` and hit `Enter ` (read everything shows up on screen) -- It should ask you `Are you sure you want to update?` and for that type `y ` and hit `Enter ` -- It should complete downloading (might take a while) and you can type `3 ` and hit `Enter ` and close it. -- You should now see a new folder `NadekoBot ` inside our `Nadeko ` folder. -- Open it and head over to https://github.com/Kwoth/NadekoBot/blob/master/README.md to setup credentials or just check it out for your knowledge. -- **For Basic Credentials Setup:** -- Rename `credentials_example.json ` into `credentials.json ` (Note: If you do not see a **.json** after `credentials_example.json `, do not add the **.json**. `You are most likely to have "Hide file extensions" as enabled. `) -- **Go to:** http://discord.kongslien.net/guide.html **for detailed guidance with screenshots.** or :arrow_down: -- **Follow for textual guidelines:** Go here https://discordapp.com/developers/applications/me -- Log in with your Discord account. Press **New Application** and fill out an **App Name** and, optionally, an app description and icon. Afterwards, create the application. Once the application is created, click on **Create a Bot User** and confirm it. You will then see the bot's username, ID and token. Reveal and copy the **token** and the **bot ID** *(one by one xD)*. -- Open up `credentials.json` with **NotePad++** Paste the **token** into the **Token** field, between the `"quotes"`. Paste the **Bot ID** into the **BotID** field, when done, *Save* and *close* `credentials.json` -- **To Invite your bot to your server:** -- Copy your CLIENT ID (that's in the same Developer page where you brought your token) and replace `12345678` in this link: -`https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303 ` with it. Go to that link and you will be able to add your bot to your server. -- Start NadekoBot.exe. In a text channel, **not a direct message**, type in [.uid @______] without the brackets, filling in the underlined portion with your name and send the message. Your bot will reply with a number; this is your ID. Copy this ID and close NadekoBot.exe. -- Reopen credentials.json. Paste your ID into the square brackets ("OwnerIds": [1231312313]). **Just paste/replace your ID with existing and keep the default format intact, you don't need to add extra space or anything else.** You can add multiple owners by separating IDs with a comma. Close and save credentials.json. + - 1) Stable release - current stable release, but might not contain all the newest Nadeko updates. + - 2) Newest release - release with all features/upgrades. + - 3) Exit +- Press `2` on your keyboard and hit `Enter`. Type `y` and hit `Enter` again. Downloading might take a while, so just be patient and wait. When download is done, press `3` on your keyboard and close the updater. +- You should have a new folder named `NadekoBot` inside the `Nadeko` folder we previously created. +####Creating DiscordBot application +- Go to [DiscordApp][DiscordApp]. +- Log in with your Discord account. +- On the left side, press `New Application`. +- Fill out the `App Name` (your bot's name, in this case), put the image you want, and add an app description(optional). +- Create the application. +- Once the application is created, click on `Create a Bot User` and confirm it. +- Keep this window open for now. + +####Setting up Credentials.json file +- In our `NadekoBot` folder you should have `.json` file named `credentials_example.json`. (Note: If you do not see a **.json** after `credentials_example.json `, do not add the `**.json**`. You most likely have `"Hide file extensions"` enabled.) +- Rename `credentials_example.json` to `credentials.json`. +- Open the file with your [Notepad++][Notepad++]. +- In there you will see fields like `Token`, `ClientId`, `BotId`, `OwnerIDs`. +- In your [DiscordApp][DiscordApp], under `Bot User` part, you will see the `Token:click to reveal` part, click to reveal it. +- Copy your bot's token, and put it between `" "` in your `credentials.json` file. +- Copy `Client ID` and replace it with the example one in your `credentials.json`. +- Copy `Bot ID` and replace it with the example one in your `credentials.json`. +- Save your `credentials.json` but keep it open. We need to put your `User ID` and owner. + +####Inviting your bot to your server [Invite Guide][Invite Guide] +- Create a new server in Discord. +- Copy your `Client ID` from your [DiscordApp][DiscordApp]. +- Replace `12345678` in this link `https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303` with your `Client ID`. +- Link should look like this: `https://discordapp.com/oauth2/authorize?client_id=**YOUR_CLENT_ID**&scope=bot&permissions=66186303`. +- Go to newly created link and pick the server we created, and click `Authorize`. +- Bot should be added to your server. + +####Starting the bot +- Enter your `NadekoBot` folder that should be (hopefully) in your `Nadeko` folder. +- Run `NadekoBot.exe` (Note: There is `NadekoBot.exe` and `NadekoBot.exe.config`, dont run the second one) +- Your bot should now be online in the server we added him to. +- Note: Your bot will be offline in case you close `NadekoBot.exe`. + +####Setting up OwnerIds: +- In the server where your bot is, in a text channel, type `.uid` +- Your `User ID` should show, copy it. +- Close `NadekoBot.exe` +- Replace your `User ID` in the `credentials.json` between `[ ]` and save the changes. +- Run `NadekoBot.exe` again. +- Now you are the bot owner. +- You can add `User IDs` from the other users by separating IDs with a comma if you want to have more owners. + +`*Alternatively, you can download nadekobot from [Releases][Releases] and extract the zip yourself. That is what updater does, except it makes it easier for you to update because it doesn't overwrite important files. If you are downloading releases you will have to be careful about your config, credentials, and other files you edited in order to preserve your data every time you update.` ________________________________________________________________________________ #### Setting Up NadekoBot For Music -###### Setting up `ffmpeg` with installer: -1) Google Account -2) Soundcloud Account (if you want soundcloud support) -3) Download installer here: https://goo.gl/lQZnsH (pick the one for your system, either 32 or 64bit and then click 'download') -4) Run the installer +##### Prerequisites +- 1) [FFMPEG][FFMPEG] installed. +- 2) Setting up API keys. -5) Follow these steps on how to setup API keys: -- Go to https://console.developers.google.com and log in. -- Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs." -- Under the "Other Popular APIs" section, enable "URL Shortener API". Under the "YouTube APIs" section, enable "YouTube Data API". Also enable Custom Search Api. -- On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key. -- Open up credentials.json. For "GoogleAPIKey", fill in with the new key. -- Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID. +- Follow these steps on how to setup Google API keys: + - Go to [Google Console][Google Console] and log in. + - Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs." + - Under the "Other Popular APIs" section, enable `URL Shortener API` and `Custom Search Api`. Under the `YouTube APIs` section, enable `YouTube Data API`. + - On the left tab, access `Credentials`. Click `Create Credentials` button. Click on `API Key`, and then `Server Key` in the new window that appears. Enter in a name for the `Server Key`. A new window will appear with your `Google API key`. + - Copy the key. + - Open up `credentials.json`. + - For `"GoogleAPIKey"`, fill in with the new key we copied. +- Follow these steps on how to setup Soundcloud API key: + - Go to [Soundcloud][Soundcloud]. + - Enter a name for the app and create it. + - You will see a page with the title of your app, and a field labeled `Client ID`. Copy the ID. + - In `credentials.json`, fill in `"SoundcloudClientID"` with the copied ID. - Restart your computer. -###### Prerequisites for manual `ffmpeg` setup: -1) Google Account -2) Soundcloud Account (if you want soundcloud support) +##### Prerequisites for manual `ffmpeg` setup: +**Do this step in case you were not able to install `ffmpeg` with the installer.** - Create a folder named `ffmpeg` in your main Windows directory. We will use **C:\ffmpeg** (for our guide) - Download FFMPEG through the link https://ffmpeg.zeranoe.com/builds/ (download static build) - Extract it using `7zip` and place the folder `ffmpeg-xxxxx-git-xxxxx-xxxx-static` inside **C:\ffmpeg** - Before proceeding, check out this gif to set up `ffmpeg` PATH correctly http://i.imgur.com/aR5l1Hn.gif *(thanks to PooPeePants#7135)* - Go to My Computer, right click and select Properties. On the left tab, select Advanced System Settings. Under the Advanced tab, select Environmental Variables near the bottom. One of the variables should be called "Path". Add a semi-colon (;) to the end followed by your FFMPEG's **bin** install location (**for example C:\ffmpeg\ffmpeg-xxxxx-git-xxxxx-xxxx-static\bin**). Save and close. -- Go to https://console.developers.google.com and log in. -- Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs." -- Under the "Other Popular APIs" section, enable "URL Shortener API". Under the "YouTube APIs" section, enable "YouTube Data API". Also enable Custom Search Api. -- On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key. -- Open up credentials.json. For "GoogleAPIKey", fill in with the new key. -- Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID. +- Setup your API keys as explained above. - Restart your computer. + +[NET Framework]: https://www.microsoft.com/en-us/download/details.aspx?id=48130 +[FFMPEG]: https://github.com/Soundofdarkness/FFMPEG-Installer +[7zip]: http://www.7-zip.org/download.html +[Releases]: //github.com/Kwoth/NadekoUpdater/releases/tag/v1.0 +[DiscordApp]: https://discordapp.com/developers/applications/me +[Notepad++]: https://notepad-plus-plus.org/ +[Invite Guide]: http://discord.kongslien.net/guide.html +[Google Console]: https://console.developers.google.com +[Soundcloud]: https://soundcloud.com/you/apps/new diff --git a/DockerGuide.md b/DockerGuide.md new file mode 100644 index 00000000..4b6ec756 --- /dev/null +++ b/DockerGuide.md @@ -0,0 +1,53 @@ +## Docker guide with digitalocean + +#####Prerequisites: +- Digital ocean account (you can use my [reflink][reflink] to support the project and get 10$ after you register) +- [PuTTY][PuTTY] +- A bot account - follow this [guide][guide] +- $5 +- Common sense + +#####Guide +- Click on the create droplet button +![img](http://i.imgur.com/g2ayOcC.png) + +- Pick one click apps and select docker on 14.04 + +![img](http://imgur.com/065Xkme.png) + +- Pick any droplet size you want (5$ will work ok-ish on a few servers) +- Pick location closest to your discord server's location +- Pick a hostname +![img](http://imgur.com/ifPKB6p.png) + +- Click create + +You will get an email from DigitalOcean with your credentials now. + +Open putty and type ip adress **you got in your email** with port 22 + +![img](http://imgur.com/Mh5ehsh.png) + +- Console will open and you will be prompted for a username, type `root`. +- Type in the password you got in the email. +- Confirm the password you just typed in. +- Type in the new password. +- Confirm new password. + +- When you are successfully logged in, type +`docker run --name nadeko -v /nadeko:/config uirel/nadeko` + +- Wait for it to download and at one point it is going to start throwing errors due to `credentials.json` being empty +- CTRL+C to exit that +- Type `docker stop nadeko` +- Type `nano /nadeko/credentials.json` and type in your `credentials` +- CTRL+X then CTRL+Y to save +- Type `docker start nadeko` + +**Your bot is running, enjoy! o/** + +*When you want to update the bot, just type `docker restart nadeko` as it always downloads latest prerelease* + +[reflink]: http://m.do.co/c/46b4d3d44795/ +[PuTTY]: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html +[guide]: http://discord.kongslien.net/guide.html diff --git a/LinuxSetup.md b/LinuxSetup.md index c29c8408..784e1253 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -26,10 +26,12 @@ If you entered your Droplets IP address correctly, it should show **login as:** ######MONO (Source: [Mono Source][Mono Source]) -**1)** +**1) Installing Mono** `sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` + `echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` + `sudo apt-get update` Note if the command is not being initiated, hit **Enter** @@ -47,7 +49,9 @@ Note if the command is not being initiated, hit **Enter** *ONLY CentOS 7, Fedora 19 (and later)* `yum install yum-util` + `rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"` + `yum-config-manager --add-repo http://download.mono-project.com/repo/centos/` **3)** @@ -71,8 +75,11 @@ Opus Voice Codec **In case you are having issues with Mono where you get a random string and the bot won't run, do this:** `sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF` + `echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list` + `apt-get install ca-certificates-mono` + `mozroots --import --sync` diff --git a/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs b/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs index b1ed632c..7ccd0e0e 100644 --- a/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs +++ b/NadekoBot/Modules/Administration/Commands/AutoAssignRole.cs @@ -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 => diff --git a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index b26c2444..2ea4346d 100644 --- a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -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 => { diff --git a/NadekoBot/Modules/Games/Commands/BetrayGame.cs b/NadekoBot/Modules/Games/Commands/BetrayGame.cs index 604664a2..74b98bf6 100644 --- a/NadekoBot/Modules/Games/Commands/BetrayGame.cs +++ b/NadekoBot/Modules/Games/Commands/BetrayGame.cs @@ -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 => { diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index 0adce45e..d0ba292b 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -80,7 +80,7 @@ $@"######For more information and how to setup your own NadekoBot, go to: { await e.Channel.SendMessage( diff --git a/NadekoBot/Modules/Help/HelpModule.cs b/NadekoBot/Modules/Help/HelpModule.cs index 1e2afb23..45e4dc76 100644 --- a/NadekoBot/Modules/Help/HelpModule.cs +++ b/NadekoBot/Modules/Help/HelpModule.cs @@ -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().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 => { diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 847070eb..e13239cf 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes public IReadOnlyCollection 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; } diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index 08be13b8..d235776a 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -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); } diff --git a/NadekoBot/Modules/Music/Classes/SongBuffer.cs b/NadekoBot/Modules/Music/Classes/SongBuffer.cs new file mode 100644 index 00000000..d9192940 --- /dev/null +++ b/NadekoBot/Modules/Music/Classes/SongBuffer.cs @@ -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 +{ + /// + /// 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. + /// + 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); + + /// + /// Return the next file to read, and delete the old one + /// + /// Name of the file to read + 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 { } + } + } + } +} diff --git a/NadekoBot/Modules/Translator/ValidLanguagesCommand.cs b/NadekoBot/Modules/Translator/ValidLanguagesCommand.cs index fa3fc11c..438e77de 100644 --- a/NadekoBot/Modules/Translator/ValidLanguagesCommand.cs +++ b/NadekoBot/Modules/Translator/ValidLanguagesCommand.cs @@ -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()); } diff --git a/NadekoBot/Modules/Trello/TrelloModule.cs b/NadekoBot/Modules/Trello/TrelloModule.cs index b144a2cf..14938aca 100644 --- a/NadekoBot/Modules/Trello/TrelloModule.cs +++ b/NadekoBot/Modules/Trello/TrelloModule.cs @@ -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; diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 23852358..d332dad2 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -231,6 +231,7 @@ +