/// <summary> /// Apply tag to audio file. /// </summary> private void tagAudio(UtilsName name, string outName, int episodeCount, int curEpisodeCount, int progessCount, int totalTracks, DateTime filenameStartTime, DateTime filenameEndTime, string lyricSubs1, string lyricSubs2) { int episodeNum = episodeCount + Settings.Instance.EpisodeStartNumber - 1; string tagArtist = name.createName(ConstantSettings.AudioId3Artist, episodeNum, progessCount, filenameStartTime, filenameEndTime, lyricSubs1, lyricSubs2); string tagAlbum = name.createName(ConstantSettings.AudioId3Album, episodeNum, progessCount, filenameStartTime, filenameEndTime, lyricSubs1, lyricSubs2); string tagTitle = name.createName(ConstantSettings.AudioId3Title, episodeNum, progessCount, filenameStartTime, filenameEndTime, lyricSubs1, lyricSubs2); string tagGenre = name.createName(ConstantSettings.AudioId3Genre, episodeNum, progessCount, filenameStartTime, filenameEndTime, lyricSubs1, lyricSubs2); string tagLyrics = name.createName(ConstantSettings.AudioId3Lyrics, episodeNum, progessCount, filenameStartTime, filenameEndTime, lyricSubs1, lyricSubs2); UtilsAudio.tagAudio(outName, tagArtist, tagAlbum, tagTitle, tagGenre, tagLyrics, curEpisodeCount, totalTracks); }
/// <summary> /// Format a pair of lyrics. /// </summary> private string formatLyricsPair(InfoCombined comb, UtilsName name, DateTime clipStartTime, int episode, int sequenceNum) { string pair = ""; string subs1Text = comb.Subs1.Text; string subs2Text = comb.Subs2.Text; DateTime lyricStartTime = new DateTime(); DateTime lyricEndTime = new DateTime(); lyricStartTime = lyricStartTime.AddMilliseconds(comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds - clipStartTime.TimeOfDay.TotalMilliseconds); lyricEndTime = lyricEndTime.AddMilliseconds(comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds - clipStartTime.TimeOfDay.TotalMilliseconds); string nameStr = name.createName(ConstantSettings.ExtractMediaLyricsSubs1Format, episode, sequenceNum, lyricStartTime, lyricEndTime, subs1Text, subs2Text); pair += String.Format("{0}", nameStr); if (this.textBoxSubs2File.Text.Trim().Length > 0) { if (ConstantSettings.ExtractMediaLyricsSubs2Format != "") { nameStr = name.createName(ConstantSettings.ExtractMediaLyricsSubs2Format, episode, sequenceNum, lyricStartTime, lyricEndTime, subs1Text, subs2Text); pair += "\r\n"; pair += String.Format("{0}", nameStr); } } return(pair); }
/// <summary> /// Extract the image file(s) from the provided Vobsub subtitle text line. /// </summary> public static List <string> extractVobsubFilesFromText(string text) { List <string> images = new List <string>(); UtilsName name = new UtilsName(Settings.Instance.DeckName, 0, 0, new DateTime(), Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); string prefixStr = name.createName(ConstantSettings.SrsVobsubFilenamePrefix, 0, 0, new DateTime(), new DateTime(), "", ""); string suffixStr = name.createName(ConstantSettings.SrsVobsubFilenameSuffix, 0, 0, new DateTime(), new DateTime(), "", ""); try { // Multiple vobsub image can be shown in a single line, so extract each image // and concatenate them before displaying. MatchCollection matches = Regex.Matches(text, prefixStr + "(?<VobsubImage>[\\w.-]*)" + suffixStr, RegexOptions.Compiled); foreach (Match match in matches) { images.Add(match.Groups["VobsubImage"].ToString().Trim()); } } catch { return(images); } return(images); }
/// <summary> /// Format a pair of dialog for the quick reference text file. /// </summary> private string formatQuickReferenceDialogPair(InfoCombined comb, UtilsName name, int episode, int sequenceNumber) { string pair = ""; string subs1Text = comb.Subs1.Text; string subs2Text = comb.Subs2.Text; string nameStr = name.createName(ConstantSettings.DuelingQuickRefSubs1Format, episode, sequenceNumber, comb.Subs1.StartTime, comb.Subs1.StartTime, subs1Text, subs2Text); pair += String.Format("{0}", nameStr); if (this.textBoxSubs2File.Text.Trim().Length > 0) { if (ConstantSettings.DuelingQuickRefSubs2Format != "") { nameStr = name.createName(ConstantSettings.DuelingQuickRefSubs2Format, episode, sequenceNumber, comb.Subs1.StartTime, comb.Subs1.StartTime, subs1Text, subs2Text); pair += "\n"; pair += String.Format("{0}", nameStr); } } return(pair); }
/// <summary> /// Experimental video preview on the selected line. /// </summary> private void buttonPreviewVideo_Click(object sender, EventArgs e) { string videoConfigPlayer = ""; if (previewWorkerVars == null || currentInfoComb == null || Settings.Instance.VideoClips.Files.Length == 0) { return; } videoConfigPlayer = ConstantSettings.VideoPlayer; // Check if the player exists if (!File.Exists(videoConfigPlayer)) { UtilsMsg.showErrMsg( $"The video player \"{ConstantSettings.VideoPlayer}\" \nprovided in {ConstantSettings.SettingsFilename} does not exist!"); return; } // Kill the player before running it again. // For some reason the players that I tested (MPC-HC, ZoomPlayer, VLC) don't // read the start time arg correctly unless they are killed first. if (videoPlayerProcess != null) { if (!videoPlayerProcess.HasExited) { videoPlayerProcess.Kill(); } } DateTime startTime = currentInfoComb.Subs1.StartTime; DateTime endTime = currentInfoComb.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); } UtilsName name = new UtilsName(Settings.Instance.DeckName, Settings.Instance.VideoClips.Files.Length, 0, endTime, Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); string videoConfigArgs = name.createName(ConstantSettings.VideoPlayerArgs, comboBoxEpisode.SelectedIndex + Settings.Instance.EpisodeStartNumber - 1, 0, startTime, endTime, "", ""); string videoFileName = Settings.Instance.VideoClips.Files[comboBoxEpisode.SelectedIndex]; string procArgs = $"{videoConfigArgs} \"{videoFileName}\""; // Play the video videoPlayerProcess = new Process(); videoPlayerProcess.StartInfo.FileName = videoConfigPlayer; videoPlayerProcess.StartInfo.Arguments = procArgs; videoPlayerProcess.StartInfo.UseShellExecute = false; videoPlayerProcess.StartInfo.CreateNoWindow = true; videoPlayerProcess.Start(); }
/// <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> /// Parse the subtitle file and return a list of lines. /// </summary> override public List <InfoLine> parse() { List <InfoLine> lineInfos = new List <InfoLine>(2000); SubtitleCreator.SUP.reset(); SubtitleCreator.SUP sup = SubtitleCreator.SUP.Instance; // Load in the .idx/.sub file (takes a while) sup.Filename = this.File; if (Settings.Instance.VobSubColors.Enabled) { sup.ReadSUP(this.Stream, Settings.Instance.VobSubColors.Colors, Settings.Instance.VobSubColors.TransparencyEnabled); } else { sup.ReadSUP(this.Stream, null, null); } UtilsName name = new UtilsName(Settings.Instance.DeckName, 0, 0, new DateTime(), Settings.Instance.VideoClips.Size.Width, Settings.Instance.VideoClips.Size.Height); for (int i = 0; i < sup.GetNoOfSubtitles(); i++) { DateTime startTime = sup.GetStartTime(i); DateTime endTime = sup.GetEndTime(i); // Used for the filename and the desision to save the image file. // Not used to set the time of the InfoLine because the shift is // also applied in WorkerSubs DateTime shiftedStartTime = sup.GetStartTime(i); DateTime shiftedEndTime = sup.GetEndTime(i); if (Settings.Instance.TimeShiftEnabled) { shiftedStartTime = UtilsSubs.shiftTiming(shiftedStartTime, Settings.Instance.Subs[SubsNum - 1].TimeShift); shiftedEndTime = UtilsSubs.shiftTiming(shiftedEndTime, Settings.Instance.Subs[SubsNum - 1].TimeShift); } string bitmapFile = string.Format("{0}_{1:000.}_Stream_{2:00.}_Subs{3}_{4:000.}.{5:00.}.{6:00.}-{7:000.}.{8:00.}.{9:00.}.png", Settings.Instance.DeckName, this.Episode, this.Stream, this.SubsNum, (int)shiftedStartTime.TimeOfDay.TotalMinutes, (int)shiftedStartTime.TimeOfDay.Seconds, (int)(shiftedStartTime.TimeOfDay.Milliseconds * 0.1), (int)shiftedEndTime.TimeOfDay.TotalMinutes, (int)shiftedEndTime.TimeOfDay.Seconds, (int)(shiftedEndTime.TimeOfDay.Milliseconds * 0.1)); DateTime spanStart = Settings.Instance.SpanStart; DateTime spanEnd = Settings.Instance.SpanEnd; // Create a image file for each line of dialog if ((this.WorkerVars.ProcessingType == WorkerVars.SubsProcessingType.Preview) || // Always save the image when previewing (!Settings.Instance.SpanEnabled) || // Always save the image when span is not enabled ((shiftedStartTime >= spanStart) && (shiftedEndTime <= spanEnd))) // When span is enabled, only save the images that are within the span { string imageSavePath = Path.Combine(this.WorkerVars.MediaDir, bitmapFile); sup.GetBitmap(i).Save(imageSavePath, System.Drawing.Imaging.ImageFormat.Png); } string prefixStr = name.createName(ConstantSettings.SrsVobsubFilenamePrefix, 0, 0, new DateTime(), new DateTime(), "", ""); string suffixStr = name.createName(ConstantSettings.SrsVobsubFilenameSuffix, 0, 0, new DateTime(), new DateTime(), "", ""); // Set the line of dialog to the bitmap file string text = String.Format("{0}{1}{2}", prefixStr, // {0} bitmapFile, // {1} suffixStr); // {2} InfoLine info = new InfoLine(startTime, endTime, text); lineInfos.Add(info); } lineInfos.Sort(); return(lineInfos); }
/// <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); }
/// <summary> /// Performs the work of the audio extraction thread. /// </summary> private void splitAudio(object sender, DoWorkEventArgs e) { WorkerVars workerVars = e.Argument as WorkerVars; List <List <InfoCombined> > combinedAll = new List <List <InfoCombined> >(); WorkerSubs subsWorker = new WorkerSubs(); if (groupBoxCheckLyrics.Checked) { // Parse and combine the subtitles try { combinedAll = subsWorker.combineAllSubs(workerVars, dialogProgress); if (combinedAll != null) { workerVars.CombinedAll = combinedAll; } else { e.Cancel = true; return; } } catch (Exception e1) { UtilsMsg.showErrMsg("Something went wrong before processing could start.\n" + e1); e.Cancel = true; return; } } DateTime mediaStartime = new DateTime(); DateTime mediaEndtime = new DateTime(); DateTime mediaDuration = new DateTime(); int episode = 0; DialogProgress.updateProgressInvoke(dialogProgress, 0, "Starting..."); foreach (string file in mediaFiles) { episode++; try { mediaEndtime = UtilsVideo.getVideoLength(file); } catch (Exception e1) { UtilsMsg.showErrMsg("Something went wrong while determining duration of the media:\n" + e1); return; } if (useSpan) { mediaStartime = spanStart; // If the span end time if not greater than the actual duration of the media if (spanEnd < mediaEndtime) { mediaEndtime = spanEnd; } } UtilsName name = new UtilsName( deckName, mediaFiles.Length, // Total number of episodes 1, // Total number of lines (Note: not filled out here) mediaEndtime, // Last time 0, 0 // Width and height (Note: not filled out anywhere) ); mediaDuration = UtilsSubs.getDurationTime(mediaStartime, mediaEndtime); string progressText = String.Format("Processing audio from media file {0} of {1}", episode, mediaFiles.Length); int progress = Convert.ToInt32((episode - 1) * (100.0 / mediaFiles.Length)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); // Enable detail mode in progress dialog DialogProgress.enableDetailInvoke(dialogProgress, true); // Set the duration of the clip in the progress dialog (for detail mode) DialogProgress.setDuration(dialogProgress, mediaDuration); string tempMp3Filename = Path.GetTempPath() + ConstantSettings.TempAudioFilename; UtilsAudio.ripAudioFromVideo(mediaFiles[episode - 1], audioStream.Num, mediaStartime, mediaEndtime, bitrate, tempMp3Filename, dialogProgress); DialogProgress.enableDetailInvoke(dialogProgress, false); if (dialogProgress.Cancel) { e.Cancel = true; File.Delete(tempMp3Filename); return; } int numClips = 1; if (!isSingleFile) { numClips = (int)Math.Ceiling((mediaDuration.TimeOfDay.TotalMilliseconds / (clipLength.TimeOfDay.TotalSeconds * 1000.0))); } for (int clipIdx = 0; clipIdx < numClips; clipIdx++) { progressText = String.Format("Splitting segment {0} of {1} from media file {2} of {3}", clipIdx + 1, numClips, episode, mediaFiles.Length); progress = Convert.ToInt32((episode - 1) * (100.0 / mediaFiles.Length)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); if (dialogProgress.Cancel) { e.Cancel = true; File.Delete(tempMp3Filename); return; } // The start and end times used for processing DateTime startTime = new DateTime(); DateTime endTime = new DateTime(); if (isSingleFile) { endTime = mediaDuration; } else { startTime = startTime.AddSeconds((double)(clipLength.TimeOfDay.TotalSeconds * clipIdx)); endTime = endTime.AddSeconds((double)(clipLength.TimeOfDay.TotalSeconds * (clipIdx + 1))); if (endTime.TimeOfDay.TotalMilliseconds >= mediaDuration.TimeOfDay.TotalMilliseconds) { endTime = mediaDuration; } } // The start and end times that will be displayed DateTime startTimeName = startTime.AddMilliseconds(mediaStartime.TimeOfDay.TotalMilliseconds); DateTime endTimeName = endTime.AddMilliseconds(mediaStartime.TimeOfDay.TotalMilliseconds); // Fill in the total number of lines with the total number of clips name.TotalNumLines = numClips; string nameStr = name.createName(ConstantSettings.ExtractMediaAudioFilenameFormat, episode + episodeStartNumber - 1, clipIdx + 1, startTimeName, endTimeName, "", ""); string outName = String.Format("{0}{1}{2}", outputDir, // {0} Path.DirectorySeparatorChar, // {1} nameStr); // {2} UtilsAudio.cutAudio(tempMp3Filename, startTime, endTime, outName); nameStr = name.createName(ConstantSettings.AudioId3Artist, episode + episodeStartNumber - 1, clipIdx + 1, startTimeName, endTimeName, "", ""); string tagArtist = String.Format("{0}", nameStr); // {0} nameStr = name.createName(ConstantSettings.AudioId3Album, episode + episodeStartNumber - 1, clipIdx + 1, startTimeName, endTimeName, "", ""); string tagAlbum = String.Format("{0}", nameStr); // {0} nameStr = name.createName(ConstantSettings.AudioId3Title, episode + episodeStartNumber - 1, clipIdx + 1, startTimeName, endTimeName, "", ""); string tagTitle = String.Format("{0}", nameStr); // {0} nameStr = name.createName(ConstantSettings.AudioId3Genre, episode + episodeStartNumber - 1, clipIdx + 1, startTimeName, endTimeName, "", ""); string tagGenre = String.Format("{0}", nameStr); // {0} string tagLyrics = ""; if (groupBoxCheckLyrics.Checked) { int totalLyricsLines = 0; int curLyricsNum = 1; // Precount the number of lyrics lines foreach (InfoCombined comb in combinedAll[episode - 1]) { if (comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds >= startTimeName.TimeOfDay.TotalMilliseconds && comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds <= endTimeName.TimeOfDay.TotalMilliseconds) { totalLyricsLines++; } } // Fill in the total number of lyrics lines name.TotalNumLines = curLyricsNum; // Foreach comb in the current episode, if the comb lies within the // current clip, add it to the lryics tag foreach (InfoCombined comb in combinedAll[episode - 1]) { if (comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds >= startTimeName.TimeOfDay.TotalMilliseconds && comb.Subs1.StartTime.TimeOfDay.TotalMilliseconds <= endTimeName.TimeOfDay.TotalMilliseconds) { tagLyrics += formatLyricsPair(comb, name, startTimeName, episode + episodeStartNumber - 1, curLyricsNum) + "\r\n"; curLyricsNum++; } } } UtilsAudio.tagAudio(outName, tagArtist, tagAlbum, tagTitle, tagGenre, tagLyrics, clipIdx + 1, numClips); } } return; }
/// <summary> /// Create the quick reference text file. /// </summary> private bool createQuickReference(WorkerVars workerVars, DialogProgress dialogProgress) { int totalLines = 0; int progessCount = 0; int episodeIdx = -1; int totalEpisodes = workerVars.CombinedAll.Count; int lineIdx = -1; DateTime lastTime = new DateTime(); foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { totalLines += combArray.Count; // Get the last time foreach (InfoCombined info in combArray) { if (info.Subs1.EndTime.TimeOfDay.TotalMilliseconds > lastTime.TimeOfDay.TotalMilliseconds) { lastTime = new DateTime(); lastTime = lastTime.AddMilliseconds(info.Subs1.EndTime.TimeOfDay.TotalMilliseconds); } } } UtilsName name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, 0, 0); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeIdx++; lineIdx = -1; string nameStr = name.createName(ConstantSettings.DuelingQuickRefFilenameFormat, Settings.Instance.EpisodeStartNumber + episodeIdx, 0, new DateTime(), new DateTime(), "", ""); // Create filename string subtitleFilename = String.Format("{0}{1}{2}", Settings.Instance.OutputDir, Path.DirectorySeparatorChar, nameStr); TextWriter subtitleWriter = new StreamWriter(subtitleFilename, false, Encoding.UTF8); /* * [0:00:31.14] This is the first line from Subs1 * [0:00:31.14] This is corresponding line from Subs2 * * [0:00:34.35] This is the second line from Subs1 * [0:00:34.35] This is corresponding line from Subs2 */ // For each line in episode foreach (InfoCombined comb in combArray) { progessCount++; lineIdx++; string dialogLine = formatQuickReferenceDialogPair(comb, name, Settings.Instance.EpisodeStartNumber + episodeIdx, progessCount); // Write line to file subtitleWriter.WriteLine(dialogLine); string progressText = string.Format("Generating quick reference file: line {0} of {1}", progessCount.ToString(), totalLines.ToString()); int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); if (dialogProgress.Cancel) { subtitleWriter.Close(); return(false); } } subtitleWriter.Close(); } return(true); }
/// <summary> /// Create the Dueling Subtitles .ass file. /// </summary> private bool createDuelingSubtitles(WorkerVars workerVars, DialogProgress dialogProgress) { int totalLines = 0; int progessCount = 0; int totalEpisodes = workerVars.CombinedAll.Count; int episodeIdx = -1; int lineIdx = -1; DateTime lastTime = new DateTime(); foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { totalLines += combArray.Count; // Get the last time foreach (InfoCombined info in combArray) { if (info.Subs1.EndTime.TimeOfDay.TotalMilliseconds > lastTime.TimeOfDay.TotalMilliseconds) { lastTime = new DateTime(); lastTime = lastTime.AddMilliseconds(info.Subs1.EndTime.TimeOfDay.TotalMilliseconds); } } } UtilsName name = new UtilsName(Settings.Instance.DeckName, totalEpisodes, totalLines, lastTime, 0, 0); // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { episodeIdx++; lineIdx = -1; string nameStr = name.createName(ConstantSettings.DuelingSubtitleFilenameFormat, Settings.Instance.EpisodeStartNumber + episodeIdx, 0, new DateTime(), new DateTime(), "", ""); // Create filename string subtitleFilename = String.Format("{0}{1}{2}", Settings.Instance.OutputDir, Path.DirectorySeparatorChar, nameStr); TextWriter subtitleWriter = new StreamWriter(subtitleFilename, false, Encoding.UTF8); /* * Example .ass file: * ; Generated with subs2srs - http://sourceforge.net/projects/subs2srs/ * * [Script Info] * Title:Some_Title_001 * ScriptType:v4.00+ * Collisions:Normal * Timer:100.0000 * * [V4+ Styles] * Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding * Style: Subs1,Meiryo,18,&H00F9EDFF,&H0000FFFF,&H3C2B0E17,&HA0000000,0,0,0,0,84,100,0,0,1,2,1,2,10,10,15,1 * Style: Subs2,Meiryo,18,&H00F9EDFF,&H0000FFFF,&H3C2B0E17,&HA0000000,0,0,0,0,84,100,0,0,1,2,1,2,10,10,15,1 * * [Events] * Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text * * Dialogue: 0,0:00:31.14,0:00:36.16,Subs1,NTP,0000,0000,0000,,This is the first line from Subs1 * Dialogue: 0,0:00:31.14,0:00:36.16,Subs2,NTP,0000,0000,0000,,This is corresponding line from Subs2 * * etc. */ // [Script Info] string scriptInfoText = formatScriptInfoText(Settings.Instance.EpisodeStartNumber + episodeIdx); subtitleWriter.WriteLine(scriptInfoText); // [V4+ Styles] string stylesText = formatSytlesText(); subtitleWriter.WriteLine(stylesText); // [Events] (header) string eventsHeaderText = formatEventHeaderText(); subtitleWriter.WriteLine(eventsHeaderText); // For each line in episode, [Events] Dialog: ... foreach (InfoCombined comb in combArray) { progessCount++; lineIdx++; string dialogLine = ""; dialogLine = formatDialogPair(workerVars.CombinedAll, episodeIdx, lineIdx); // Write line to file subtitleWriter.WriteLine(dialogLine); string progressText = string.Format("Generating Subtitle file: line {0} of {1}", progessCount.ToString(), totalLines.ToString()); int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); if (dialogProgress.Cancel) { subtitleWriter.Close(); return(false); } } subtitleWriter.Close(); } return(true); }