Example #1
0
 private void MakeZip(string originalAudioFile, string outputFilePath, YoutubeProcessRequest request, List <StemFileInfo> stemFiles)
 {
     _logger.LogInformation($"Will create Zip file {outputFilePath}.");
     using (var zip = new FileStream(outputFilePath, FileMode.Create))
     {
         using (var archive = new ZipArchive(zip, ZipArchiveMode.Create))
         {
             // Add the original audio file
             archive.CreateEntryFromFile(originalAudioFile, $"original/{Path.GetFileName(originalAudioFile)}.webm");
             // Add the stems requested
             var validSubformats = YoutubeHelper.FormatMapSub[request.BaseFormat];
             foreach (var subformat in validSubformats)
             {
                 if (request.SubFormats == null || request.SubFormats.Count == 0 || request.SubFormats.Contains(subformat))
                 {
                     var stemFile = stemFiles.FirstOrDefault(sf => sf.FileNameWithoutExtension == subformat);
                     if (stemFile != null)
                     {
                         _logger.LogInformation($"Adding stem {subformat} to Zip file.");
                         archive.CreateEntryFromFile(stemFile.FilePath, stemFile.FileName);
                     }
                 }
             }
         }
     }
 }
Example #2
0
        /// <summary>
        /// Gets the output filename for the spleeter separation.
        /// Format: {cache_root}/yt/split/{vid}.{options}/{format}
        /// </summary>
        private static string GetAudioSplitOutputFolder(YoutubeProcessRequest request)
        {
            var folder = $"{Cache_Root}/yt/split/{request.Vid}";

            if (request.Options.IncludeHighFrequencies)
            {
                folder += ".hf";
            }
            folder += $"/{request.BaseFormat}";
            return(folder);
        }
Example #3
0
 private void MakeMp3(string outputFilePath, YoutubeProcessRequest request, List <StemFileInfo> stemFiles)
 {
     if (request.SubFormats != null && request.SubFormats.Count == 1)
     {
         // Single MP3
         _logger.LogInformation($"Will copy {request.SubFormats[0]}.mp3 stem to {outputFilePath}.");
         var stemFile = stemFiles.FirstOrDefault(sf => sf.FileNameWithoutExtension == request.SubFormats[0]);
         if (stemFile != null)
         {
             File.Copy(stemFile.FilePath, outputFilePath, true);
         }
         else
         {
             _logger.LogError($"Stem file {request.SubFormats[0]} not found for {request.Vid}");
         }
     }
     else
     {
         // Multiple MP3s, merge with ffmpeg
         MergeMp3(outputFilePath, stemFiles.Where(sf => request.SubFormats.Contains(sf.FileNameWithoutExtension)));
     }
 }
Example #4
0
        public ProcessResponse Process(YoutubeProcessRequest request)
        {
            var mainSw   = Stopwatch.StartNew();
            var ip       = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
            var logEntry = new YoutubeOutputLogEntry()
            {
                StartTime   = DateTime.Now,
                Vid         = request.Vid,
                Config      = $"{request.BaseFormat}{(request.Options.IncludeHighFrequencies ? "-hf" : "")}{request.Extension}",
                IpAddress   = ip,
                GeoLocation = GeoLocationHelper.GetGeoLocation(ip),
                Cache       = "Miss"
            };
            // 0. Check output cache
            var outputFilename = GetOutputFileName(request.Vid, request.BaseFormat, request.SubFormats, request.Extension, request.Options.IncludeHighFrequencies);
            var outputFilePath = $"{Output_Root}/yt/{outputFilename}";

            if (File.Exists(outputFilePath))
            {
                logEntry.Cache         = "L1 HIT";
                logEntry.Success       = true;
                logEntry.TimeToProcess = (int)mainSw.Elapsed.TotalSeconds;
                _logger.LogInformation($"Output cache hit: {outputFilePath}");
                return(new ProcessResponse()
                {
                    FileId = outputFilename, LogEntry = logEntry
                });
            }

            // Check processing cache, avoid duplicate requests to run
            var now = DateTime.UtcNow;

            if (_processing.TryGetValue(outputFilename, out DateTime startDate))
            {
                var startedSecondsAgo = (now - startDate).TotalSeconds;
                if (startedSecondsAgo < 1800)
                {
                    return(new ProcessResponse()
                    {
                        Error = $"File {outputFilename} is being processed, started {startedSecondsAgo:N0} seconds ago. Try again later in few more minutes..."
                    });
                }
            }
            _processing[outputFilename] = now;

            // 1. Get video title and duration
            var info = YoutubeHelper.GetVideoInfo(request.Vid);

            if (info.DurationSeconds > Max_Duration_Seconds)
            {
                return(new ProcessResponse()
                {
                    Error = $"Cannot process videos longer than {Max_Duration_Seconds} seconds"
                });
            }
            logEntry.Title    = info.Filename;
            logEntry.Duration = info.DurationSeconds;
            LogStart(request, info);

            // 2. Download Audio
            var audio = YoutubeHelper.DownloadAudio(request.Vid);

            // 3. Split
            var sw = Stopwatch.StartNew();
            var splitOutputFolder = GetAudioSplitOutputFolder(request);
            int fileCount         = Directory.Exists(splitOutputFolder) ? Directory.GetFiles(splitOutputFolder, "*.mp3", SearchOption.AllDirectories).Length : 0;

            if (fileCount == int.Parse(request.BaseFormat.Substring(0, 1)))
            {
                logEntry.Cache = "L2 HIT";
                _logger.LogInformation("Split output cache hit");
            }
            else
            {
                if (fileCount > 0)
                {
                    _logger.LogInformation($"Deleting folder {splitOutputFolder}");
                    Directory.Delete(splitOutputFolder, true);
                }
                // Create the directory to avoid File exists error on spleeter separate process (probably because of missing exist_ok=True parameter in https://github.com/deezer/spleeter/blob/42d476f6fa8d06f498389d1e620d2f1f59565f51/spleeter/audio/ffmpeg.py#L108)
                Directory.CreateDirectory(Path.Combine(splitOutputFolder, request.Vid));
                // Execute the split
                var splitResult = SpliterHelper.Split(audio.AudioFileFullPath, splitOutputFolder, request, isBatch: false);
                _logger.LogInformation($"Separation for {request.Vid}: {(splitResult.ExitCode == 0 ? "Successful" : "Failed")}\n\tDuration: {info.Duration}\n\tProcessing time: {sw.Elapsed:hh\\:mm\\:ss}");
                logEntry.TimeToSeparate = (int)sw.Elapsed.TotalSeconds;
                logEntry.Success        = splitResult.ExitCode == 0;
                logEntry.Errors         = string.Join(", ", splitResult.Errors);
                if (splitResult.ExitCode != 0)
                {
                    _processing.TryRemove(outputFilename, out _);
                    logEntry.TimeToProcess = (int)mainSw.Elapsed.TotalSeconds;
                    return(new ProcessResponse()
                    {
                        Error = $"spleeter separate command exited with code {splitResult.ExitCode}\nMessages: {splitResult.Output}.", LogEntry = logEntry
                    });
                }
            }
            sw.Stop();

            // 4. Make output
            MakeOutput(audio.AudioFileFullPath, outputFilePath, splitOutputFolder, request);

            _processing.TryRemove(outputFilename, out _);
            logEntry.Success       = true;
            logEntry.TimeToProcess = (int)mainSw.Elapsed.TotalSeconds;
            return(new ProcessResponse()
            {
                FileId = outputFilename,
                Speed = sw.Elapsed.TotalSeconds == 0 ? null : $"{info.DurationSeconds / sw.Elapsed.TotalSeconds:N1}x",
                TotalTime = sw.Elapsed.ToString("hh\\:mm\\:ss"),
                LogEntry = logEntry
            });
        }
Example #5
0
 private void LogStart(YoutubeProcessRequest request, YoutubeVideoInfo info)
 {
     Startup.EphemeralLog($"=== YouTube Process ===", true);
     Startup.EphemeralLog($"Request: {JsonConvert.SerializeObject(request)} - Title: {info.Filename} - Duration: {info.Duration}", true);
 }
Example #6
0
        private void MakeOutput(string originalAudioFile, string outputFilePath, string splitOutputFolder, YoutubeProcessRequest request)
        {
            if (File.Exists(outputFilePath))
            {
                File.Delete(outputFilePath);
            }
            Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath));
            var stemFiles = Directory.GetFiles(splitOutputFolder, "*.mp3", SearchOption.AllDirectories)
                            .Select(sf => new StemFileInfo {
                FileName = Path.GetFileName(sf), FileNameWithoutExtension = Path.GetFileNameWithoutExtension(sf), FilePath = sf
            })
                            .ToList();

            if (request.Extension == ".mp4")
            {
                // Video
                var    video = YoutubeHelper.DownloadVideo(request.Vid, true);
                string audioFilepath;
                if (request.SubFormats != null && request.SubFormats.Count == 1)
                {
                    // Single audio file for the video
                    audioFilepath = stemFiles.FirstOrDefault(sf => sf.FileNameWithoutExtension == request.SubFormats[0])?.FilePath;
                }
                else
                {
                    // Multiple audio files for the video
                    audioFilepath = outputFilePath.Replace(".mp4", ".mp3");
                    MergeMp3(audioFilepath, stemFiles.Where(sf => request.SubFormats.Contains(sf.FileNameWithoutExtension)));
                }

                if (audioFilepath != null)
                {
                    var cmd         = $"ffmpeg -y -i \"{video.VideoFileFullPath}\" -i \"{audioFilepath}\" -c:v copy -c:s mov_text -map 0:v:0 -map 1:a:0 -map 0:s:0? \"{outputFilePath}\"";
                    var shellResult = ShellHelper.Execute(cmd);
                    if (shellResult.ExitCode != 0)
                    {
                        throw new Exception(shellResult.Output);
                    }
                }
            }
            else if (request.Extension == ".mp3")
            {
                // Return a single stem mp3 or merge multiple mp3s
                MakeMp3(outputFilePath, request, stemFiles);
            }
            else if (request.Extension == ".zip")
            {
                // ZIP multiple MP3s
                MakeZip(originalAudioFile, outputFilePath, request, stemFiles);
            }
        }
        // V2
        public SplitProcessResult Split(string inputFile, YoutubeProcessRequest request, string outputFolder, bool isBatch = false)
        {
            var output  = new StringBuilder();
            var status  = new SplitProcessResult();
            var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = @"cmd.exe",
                    RedirectStandardInput  = true,
                    UseShellExecute        = false,
                    RedirectStandardOutput = true,
                    RedirectStandardError  = true,
                    CreateNoWindow         = true
                },
                EnableRaisingEvents = true
            };

            process.ErrorDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e)
            {
                if (!string.IsNullOrEmpty(e.Data))
                {
                    ProcessOutputLine("stderr", e.Data, status);
                }
            });
            process.OutputDataReceived += new DataReceivedEventHandler(delegate(object sender, DataReceivedEventArgs e)
            {
                if (!string.IsNullOrEmpty(e.Data))
                {
                    ProcessOutputLine("stdout", e.Data, status);
                }
            });
            process.Start();
            process.BeginOutputReadLine();
            process.BeginErrorReadLine();

            var    maxDurationParam = Max_Duration == "" ? "" : $"--duration {Max_Duration}";
            string formatParam;

            if (request.Options.IncludeHighFrequencies)
            {
                //formatParam = $"-p alt-config/{request.BaseFormat}/base_config_hf.json";
                formatParam = $"-p spleeter:{request.BaseFormat}-16kHz";
            }
            else
            {
                formatParam = $"-p spleeter:{request.BaseFormat}";
            }
            var inputParam      = isBatch ? inputFile : $"\"{inputFile}\"";
            var separateCommand = @$ "spleeter separate -o " "{outputFolder}" " {maxDurationParam} {formatParam} -c mp3 {inputParam}";

            using (var sw = process.StandardInput)
            {
                if (sw.BaseStream.CanWrite)
                {
                    sw.WriteLine(Anaconda_Activate_Script);
                    sw.WriteLine(separateCommand);
                    sw.WriteLine("conda deactivate");
                }
            }

            WaitOrKill(process, 15, separateCommand);

            status.ExitCode = process.ExitCode != 0 ? process.ExitCode : status.FileWrittenCount == 0 ? -1 : 0;
            return(status);
        }
Example #8
0
        public static SplitProcessResult Split(string inputFile, string outputFolder, YoutubeProcessRequest request, bool isBatch = false)
        {
            bool             isWindows = Startup.IsWindows;
            ISplitterAdapter adapter   = isWindows ? _cmdAdapter.Value : _bashAdapter.Value;

            return(adapter?.Split(inputFile, request, outputFolder, isBatch));
        }