/// <summary> /// Generate snapshots for all episodes. /// </summary> public bool genSnapshots(WorkerVars workerVars, DialogProgress dialogProgress) { int progessCount = 0; int episodeCount = 0; int totalEpisodes = workerVars.CombinedAll.Count; int totalLines = UtilsSubs.getTotalLineCount(workerVars.CombinedAll); DateTime lastTime = UtilsSubs.getLastTime(workerVars.CombinedAll); UtilsName name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeCount++; // For each line in episode, generate a snapshot foreach (InfoCombined t in combArray) { progessCount++; string progressText = $"Generating snapshot: {progessCount.ToString()} of {totalLines.ToString()}"; int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); // Update the progress dialog DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); InfoCombined comb = t; DateTime startTime = comb.Subs1.StartTime; DateTime endTime = comb.Subs1.EndTime; DateTime midTime = UtilsSubs.getMidpointTime(startTime, endTime); string videoFileName = Settings.Instance.VideoClips.Files[episodeCount - 1]; // Create output filename string nameStr = name.createName(ConstantSettings.SnapshotFilenameFormat, (int)episodeCount + Settings.Instance.EpisodeStartNumber - 1, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string outFile = $"{workerVars.MediaDir}{Path.DirectorySeparatorChar}{nameStr}"; // {2} // Generate snapshot UtilsSnapshot.takeSnapshotFromVideo(videoFileName, midTime, Settings.Instance.Snapshots.Size, Settings.Instance.Snapshots.Crop, outFile); // Did the user press the cancel button? if (dialogProgress.Cancel) { return(false); } } } return(true); }
/// <summary> /// Generate the SRS import file. /// </summary> public bool genSrs(WorkerVars workerVars, DialogProgress dialogProgress) { int episodeIdx = -1; int totalEpisodes = workerVars.CombinedAll.Count; int lineIdx = -1; int totalLines = UtilsSubs.getTotalLineCount(workerVars.CombinedAll); lastTime = UtilsSubs.getLastTime(workerVars.CombinedAll); name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); string nameStr = name.createName(ConstantSettings.SrsFilenameFormat, 0, 0, new DateTime(), new DateTime(), "", ""); // Create filename // Example: <outdir>\Toki_wo_Kakeru_Shoujo.tsv string srsFilename = $"{Settings.Instance.OutputDir}{Path.DirectorySeparatorChar}{nameStr}"; TextWriter srsWriter = new StreamWriter(srsFilename, false, Encoding.UTF8); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeIdx++; lineIdx = -1; // For each line in episode, process foreach (InfoCombined comb in combArray) { progessCount++; lineIdx++; mainComb = comb; // Skip lines that are only needed for context if (mainComb.OnlyNeededForContext) { continue; } string srsLine = ""; // Format the current import file line srsLine = formatAll(workerVars.CombinedAll, episodeIdx, lineIdx, FormatType.Normal); // Add leading context lines if (Settings.Instance.ContextLeadingCount > 0) { for (int i = Settings.Instance.ContextLeadingCount; i > 0; i--) { int prevLineIdx = lineIdx - i; if (prevLineIdx >= 0) { if (isLineInContextRange(combArray, lineIdx, prevLineIdx)) { srsLine += formatAll(workerVars.CombinedAll, episodeIdx, prevLineIdx, FormatType.Leading); } else { srsLine += formatContextPlaceholder(FormatType.Leading); } } else { srsLine += formatContextPlaceholder(FormatType.Leading); } } } // Add trailing context lines if (Settings.Instance.ContextTrailingCount > 0) { for (int i = 1; i <= Settings.Instance.ContextTrailingCount; i++) { int nextLineIdx = lineIdx + i; if (nextLineIdx < combArray.Count) { if (isLineInContextRange(combArray, lineIdx, nextLineIdx)) { srsLine += formatAll(workerVars.CombinedAll, episodeIdx, nextLineIdx, FormatType.Trailing); } else { srsLine += formatContextPlaceholder(FormatType.Trailing); } } else { srsLine += formatContextPlaceholder(FormatType.Trailing); } } } // Write line to file srsWriter.WriteLine(srsLine); string progressText = $"Generating SRS (ex. Anki) import file: line {progessCount.ToString()} of {totalLines.ToString()}"; int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); // Did the user press the cancel button? if (dialogProgress.Cancel) { srsWriter.Close(); return(false); } } } srsWriter.Close(); return(true); }
/// <summary> /// Generate video clips for all episodes. /// </summary> public bool genVideoClip(WorkerVars workerVars, DialogProgress dialogProgress) { int progessCount = 0; int episodeCount = 0; int totalEpisodes = workerVars.CombinedAll.Count; int totalLines = UtilsSubs.getTotalLineCount(workerVars.CombinedAll); DateTime lastTime = UtilsSubs.getLastTime(workerVars.CombinedAll); UtilsName name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); DialogProgress.updateProgressInvoke(dialogProgress, 0, "Creating video clips."); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeCount++; // It is possible for all lines in an episode to be set to inactive if (combArray.Count == 0) { // Skip this episode continue; } string progressText = String.Format("Converting video file {0} of {1}", episodeCount, totalEpisodes); DialogProgress.updateProgressInvoke(dialogProgress, progressText); DateTime entireClipStartTime = combArray[0].Subs1.StartTime; DateTime entireClipEndTime = combArray[combArray.Count - 1].Subs1.EndTime; // Apply pad to entire clip timings (if requested) if (Settings.Instance.VideoClips.PadEnabled) { entireClipStartTime = UtilsSubs.applyTimePad(entireClipStartTime, -Settings.Instance.VideoClips.PadStart); entireClipEndTime = UtilsSubs.applyTimePad(entireClipEndTime, Settings.Instance.VideoClips.PadEnd); } // Enable detail mode in progress dialog DialogProgress.enableDetailInvoke(dialogProgress, true); // Set the duration of the clip in the progress dialog (for detail mode) DateTime entireClipDuration = UtilsSubs.getDurationTime(entireClipStartTime, entireClipEndTime); DialogProgress.setDuration(dialogProgress, entireClipDuration); string tempVideoFilename = Path.GetTempPath() + ConstantSettings.TempVideoFilename; string videoExtension = ".avi"; // Convert entire video (from first line to last line) based on user settings (size, crop, bitrate, etc). // It will be cut into smaller video clips later. if (Settings.Instance.VideoClips.IPodSupport) { videoExtension = ".mp4"; tempVideoFilename += videoExtension; UtilsVideo.convertVideo(Settings.Instance.VideoClips.Files[episodeCount - 1], Settings.Instance.VideoClips.AudioStream.Num, entireClipStartTime, entireClipEndTime, Settings.Instance.VideoClips.Size, Settings.Instance.VideoClips.Crop, Settings.Instance.VideoClips.BitrateVideo, Settings.Instance.VideoClips.BitrateAudio, UtilsVideo.VideoCodec.h264, UtilsVideo.AudioCodec.AAC, UtilsVideo.Profilex264.IPod640, UtilsVideo.Presetx264.SuperFast, tempVideoFilename, dialogProgress); } else { tempVideoFilename += videoExtension; UtilsVideo.convertVideo(Settings.Instance.VideoClips.Files[episodeCount - 1], Settings.Instance.VideoClips.AudioStream.Num, entireClipStartTime, entireClipEndTime, Settings.Instance.VideoClips.Size, Settings.Instance.VideoClips.Crop, Settings.Instance.VideoClips.BitrateVideo, Settings.Instance.VideoClips.BitrateAudio, UtilsVideo.VideoCodec.MPEG4, UtilsVideo.AudioCodec.MP3, UtilsVideo.Profilex264.None, UtilsVideo.Presetx264.None, tempVideoFilename, dialogProgress); } DialogProgress.enableDetailInvoke(dialogProgress, false); // Generate a video clip for each line of the episode foreach (InfoCombined comb in combArray) { progessCount++; progressText = string.Format("Generating video clip: {0} of {1}", progessCount.ToString(), totalLines.ToString()); int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); // Update the progress dialog DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); // Did the user press the cancel button? if (dialogProgress.Cancel) { File.Delete(tempVideoFilename); return(false); } // Adjust timing to sync with the start of the converted video (which starts at the episode's first line of dialog) DateTime startTime = UtilsSubs.shiftTiming(comb.Subs1.StartTime, -((int)entireClipStartTime.TimeOfDay.TotalMilliseconds)); DateTime endTime = UtilsSubs.shiftTiming(comb.Subs1.EndTime, -((int)entireClipStartTime.TimeOfDay.TotalMilliseconds)); // Times used in the filename DateTime filenameStartTime = comb.Subs1.StartTime; DateTime filenameEndTime = comb.Subs1.EndTime; // Apply pad (if requested) if (Settings.Instance.VideoClips.PadEnabled) { startTime = UtilsSubs.applyTimePad(startTime, -Settings.Instance.VideoClips.PadStart); endTime = UtilsSubs.applyTimePad(endTime, Settings.Instance.VideoClips.PadEnd); filenameStartTime = UtilsSubs.applyTimePad(comb.Subs1.StartTime, -Settings.Instance.VideoClips.PadStart); filenameEndTime = UtilsSubs.applyTimePad(comb.Subs1.EndTime, Settings.Instance.VideoClips.PadEnd); } // Create output filename string nameStr = name.createName(ConstantSettings.VideoFilenameFormat, (int)episodeCount + Settings.Instance.EpisodeStartNumber - 1, progessCount, filenameStartTime, filenameEndTime, comb.Subs1.Text, comb.Subs2.Text); string outFile = string.Format("{0}{1}{2}{3}", workerVars.MediaDir, // {0} Path.DirectorySeparatorChar, // {1} nameStr, // {2} videoExtension); // {3} // Cut video clip for current line UtilsVideo.cutVideo(tempVideoFilename, startTime, endTime, outFile); } File.Delete(tempVideoFilename); } return(true); }
/// <summary> /// Generate Audio clips for all episodes. /// </summary> public bool genAudioClip(WorkerVars workerVars, DialogProgress dialogProgress) { int progessCount = 0; int episodeCount = 0; int totalEpisodes = workerVars.CombinedAll.Count; int curEpisodeCount = 0; int totalLines = UtilsSubs.getTotalLineCount(workerVars.CombinedAll); DateTime lastTime = UtilsSubs.getLastTime(workerVars.CombinedAll); UtilsName name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); DialogProgress.updateProgressInvoke(dialogProgress, 0, "Creating audio clips."); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeCount++; // It is possible for all lines in an episode to be set to inactive if (combArray.Count == 0) { // Skip this episode continue; } // Is the audio input an mp3 file? bool inputFileIsMp3 = Settings.Instance.AudioClips.Files.Length > 0 && Path.GetExtension(Settings.Instance.AudioClips.Files[episodeCount - 1]) .ToLower() == ".mp3"; DateTime entireClipStartTime = combArray[0].Subs1.StartTime; DateTime entireClipEndTime = combArray[combArray.Count - 1].Subs1.EndTime; string tempMp3Filename = Path.GetTempPath() + ConstantSettings.TempAudioFilename; // Apply pad to entire clip timings (if requested) if (Settings.Instance.AudioClips.PadEnabled) { entireClipStartTime = UtilsSubs.applyTimePad(entireClipStartTime, -Settings.Instance.AudioClips.PadStart); entireClipEndTime = UtilsSubs.applyTimePad(entireClipEndTime, Settings.Instance.AudioClips.PadEnd); } // Do we need to extract the audio from the video file? if (Settings.Instance.AudioClips.UseAudioFromVideo) { string progressText = $"Extracting audio from video file {episodeCount} of {totalEpisodes}"; // {1} bool success = convertToMp3( Settings.Instance.VideoClips.Files[episodeCount - 1], Settings.Instance.VideoClips.AudioStream.Num, progressText, dialogProgress, entireClipStartTime, entireClipEndTime, tempMp3Filename); if (!success) { UtilsMsg.showErrMsg("Failed to extract the audio from the video.\n" + "Make sure that the video does not have any DRM restrictions."); return(false); } } // If the reencode option is set or the input audio is not an mp3, reencode to mp3 else if (ConstantSettings.ReencodeBeforeSplittingAudio || !inputFileIsMp3) { string progressText = $"Reencoding audio file {episodeCount} of {totalEpisodes}"; bool success = convertToMp3( Settings.Instance.AudioClips.Files[episodeCount - 1], "0", progressText, dialogProgress, entireClipStartTime, entireClipEndTime, tempMp3Filename); if (!success) { UtilsMsg.showErrMsg("Failed to reencode the audio file.\n" + "Make sure that the audio file does not have any DRM restrictions."); return(false); } } curEpisodeCount = 0; // Reset // For each line in episode, generate an audio clip foreach (InfoCombined comb in combArray) { progessCount++; curEpisodeCount++; int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); string progressText = $"Generating audio clip: {progessCount.ToString()} of {totalLines.ToString()}"; // Update the progress dialog DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); // Did the user press the cancel button? if (dialogProgress.Cancel) { File.Delete(tempMp3Filename); return(false); } DateTime startTime = comb.Subs1.StartTime; DateTime endTime = comb.Subs1.EndTime; DateTime filenameStartTime = comb.Subs1.StartTime; DateTime filenameEndTime = comb.Subs1.EndTime; string fileToCut = ""; if (Settings.Instance.AudioClips.UseAudioFromVideo || ConstantSettings.ReencodeBeforeSplittingAudio || !inputFileIsMp3) { startTime = UtilsSubs.shiftTiming(startTime, -(int)entireClipStartTime.TimeOfDay.TotalMilliseconds); endTime = UtilsSubs.shiftTiming(endTime, -(int)entireClipStartTime.TimeOfDay.TotalMilliseconds); fileToCut = tempMp3Filename; } else { fileToCut = Settings.Instance.AudioClips.Files[episodeCount - 1]; } // Apply pad (if requested) if (Settings.Instance.AudioClips.PadEnabled) { startTime = UtilsSubs.applyTimePad(startTime, -Settings.Instance.AudioClips.PadStart); endTime = UtilsSubs.applyTimePad(endTime, Settings.Instance.AudioClips.PadEnd); filenameStartTime = UtilsSubs.applyTimePad(comb.Subs1.StartTime, -Settings.Instance.AudioClips.PadStart); filenameEndTime = UtilsSubs.applyTimePad(comb.Subs1.EndTime, Settings.Instance.AudioClips.PadEnd); } string lyricSubs2 = ""; // Set the Subs2 lyric if it exists if (Settings.Instance.Subs[1].Files.Length != 0) { lyricSubs2 = comb.Subs2.Text.Trim(); } // Create output filename string nameStr = name.createName(ConstantSettings.AudioFilenameFormat, (int)episodeCount + Settings.Instance.EpisodeStartNumber - 1, progessCount, filenameStartTime, filenameEndTime, comb.Subs1.Text, lyricSubs2); string outName = $"{workerVars.MediaDir}{Path.DirectorySeparatorChar}{nameStr}"; // {2} // Create audio clip UtilsAudio.cutAudio(fileToCut, startTime, endTime, outName); // Tag the audio clip tagAudio(name, outName, episodeCount, curEpisodeCount, progessCount, combArray.Count, filenameStartTime, filenameEndTime, comb.Subs1.Text, lyricSubs2); } File.Delete(tempMp3Filename); } // Normalize all mp3 files in the media directory if (Settings.Instance.AudioClips.Normalize) { DialogProgress.updateProgressInvoke(dialogProgress, -1, "Normalizing audio..."); UtilsAudio.normalizeAudio(workerVars.MediaDir); } return(true); }