/// <summary> /// Gets (or downloads if necessary) a song /// </summary> /// <param name="songTitle"></param> /// <param name="botMessage"></param> /// <returns></returns> public async Task <string> GetOrDownloadSong(string songTitle, IUserMessage botMessage) { try { await MessageUtils.ModifyMessage(botMessage, $"Searching my audio banks for '{songTitle}'"); //First, check if this song exists in our music DIR string songLocation = MusicService.SearchMusicDirectory(songTitle, fileFormat); if (songLocation != null) { return(songLocation); } await MessageUtils.ModifyMessage(botMessage, $"Searching YouTube for '{songTitle}'"); //It doesn't exist, search YouTube for it IList <YouTubeVideo> response = await youTubeSearcher.SearchForYouTube(songTitle); if (response == null) { await MessageUtils.ModifyMessage(botMessage, "Something went wrong while searching on YouTube!"); return(null); } //There were no results if (response.Count == 0) { await MessageUtils.ModifyMessage(botMessage, $"There were no results for '{songTitle}' on YouTube."); return(null); } //Get the first video YouTubeVideo video = response[0]; //This shouldn't ever happen if (video == null) { await MessageUtils.ModifyMessage(botMessage, $"Some issue happened while getting '{songTitle}' off from YouTube."); return(null); } string videoTitle = video.VideoTitle.RemoveIllegalChars(); //Do a second search with the title from YouTube songLocation = MusicService.SearchMusicDirectory(videoTitle, fileFormat); if (songLocation != null) { return(songLocation); } //Make sure the song doesn't succeeds max time if (video.VideoDuration >= Config.bot.AudioSettings.MaxVideoTime) { await MessageUtils.ModifyMessage(botMessage, $"The video **{videoTitle}** by **{video.VideoAuthor}** succeeds max time of {Config.bot.AudioSettings.MaxVideoTime}"); return(null); } //Download the song await MessageUtils.ModifyMessage(botMessage, $"Downloading **{videoTitle}** by **{video.VideoAuthor}**"); songLocation = await musicDownloader.DownloadYouTubeVideo(video.VideoId, musicDirectory); //Do a check here first, in case the operation was cancelled, so we don't say "Something went wrong...", when well... it was just cancelled if (cancellationTokenSource.IsCancellationRequested) { return(null); } //The download must have failed if (songLocation == null) { await MessageUtils.ModifyMessage(botMessage, $"Something went wrong while downloading the song **{videoTitle}** from YouTube!"); return(null); } //If the file extension isn't the same then we need to convert it string audioFileExtension = Path.GetExtension(songLocation); if (audioFileExtension == fileFormat.GetFormatExtension()) { return(songLocation); } //We need to convert it, since they are not the same file format songLocation = await audioConverter.ConvertFileToAudio(songLocation, musicDirectory, true, fileFormat); //Everything when well if (songLocation != null) { return(songLocation); } //Do a check here first, in case the operation was cancelled, so we don't say "An issue occured...", when well... it was just cancelled if (cancellationTokenSource.IsCancellationRequested) { return(null); } //Conversion failed await MessageUtils.ModifyMessage(botMessage, "An issue occured while getting the song ready for playing!"); return(null); } catch (OperationCanceledException) { //User cancelled return(null); } catch (Exception ex) { Logger.Error("An error occured while downloading a YouTube video! {@Exception}", ex); await MessageUtils.ModifyMessage(botMessage, "An issue occured while trying to get the song!"); return(null); } }
public async Task <string> ConvertFileToAudio(string originalLocation, string location, bool deleteOriginal = true, MusicFileFormat musicFileFormat = MusicFileFormat.Mp3) { try { string fullNewLocation = $"{location}{Path.GetFileName(originalLocation).Replace(Path.GetExtension(originalLocation), "")}.{musicFileFormat.GetFormatExtension()}"; Logger.Log($"Converting '{originalLocation}' to '{fullNewLocation}'...", LogVerbosity.Debug); //Start our ffmpeg process Process ffmpeg = new Process { StartInfo = new ProcessStartInfo { FileName = Config.bot.AudioSettings.FfmpegLocation, Arguments = $"-i \"{originalLocation}\" -ar 48000 -y \"{fullNewLocation}\"" } }; ffmpeg.Start(); while (!ffmpeg.HasExited) { if (cancellationToken.IsCancellationRequested) { ffmpeg.Kill(true); ffmpeg.Dispose(); return(null); } await Task.Delay(50); } ffmpeg.Dispose(); //Delete our old file if (deleteOriginal) { //if (File.Exists(originalLocation)) //File.Delete(originalLocation); //else //Were the f**k is our fileToConvert then?? This should never happen but it is here anyway //return null; } //So obviously there was an issue converting... if (!File.Exists(fullNewLocation)) { Logger.Log("There was an issue converting the file!", LogVerbosity.Debug); return(null); } //Ayy, we converted Logger.Log($"Successfully converted to '{fullNewLocation}'.", LogVerbosity.Debug); return(fullNewLocation); } catch (NullReferenceException ex) { #if DEBUG Logger.Log($"Null reference exception while trying to convert a song! FFMPEG path could be set incorrectly!\n{ex}", LogVerbosity.Error); #else Logger.Log($"Null refrence exception while trying to convert a song! FFMPEG path could be set incorrectly!\n{ex.Message}", LogVerbosity.Error); #endif return(null); } catch (Exception ex) { #if DEBUG Logger.Log(ex.ToString(), LogVerbosity.Error); #else Logger.Log(ex.Message, LogVerbosity.Error); #endif return(null); } }
private string DownloadAudio(Video youTubeVideo) { try { //Make sure we haven't been canceled yet if (cancellationSource.IsCancellationRequested) { return(null); } string videoTitle = youTubeVideo.Title.RemoveIllegalChars(); MessageUtils.ModifyMessage(message, $":musical_note: Give me a sec. Downloading **{videoTitle}** from **{youTubeVideo.Author}**...") .GetAwaiter().GetResult(); //Get our video stream info MediaStreamInfoSet videoMediaInfo = client.GetVideoMediaStreamInfosAsync(youTubeVideo.Id).GetAwaiter().GetResult(); AudioStreamInfo streamInfo = videoMediaInfo.Audio.WithHighestBitrate(); string songDownloadLocation = $"{downloadLocation}{videoTitle}.{streamInfo.Container.GetFileExtension()}"; //Download the audio file client.DownloadMediaStreamAsync(streamInfo, songDownloadLocation, null, downloadCancellationToken) .GetAwaiter().GetResult(); //Do another check to make sure our video hasn't been canceled if (cancellationSource.IsCancellationRequested) { return(null); } Logger.Log($"The downloaded video file extension is '{streamInfo.Container.GetFileExtension()}'.", LogVerbosity.Debug); if (streamInfo.Container.GetFileExtension() != downloadFileContainer.GetFormatExtension()) { if (cancellationSource.IsCancellationRequested) { return(null); } if (!ConvertAudioFileToMp3(songDownloadLocation, $"{this.downloadLocation}{videoTitle}.{downloadFileContainer.GetFormatExtension()}")) { if (!disposed) { MessageUtils.ModifyMessage(message, "Sorry, but there was an issue downloading the song! Try again later.").GetAwaiter() .GetResult(); } return(null); } } songDownloadLocation = $"{this.downloadLocation}{videoTitle}.{downloadFileContainer.GetFormatExtension()}"; hasFinishedDownloading = true; //We have finished downloading return(songDownloadLocation); } catch (Exception ex) { #if DEBUG Logger.Log(ex.ToString(), LogVerbosity.Error); #else Logger.Log(ex.Message, LogVerbosity.Error); #endif MessageUtils .ModifyMessage(message, "Sorry, but there was an issue downloading the song! Try again later.") .GetAwaiter().GetResult(); //Log out an error to the owner if they have it enabled if (Config.bot.ReportErrorsToOwner) { Global.BotOwner.SendMessageAsync( $"ERROR: {ex.Message}\nError occured while trying to search or download a video from YouTube on guild `{guild.Id}`."); } //Mark this as true so our error doesn't get deleted hasFinishedDownloading = true; return(null); } }
/// <summary> /// Searches music folder for similar or same results to <see cref="search"/> /// </summary> /// <param name="search">The name of the song to search for</param> /// <param name="fileFormat"></param> /// <returns>Returns the first found similar or matching result</returns> public static string SearchMusicDirectory(string search, MusicFileFormat fileFormat) { if (!Directory.Exists(MusicDir)) { Directory.CreateDirectory(MusicDir); } DirectoryInfo hdDirectoryInWhichToSearch = new DirectoryInfo(MusicDir); FileInfo[] filesInDir = hdDirectoryInWhichToSearch.GetFiles($"*{search}*.{fileFormat.GetFormatExtension()}"); return(filesInDir.Select(foundFile => foundFile.FullName).FirstOrDefault()); }
/// <summary> /// Plays a song in a given voice channel /// </summary> /// <param name="guild">The <see cref="SocketGuild"/> where we are playing in</param> /// <param name="channel">The <see cref="IMessageChannel"/> to log messages to</param> /// <param name="target">The target <see cref="IVoiceChannel"/> to play music in</param> /// <param name="user">The <see cref="IUser"/> who requested this command</param> /// <param name="search">The query to search for</param> /// <returns></returns> public async Task SendAudio(SocketGuild guild, IMessageChannel channel, IVoiceChannel target, IUser user, string search) { //Join the voice channel the user is in if we are already not in a voice channel if (!CheckIfServerIsPlayingMusic(guild, out ServerMusicItem serverMusicList)) { await JoinAudio(guild, target, channel, user); serverMusicList = GetMusicList(guild.Id); } //Check to see if the user is in the playing audio channel if (!await CheckIfUserInChat(user, channel, serverMusicList)) { return; } //Make sure the search isn't empty or null if (string.IsNullOrWhiteSpace(search)) { await channel.SendMessageAsync("You need to input a search!"); return; } IUserMessage message = await channel.SendMessageAsync($":musical_note: Preparing to search for '{search}'"); string songFileLocation; string songName; search.RemoveIllegalChars(); try { songFileLocation = await GetOrDownloadSong(search, message, serverMusicList); //It failed if (songFileLocation == null) { return; } Logger.Log($"Playing song from {songFileLocation}", LogVerbosity.Debug); //This is so we say "Now playing 'Epic Song'" instead of "Now playing 'Epic Song.mp3'" songName = Path.GetFileName(songFileLocation).Replace($".{fileFormat.GetFormatExtension()}", ""); //If there is already a song playing, cancel it await StopPlayingAudioOnServer(serverMusicList); } catch (Exception ex) { Logger.Log(ex.ToString(), LogVerbosity.Error); return; } //Create music playback for our music format IMusicPlaybackInterface playbackInterface = serverMusicList.MusicPlayback = CreateMusicPlayback(songFileLocation); //Log (if enabled) to the console that we are playing a new song if (Config.bot.AudioSettings.LogPlayStopSongToConsole) { Logger.Log($"The song '{songName}' on server {guild.Name}({guild.Id}) has started.", LogVerbosity.Music); } serverMusicList.CancellationSource = new CancellationTokenSource(); CancellationToken token = serverMusicList.CancellationSource.Token; serverMusicList.IsPlaying = true; //Create an outgoing pcm stream await using (AudioOutStream outStream = serverMusicList.AudioClient.CreatePCMStream(AudioApplication.Music)) { bool fail = false; bool exit = false; const int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; await MessageUtils.ModifyMessage(message, $":musical_note: Now playing **{songName}**."); while (!fail && !exit) { try { if (token.IsCancellationRequested) { exit = true; break; } //Read from stream int read = await playbackInterface.ReadAudioStream(buffer, bufferSize, token); if (read == 0) { exit = true; break; } //Flush await playbackInterface.Flush(); //Write it to outgoing pcm stream await outStream.WriteAsync(buffer, 0, read, token); //If we are still playing if (serverMusicList.IsPlaying) { continue; } //For pausing the song do { //Do nothing, wait till is playing is true await Task.Delay(100, token); } while (serverMusicList.IsPlaying == false); } catch (OperationCanceledException) { //User canceled } catch (Exception ex) { await channel.SendMessageAsync("Sorry, but an error occured while playing!"); if (Config.bot.ReportErrorsToOwner) { await Global.BotOwner.SendMessageAsync( $"ERROR: {ex.Message}\nError occured while playing music on guild `{guild.Id}`."); } fail = true; } } if (Config.bot.AudioSettings.LogPlayStopSongToConsole) { Logger.Log($"The song '{songName}' on server {guild.Name}({guild.Id}) has stopped.", LogVerbosity.Music); } //There wasn't a request to cancel if (!token.IsCancellationRequested) { await channel.SendMessageAsync($":musical_note: **{songName}** ended."); } //Clean up // ReSharper disable MethodSupportsCancellation await outStream.FlushAsync(); outStream.Dispose(); serverMusicList.IsPlaying = false; playbackInterface.EndAudioStream(); serverMusicList.MusicPlayback = null; // ReSharper restore MethodSupportsCancellation serverMusicList.CancellationSource.Dispose(); serverMusicList.CancellationSource = null; } }
public async Task <string> ConvertFileToAudio(string originalLocation, string location, bool deleteOriginal = true, MusicFileFormat musicFileFormat = MusicFileFormat.Mp3) { try { string fullNewLocation = $"{location}{Path.GetFileName(originalLocation).Replace(Path.GetExtension(originalLocation), "")}.{musicFileFormat.GetFormatExtension()}"; Logger.Debug("Converting {@OriginalLocation} to {@FullNewLocation}...", originalLocation, fullNewLocation); //Start our ffmpeg process Process ffmpeg = new Process { StartInfo = new ProcessStartInfo { FileName = $"{Config.bot.AudioSettings.ExternalDirectory}ffmpeg", Arguments = $"-loglevel fatal -nostdin -i \"{originalLocation}\" -ar 48000 -y \"{fullNewLocation}\"", CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = false } }; ffmpeg.Start(); while (!ffmpeg.HasExited) { if (cancellationToken.IsCancellationRequested) { ffmpeg.Kill(true); ffmpeg.Dispose(); return(null); } await Task.Delay(50); } ffmpeg.Dispose(); //Delete our old file if (deleteOriginal) { if (File.Exists(originalLocation)) { File.Delete(originalLocation); } else //Were the f**k is our fileToConvert then?? This should never happen but it is here anyway { return(null); } } //So obviously there was an issue converting... if (!File.Exists(fullNewLocation)) { Logger.Debug("There was an issue converting the file!"); return(null); } //Ayy, we converted Logger.Debug($"Successfully converted to '{fullNewLocation}'."); return(fullNewLocation); } catch (NullReferenceException ex) { Logger.Error( "Null reference exception while trying to convert a song! FFMPEG path could be set incorrectly! {@Exception}", ex); return(null); } catch (Exception ex) { Logger.Error("An error occured while trying to convert a video! {@Exception}", ex); return(null); } }