/// <summary> /// Get a list of consecutive Subs2 repeats. /// </summary> private List <InfoCombined> findConsecutiveRepeats(List <InfoCombined> combList) { List <InfoCombined> subs2Repeats = new List <InfoCombined>(); InfoCombined prevComb = new InfoCombined(new InfoLine(), new InfoLine()); //TextWriter writer = new StreamWriter(@"repeats.txt", false, Encoding.UTF8); // For debug foreach (InfoCombined comb in combList) { if (comb.Subs2.Text == prevComb.Subs2.Text && comb.Subs2.StartTime == prevComb.Subs2.StartTime) { subs2Repeats.Add(comb); //writer.WriteLine("******** REPEATED LINE: ********: " + comb.Subs1.Text + " " + comb.Subs2.Text); } else { //writer.WriteLine(comb.Subs1.Text + " " + comb.Subs2.Text); prevComb = comb; } } //writer.Close(); return(subs2Repeats); }
/// <summary> /// Format a audio clip. /// </summary> private string formatAudioClip(InfoCombined comb, int episodeIndex) { DateTime startTime; DateTime endTime; string outText = ""; // Apply pad (if requested) if (Settings.Instance.AudioClips.PadEnabled) { startTime = UtilsSubs.applyTimePad(comb.Subs1.StartTime, -Settings.Instance.AudioClips.PadStart); endTime = UtilsSubs.applyTimePad(comb.Subs1.EndTime, Settings.Instance.AudioClips.PadEnd); } else { startTime = comb.Subs1.StartTime; endTime = comb.Subs1.EndTime; } string prefixStr = name.createName(ConstantSettings.SrsAudioFilenamePrefix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string nameStr = name.createName(ConstantSettings.AudioFilenameFormat, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string suffixStr = name.createName(ConstantSettings.SrsAudioFilenameSuffix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); outText = $"{prefixStr}{nameStr}{suffixStr}"; // {2} return(outText); }
/// <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> /// Format a pair of .ass dialog text lines. /// </summary> private string formatDialogPair(List <List <InfoCombined> > combinedAll, int episodeIndex, int lineIndex) { List <InfoCombined> combArray = combinedAll[episodeIndex]; InfoCombined comb = combArray[lineIndex]; string pair = ""; // Draw Subs1 or Subs2 first based on the priority selected by the user if (isSubs1First) { pair += formatDialogSingle(true, comb); if (lineIndex % duelPattern == 0) { pair += "\n"; pair += formatDialogSingle(false, comb); } } else { if (lineIndex % duelPattern == 0) { pair += formatDialogSingle(false, comb); pair += "\n"; } pair += formatDialogSingle(true, comb); } return(pair); }
/// <summary> /// Format a single line of .ass dialog text. /// </summary> private string formatDialogSingle(bool isSubs1, InfoCombined comb) { string name = ""; string text = ""; string single = ""; if (isSubs1) { name = "Subs1"; text = comb.Subs1.Text; } else { name = "Subs2"; text = comb.Subs2.Text; } // Example: // Dialogue: 0,0:00:31.14,0:00:36.16,Subs1,NA,0000,0000,0000,,This is the first line from Subs1 single = String.Format("Dialogue: 0,{0},{1},{2},NA,0000,0000,0000,,{3}", UtilsSubs.formatAssTime(comb.Subs1.StartTime), UtilsSubs.formatAssTime(comb.Subs1.EndTime), name, text); return(single); }
/// <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> /// Set the onlyNeededForContext flag for lines that are inactive but needed for context purposes. /// Call inactivateLines() before calling this routine. /// </summary> public List <List <InfoCombined> > markLinesOnlyNeededForContext(WorkerVars workerVars, DialogProgress dialogProgress) { int totalLines = 0; int progessCount = 0; foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { totalLines += combArray.Count; } // For each episode foreach (List <InfoCombined> combArray in workerVars.CombinedAll) { // For each line in episode for (int lineIdx = 0; lineIdx < combArray.Count; lineIdx++) { InfoCombined comb = combArray[lineIdx]; progessCount++; if (comb.Active) { // Leading for (int i = Math.Max(0, lineIdx - Settings.Instance.ContextLeadingCount); i < lineIdx; i++) { if (!combArray[i].Active) { combArray[i].OnlyNeededForContext = true; } } // Trailing for (int i = Math.Min(combArray.Count - 1, lineIdx + 1); i < Math.Min(combArray.Count - 1, lineIdx + Settings.Instance.ContextTrailingCount + 1); i++) { if (!combArray[i].Active) { combArray[i].OnlyNeededForContext = true; } } } string progressText = $"Find lines needed for context: {Convert.ToInt32(progessCount * (100.0 / totalLines))}%"; int progress = Convert.ToInt32(progessCount * (100.0 / totalLines)); DialogProgress.updateProgressInvoke(dialogProgress, progress, progressText); if (dialogProgress.Cancel) { return(null); } } } return(workerVars.CombinedAll); }
/// <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> /// Is the specified line too long as determined by the "Long Clip Warning" preference? /// </summary> private bool isLineTooLong(InfoCombined comb) { DateTime durationTime = UtilsSubs.getDurationTime(comb.Subs1.StartTime, comb.Subs1.EndTime); if ((ConstantSettings.LongClipWarningSeconds > 0) && (durationTime.TimeOfDay.TotalMilliseconds > (ConstantSettings.LongClipWarningSeconds * 1000))) { return(true); } return(false); }
/// <summary> /// Format Subs2 text. /// </summary> private string formatSubs2(InfoCombined comb, int episodeIndex) { DateTime startTime = comb.Subs1.StartTime; DateTime endTime = comb.Subs1.EndTime; string outText = ""; string nameStr = name.createName(ConstantSettings.SrsSubs2Format, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); outText = $"{nameStr}"; // {0} return(outText); }
/// <summary> /// Reprocess the subtitles and discard active/inactive and text changes made by the user. /// </summary> private void buttonRegenerate_Click(object sender, EventArgs e) { string oldSubs1 = "-----"; string oldSubs2 = "-----"; if (changesHaveBeenMade) { bool confirmed = UtilsMsg.showConfirm("Regenerate the preview and discard the changes that you have made?"); if (!confirmed) { return; } } changesHaveBeenMade = false; // Save the currently selected item if (currentInfoComb != null) { InfoCombined selectedComb = currentInfoComb; oldSubs1 = selectedComb.Subs1.Text; oldSubs2 = selectedComb.Subs2.Text; } generatePreview(); int indexOfItemToSelect = 0; // Try to select the saved item for (int i = 0; i < listViewLines.Items.Count; i++) { InfoCombined curComb = (InfoCombined)listViewLines.Items[i].Tag; if (curComb.Subs1.Text == oldSubs1 && curComb.Subs2.Text == oldSubs2) { indexOfItemToSelect = i; break; } } if (listViewLines.Items.Count > 0) { listViewLines.Focus(); listViewLines.Items[indexOfItemToSelect].Selected = true; listViewLines.EnsureVisible(indexOfItemToSelect); } }
/// <summary> /// Format the tag. /// </summary> private string formatTag(InfoCombined comb, int episodeIndex) { string outText = ""; DateTime startTime = comb.Subs1.StartTime; DateTime endTime = comb.Subs1.EndTime; string nameStr = name.createName(ConstantSettings.SrsTagFormat, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); outText = String.Format("{0}", nameStr); return(outText); }
private void updateGuiWithInfo(InfoCombined selectedComb) { if (selectedComb == null) { return; } currentInfoComb = selectedComb; updateTextPreview(currentInfoComb); updateImagePreview(currentInfoComb); audioPreviewNeedsUpdating = true; this.textBoxTimings.Text = this.formatTimeForDisplay(currentInfoComb.Subs1.StartTime) + " - " + this.formatTimeForDisplay(currentInfoComb.Subs1.EndTime); }
/// <summary> /// Subtitle matching Pass 1. Match each line in Subs1 to the closest line is Subs2 (based on timestamp). /// </summary> private List <InfoCombined> pass1CombineSubs(List <InfoLine> subs1LineList, List <InfoLine> subs2LineList) { List <InfoCombined> combinedSubs = new List <InfoCombined>(); // Pass 1: Pair Subs1 and Subs2 based on timestamp foreach (InfoLine info in subs1LineList) { InfoCombined combInfo = getCombinedOverlap(info, subs2LineList); // If a match was found if (combInfo != null) { combinedSubs.Add(combInfo); } } return(combinedSubs); }
/// <summary> /// Format a video clip. /// </summary> private string formatVideoClip(InfoCombined comb, int episodeIndex) { DateTime startTime; DateTime endTime; string outText = ""; // Apply pad (if requested) if (Settings.Instance.VideoClips.PadEnabled) { startTime = UtilsSubs.applyTimePad(comb.Subs1.StartTime, -Settings.Instance.VideoClips.PadStart); endTime = UtilsSubs.applyTimePad(comb.Subs1.EndTime, Settings.Instance.VideoClips.PadEnd); } else { startTime = comb.Subs1.StartTime; endTime = comb.Subs1.EndTime; } string videoExtension = ".avi"; if (Settings.Instance.VideoClips.IPodSupport) { videoExtension = ".mp4"; } string prefixStr = name.createName(ConstantSettings.SrsVideoFilenamePrefix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string nameStr = name.createName(ConstantSettings.VideoFilenameFormat, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string suffixStr = name.createName(ConstantSettings.SrsVideoFilenameSuffix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); outText = String.Format("{0}{1}{2}{3}", prefixStr, // {0} nameStr, // {1} videoExtension, // {2} suffixStr); // {3} return(outText); }
/// <summary> /// Update the image preview. /// </summary> private void updateImagePreview(InfoCombined selectedComb) { if (selectedComb == null) { return; } // Update the snapshot if (Settings.Instance.VideoClips.Files.Length > 0 && checkBoxSnapshotPreview.Checked) { string videoFileName = Settings.Instance.VideoClips.Files[comboBoxEpisode.SelectedIndex]; DateTime midTime = UtilsSubs.getMidpointTime(selectedComb.Subs1.StartTime, selectedComb.Subs1.EndTime); string outFile = previewWorkerVars.MediaDir + Path.DirectorySeparatorChar + ConstantSettings.TempImageFilename; try { File.Delete(outFile); } catch { // File is locked return; } UtilsSnapshot.takeSnapshotFromVideo(videoFileName, midTime, Settings.Instance.Snapshots.Size, Settings.Instance.Snapshots.Crop, outFile); if (File.Exists(outFile)) { Image image = UtilsSnapshot.getImageFromFile(outFile); image = UtilsSnapshot.fitImageToSize(image, new Size(pictureBoxImage.Width, pictureBoxImage.Height)); pictureBoxImage.Image = image; } } }
/// <summary> /// Format a snapshot. /// </summary> private string formatSnapshot(InfoCombined comb, int episodeIndex) { DateTime startTime = comb.Subs1.StartTime; DateTime endTime = comb.Subs1.EndTime; DateTime midTime = UtilsSubs.getMidpointTime(comb.Subs1.StartTime, comb.Subs1.EndTime); string outText = ""; string prefixStr = name.createName(ConstantSettings.SrsSnapshotFilenamePrefix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string nameStr = name.createName(ConstantSettings.SnapshotFilenameFormat, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); string suffixStr = name.createName(ConstantSettings.SrsSnapshotFilenameSuffix, episodeIndex + Settings.Instance.EpisodeStartNumber, progessCount, startTime, endTime, comb.Subs1.Text, comb.Subs2.Text); outText = $"{prefixStr}{nameStr}{suffixStr}"; // {2} return(outText); }
/// <summary> /// Set the list to the lines of the provided episode. /// </summary> private void populateLinesListView(int episodeIndex) { if (previewWorkerVars == null) { return; } this.listViewLines.BeginUpdate(); this.listViewLines.Items.Clear(); // Create a new row for each comb foreach (InfoCombined comb in previewWorkerVars.CombinedAll[episodeIndex]) { // Create the text for both columns string[] entryToAdd = new String[5]; entryToAdd[0] = comb.Subs1.Text; if (Settings.Instance.Subs[1].Files.Length != 0) { entryToAdd[1] = comb.Subs2.Text; } entryToAdd[2] = this.formatTimeForDisplay(comb.Subs1.StartTime); entryToAdd[3] = this.formatTimeForDisplay(comb.Subs1.EndTime); if (this.isLineTooLong(comb)) { entryToAdd[4] = this.formatTimeForDisplay(UtilsSubs.getDurationTime(comb.Subs1.StartTime, comb.Subs1.EndTime)) + " (Long!)"; } else { entryToAdd[4] = this.formatTimeForDisplay(UtilsSubs.getDurationTime(comb.Subs1.StartTime, comb.Subs1.EndTime)); } ListViewItem item = new ListViewItem(entryToAdd); // Embed a comb into each item item.Tag = comb; this.listViewLines.Items.Add(item); } for (int i = 0; i < this.listViewLines.Items.Count; i++) { InfoCombined comb = (InfoCombined)this.listViewLines.Items[i].Tag; if (comb.Active) { this.listViewLines.Items[i].BackColor = activeColor; } else { this.listViewLines.Items[i].BackColor = inactiveColor; } if (this.isLineTooLong(comb)) { this.listViewLines.Items[i].ForeColor = longLineWarningColor; } else { this.listViewLines.Items[i].ForeColor = Color.Black; } } this.listViewLines.EndUpdate(); }
private void backgroundWorkerAudio_DoWork(object sender, DoWorkEventArgs e) { InfoCombined selectedComb = currentInfoComb; DateTime startTime = selectedComb.Subs1.StartTime; DateTime endTime = selectedComb.Subs1.EndTime; string outMp3File = Path.Combine(previewWorkerVars.MediaDir, ConstantSettings.TempAudioFilename); string outWavFile = Path.Combine(previewWorkerVars.MediaDir, ConstantSettings.TempAudioPreviewFilename); if (audioPreviewNeedsUpdating) { audioPreviewNeedsUpdating = false; if (Settings.Instance.AudioClips.UseAudioFromVideo) { if (Settings.Instance.VideoClips.Files.Length > 0) { string videoFileName = Settings.Instance.VideoClips.Files[selectedEpisodeIndex]; // Apply pad (if requested) if (Settings.Instance.AudioClips.PadEnabled) { startTime = UtilsSubs.applyTimePad(selectedComb.Subs1.StartTime, -Settings.Instance.AudioClips.PadStart); endTime = UtilsSubs.applyTimePad(selectedComb.Subs1.EndTime, Settings.Instance.AudioClips.PadEnd); } UtilsAudio.ripAudioFromVideo(videoFileName, Settings.Instance.VideoClips.AudioStream.Num, startTime, endTime, Settings.Instance.AudioClips.Bitrate, outMp3File, null); } } else // Audio file is provided { if (Settings.Instance.AudioClips.Files.Length > 0) { string fileToCut = Settings.Instance.AudioClips.Files[selectedEpisodeIndex]; bool inputFileIsMp3 = (Path.GetExtension(fileToCut).ToLower() == ".mp3"); // 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); } if (ConstantSettings.ReencodeBeforeSplittingAudio || !inputFileIsMp3) { UtilsAudio.ripAudioFromVideo(fileToCut, "0", startTime, endTime, Settings.Instance.AudioClips.Bitrate, outMp3File, null); } else { UtilsAudio.cutAudio(fileToCut, startTime, endTime, outMp3File); } } } // If no external audio player is specified, convert the .mp3 to .wav so that it can be played with SoundPlayer if (File.Exists(outMp3File)) { UtilsAudio.convertAudioFormat(outMp3File, outWavFile, 2); } } }
/// <summary> /// Update the Subs1 and Subs2 text boxes with the provided combined info. /// </summary> private void updateTextPreview(InfoCombined selectedComb) { if (selectedComb == null) { return; } if (selectedComb.Active) { this.textBoxSubs1.BackColor = activeColor; this.textBoxSubs2.BackColor = activeColor; this.pictureBoxSubs1.BackColor = activeColor; this.pictureBoxSubs2.BackColor = activeColor; this.panelSubs1.BackColor = activeColor; this.panelSubs2.BackColor = activeColor; } else { this.textBoxSubs1.BackColor = inactiveColor; this.textBoxSubs2.BackColor = inactiveColor; this.pictureBoxSubs1.BackColor = inactiveColor; this.pictureBoxSubs2.BackColor = inactiveColor; this.panelSubs1.BackColor = inactiveColor; this.panelSubs2.BackColor = inactiveColor; } // Disable subs2 textbox if there aren't any subs2 files this.textBoxSubs2.Enabled = (Settings.Instance.Subs[1].Files.Length != 0); this.pictureBoxSubs2.Enabled = (Settings.Instance.Subs[1].Files.Length != 0); this.panelSubs2.Enabled = (Settings.Instance.Subs[1].Files.Length != 0); if (selectedComb.Subs1.Text.EndsWith("png" + ConstantSettings.SrsVobsubFilenameSuffix)) { this.textBoxSubs1.Visible = false; this.pictureBoxSubs1.Visible = true; try { // Multiple vobsub image can be shown in a single line, so extract each image // and concatenate them before displaying. List <string> vobsubImages = UtilsSubs.extractVobsubFilesFromText(selectedComb.Subs1.Text); List <Image> vobSubImages = new List <Image>(); foreach (string vobsubImage in vobsubImages) { Image image = UtilsSnapshot.getImageFromFile(previewWorkerVars.MediaDir + Path.DirectorySeparatorChar + vobsubImage); vobSubImages.Add(image); } if (vobSubImages.Count != 0) { Image image; if (vobSubImages.Count == 0) { image = vobSubImages[0]; } else { image = UtilsSnapshot.concatImages(vobSubImages); } this.pictureBoxSubs1.Image = image; } } catch { // Don't care } } else { this.textBoxSubs1.Visible = true; this.pictureBoxSubs1.Visible = false; this.textBoxSubs1.Text = selectedComb.Subs1.Text; } if (Settings.Instance.Subs[1].Files.Length != 0) { if (selectedComb.Subs2.Text.EndsWith("png" + ConstantSettings.SrsVobsubFilenameSuffix)) { this.textBoxSubs2.Visible = false; this.pictureBoxSubs2.Visible = true; try { // Multiple vobsub image can be shown in a single line, so extract each image // and concatenate them before displaying. List <string> vobsubImages = UtilsSubs.extractVobsubFilesFromText(selectedComb.Subs2.Text); List <Image> vobSubImages = new List <Image>(); foreach (string vobsubImage in vobsubImages) { Image image = UtilsSnapshot.getImageFromFile(previewWorkerVars.MediaDir + Path.DirectorySeparatorChar + vobsubImage); vobSubImages.Add(image); } if (vobSubImages.Count != 0) { Image image; if (vobSubImages.Count == 0) { image = vobSubImages[0]; } else { image = UtilsSnapshot.concatImages(vobSubImages); } this.pictureBoxSubs2.Image = image; } } catch { } } else { this.textBoxSubs2.Visible = true; this.pictureBoxSubs2.Visible = false; this.textBoxSubs2.Text = selectedComb.Subs2.Text; } } else { this.textBoxSubs2.Visible = true; this.pictureBoxSubs2.Visible = false; this.textBoxSubs2.Text = ""; } }
/// <summary> /// When Subs1 and Subs2 have different number of lines or the timings are mismatched, /// then lines from Subs2 can be skipped. This routine will combine the skipped lines with /// their closest match based on timestamp. /// /// Example: /// /// Subs1_A --> Subs2_A /// Subs1_B --> Subs2_C ; Subs2_B was skipped! /// Subs1_C --> Subs2_D /// Subs1_D --> Subs2_E /// /// Transform the above into: /// /// Subs1_A --> Subs2_A Subs2_B /// Subs1_B --> Subs2_C /// Subs1_C --> Subs2_D /// Subs1_D --> Subs2_E /// /// or: /// /// Subs1_A --> Subs2_A /// Subs1_B --> Subs2_B Subs2_C /// Subs1_C --> Subs2_D /// Subs1_D --> Subs2_E /// /// </summary> private List <InfoCombined> combineSkipped(List <InfoLine> subs2LineList, List <InfoCombined> combList) { // Get list of skipped lines List <InfoLine> skippedList = findSkippedLines(subs2LineList, combList); Dictionary <InfoCombined, List <InfoLine> > skipDic = new Dictionary <InfoCombined, List <InfoLine> >(); // Associate each skipped line with the closest non-skipped line in the Subs1 list foreach (InfoLine skip in skippedList) { InfoCombined bestMatch = new InfoCombined(new InfoLine(), new InfoLine()); double bestOverlap = -999999999999999999.0; foreach (InfoCombined comb in combList) { double overlap = UtilsSubs.getOverlap(skip.StartTime, skip.EndTime, comb.Subs1.StartTime, comb.Subs1.EndTime); if (overlap >= bestOverlap) { bestMatch = comb; bestOverlap = overlap; } } // If user wants to, toss out Subs2 with no obvious Subs1 counterpart if (Settings.Instance.Subs[1].RemoveNoCounterpart && bestOverlap < 0.0) { continue; } else { // Because multiple skipped lines can match the same line from Sub1, // add to dictionary so that they can be sorted lateer if (skipDic.ContainsKey(bestMatch)) { skipDic[bestMatch].Add(skip); } else { // Add Subs1 line to dictionary as the key skipDic.Add(bestMatch, new List <InfoLine>()); // Add the best matched line's Subs2 line skipDic[bestMatch].Add(bestMatch.Subs2); // Add the original skipped line skipDic[bestMatch].Add(skip); } } } // Sort the matched lines (for the case where multiple skipped lines match the same Subs1 line) foreach (List <InfoLine> infoList in skipDic.Values) { infoList.Sort(); } // Now change text and time info from the skipped lines into the comb list foreach (InfoCombined comb in skipDic.Keys) { string subs2Text = ""; // Combine the text foreach (InfoLine infoList in skipDic[comb]) { subs2Text += infoList.Text + " "; } comb.Subs2.Text = subs2Text.Trim(); comb.Subs2.StartTime = skipDic[comb][0].StartTime; int numInfoElements = skipDic[comb].Count - 1; comb.Subs2.EndTime = skipDic[comb][numInfoElements].EndTime; } return(combList); }
/// <summary> /// Format a line for the SRS import file. /// </summary> private string formatAll(List <List <InfoCombined> > combinedAll, int episodeIndex, int lineIndex, FormatType formatType) { List <InfoCombined> combArray = combinedAll[episodeIndex]; InfoCombined comb = combArray[lineIndex]; string outText = ""; // Add tag and sequence marker if (formatType == FormatType.Normal) { if (ConstantSettings.SrsTagFormat != "") { outText += formatTag(comb, episodeIndex); } if (ConstantSettings.SrsTagFormat != "" && ConstantSettings.SrsSequenceMarkerFormat != "") { outText += ConstantSettings.SrsDelimiter; } if (ConstantSettings.SrsSequenceMarkerFormat != "") { outText += formatSequenceMarker(comb, episodeIndex); } } // Add audio clip if (Settings.Instance.AudioClips.Enabled) { if (formatType == FormatType.Normal || formatType == FormatType.Leading && Settings.Instance.ContextLeadingIncludeAudioClips || formatType == FormatType.Trailing && Settings.Instance.ContextTrailingIncludeAudioClips) { outText += ConstantSettings.SrsDelimiter + formatAudioClip(comb, episodeIndex); } } // Add snapshot clip if (Settings.Instance.Snapshots.Enabled) { if (formatType == FormatType.Normal || formatType == FormatType.Leading && Settings.Instance.ContextLeadingIncludeSnapshots || formatType == FormatType.Trailing && Settings.Instance.ContextTrailingIncludeSnapshots) { outText += ConstantSettings.SrsDelimiter + formatSnapshot(comb, episodeIndex); } } // Add video clip if (Settings.Instance.VideoClips.Enabled) { if (formatType == FormatType.Normal || formatType == FormatType.Leading && Settings.Instance.ContextLeadingIncludeVideoClips || formatType == FormatType.Trailing && Settings.Instance.ContextTrailingIncludeVideoClips) { outText += ConstantSettings.SrsDelimiter + formatVideoClip(comb, episodeIndex); } } // Add line from Subs1 outText += ConstantSettings.SrsDelimiter + formatSubs1(comb, episodeIndex); // Add line from Subs2 if (Settings.Instance.Subs[1].FilePattern != "") { outText += ConstantSettings.SrsDelimiter + formatSubs2(comb, episodeIndex); } // Remove any initial tab if (formatType == FormatType.Normal) { if (outText.StartsWith("\t")) { outText = outText.Substring(1); } } return(outText); }
/// <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); }