public static async Task ExtractSubtitles(string inputFile, string outFolder, Interpolate.OutMode outMode, bool showMsg = true) { try { string msg = "Extracting subtitles from video..."; if (showMsg) { Logger.Log(msg); } List <SubtitleTrack> subtitleTracks = await GetSubtitleTracks(inputFile); int counter = 1; int extractedSuccessfully = 0; foreach (SubtitleTrack subTrack in subtitleTracks) { if (Interpolate.canceled) { break; } string outPath = Path.Combine(outFolder, $"{subTrack.streamIndex}_{subTrack.lang}_{subTrack.encoding}.srt"); string[] trim = FfmpegExtract.GetTrimArgs(); string args = $"-sub_charenc {subTrack.encoding} {trim[0]} -i {inputFile.Wrap()} {trim[1]} -map 0:{subTrack.streamIndex} {outPath.Wrap()}"; await RunFfmpeg(args, LogMode.Hidden, "error"); if (subtitleTracks.Count > 4) { Program.mainForm.SetProgress(FormatUtils.RatioInt(counter, subtitleTracks.Count)); } counter++; if (IOUtils.GetFilesize(outPath) >= 32) { Logger.Log($"[FFCmds] Extracted subtitle track {subTrack.streamIndex} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg"); extractedSuccessfully++; } else { IOUtils.TryDeleteIfExists(outPath); // Delete if encode was not successful } } if (extractedSuccessfully > 0) { Logger.Log($"Extracted {extractedSuccessfully} subtitle tracks from the input video.", false, Logger.GetLastLine().Contains(msg)); Utils.ContainerSupportsSubs(Utils.GetExt(outMode), true); } } catch (Exception e) { Logger.Log("Error extracting subtitles: " + e.Message); } Program.mainForm.SetProgress(0); }
public static async Task MergeAudioAndSubs(string inputFile, string tempFolder) // https://superuser.com/a/277667 { string containerExt = Path.GetExtension(inputFile); string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); File.Move(inputFile, tempPath); string inName = Path.GetFileName(tempPath); string outName = Path.GetFileName(outPath); bool audio = Config.GetBool("keepAudio"); bool subs = Utils.ContainerSupportsSubs(containerExt, false) && Config.GetBool("keepSubs"); string[] audioTracks = audio ? IOUtils.GetFilesSorted(tempFolder, false, "*_audio.*") : new string[0]; // Find audio files string[] subTracks = subs ? IOUtils.GetFilesSorted(tempFolder, false, "*.srt") : new string[0]; // Find subtitle files Dictionary <int, string> trackFiles = new Dictionary <int, string>(); // Dict holding all track files with their index foreach (string audioTrack in audioTracks) // Loop through audio streams to add them to the dict { trackFiles[Path.GetFileNameWithoutExtension(audioTrack).Split('_')[0].GetInt()] = audioTrack; // Add file, dict key is stream index } foreach (string subTrack in subTracks) // Loop through subtitle streams to add them to the dict { trackFiles[Path.GetFileNameWithoutExtension(subTrack).Split('_')[0].GetInt()] = subTrack; // Add file, dict key is stream index } string trackInputArgs = ""; string trackMapArgs = ""; string trackMetaArgs = ""; SortedDictionary <int, string> sortedTrackFiles = new SortedDictionary <int, string>(trackFiles); int inputIndex = 1; // Input index (= output stream index) - Start at 1 since 0 is the video stream foreach (KeyValuePair <int, string> track in sortedTrackFiles) { int streamIndex = track.Key; string trackFile = track.Value; trackInputArgs += $" -i {Path.GetFileName(trackFile)}"; // Input filename if (Path.GetFileNameWithoutExtension(trackFile).Contains("_audio")) { trackMapArgs += $" -map {inputIndex}:a"; // Map input file (audio track) } else { trackMapArgs += $" -map {inputIndex}:s"; // Map input file (subtitle track) } string meta = Path.GetFileNameWithoutExtension(trackFile).Split('_')[1]; if (!string.IsNullOrWhiteSpace(meta)) { if (Path.GetFileNameWithoutExtension(trackFile).Contains("_audio")) { trackMetaArgs += $" -metadata:s:{inputIndex} {meta}"; // Metadata } else { trackMetaArgs += $" -metadata:s:{inputIndex} language={meta}"; // Language } } inputIndex++; } bool allAudioCodecsSupported = true; foreach (string audioTrack in audioTracks) { if (!Utils.ContainerSupportsAudioFormat(Interpolate.current.outMode, Path.GetExtension(audioTrack))) { allAudioCodecsSupported = false; } } if (!allAudioCodecsSupported) { Logger.Log("Warning: Input audio format(s) not fully supported in output container. Audio transfer will not be lossless.", false, false, "ffmpeg"); } string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt); string audioArgs = allAudioCodecsSupported ? "-c:a copy" : Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile)); string args = $" -i {inName} {trackInputArgs} -map 0:v {trackMapArgs} -c:v copy {audioArgs} {subArgs} {trackMetaArgs} -shortest {outName}"; await RunFfmpeg(args, tempFolder, LogMode.Hidden); // if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) // { // Logger.Log("Failed to merge audio losslessly! Trying to re-encode.", false, false, "ffmpeg"); // // args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" + // $"{trackInputArgs} -map 0:v -map 1:a {trackMapArgs} -c:v copy {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} -c:s {subCodec} {trackMetaArgs} -shortest {outName}"; // // await RunFfmpeg(args, tempFolder, LogMode.Hidden); // // if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) // { // Logger.Log("Failed to merge audio, even with re-encoding. Output will not have audio.", false, false, "ffmpeg"); // IOUtils.TryMove(tempPath, inputFile); // Move temp file back // IOUtils.TryDeleteIfExists(tempPath); // return; // } // // string audioExt = Path.GetExtension(audioPath).Remove(".").ToUpper(); // Logger.Log($"Source audio ({audioExt}) has been re-encoded to fit into the target container ({containerExt.Remove(".").ToUpper()}). This may decrease the quality slightly.", false, true, "ffmpeg"); // } if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512) { File.Delete(tempPath); File.Move(outPath, inputFile); } else { File.Move(tempPath, inputFile); } }