From 3892288695fca47cc6a5041cefad209b083754bd Mon Sep 17 00:00:00 2001 From: Ivandro Jao Date: Wed, 24 Apr 2024 11:02:26 +0100 Subject: [PATCH 01/28] Refactor list view item checking actions The list view item checking actions, such as "Select All" and "Invert Selection", have been refactored to their own methods (CheckAll, InvertCheck) to reduce repetition. These methods have been implemented in numerous forms across the code. This makes the codebase cleaner and more maintainable as the operations are abstracted out to separate methods. Signed-off-by: Ivandro Jao --- src/ui/Forms/ApplyDurationLimits.cs | 16 ++------- ...tationAlphaStylesCategoriesImportExport.cs | 16 ++------- src/ui/Forms/AutoBreakUnbreakLines.cs | 10 ++---- src/ui/Forms/BatchConvert.cs | 10 ++---- src/ui/Forms/ChangeCasingNames.cs | 10 ++---- src/ui/Forms/FixCommonErrors.cs | 36 ++++--------------- src/ui/Forms/MergeShortLines.cs | 10 ++---- src/ui/Forms/MergeTextWithSameTimeCodes.cs | 10 ++---- src/ui/Forms/ModifySelection.cs | 10 ++---- src/ui/Forms/MultipleReplace.cs | 20 +++-------- src/ui/Forms/MultipleReplaceExportImport.cs | 10 ++---- src/ui/Forms/Ocr/BinaryOcrTrain.cs | 7 +--- src/ui/Forms/Ocr/VobSubCharactersImport.cs | 6 +--- src/ui/Forms/Options/SettingsProfileExport.cs | 10 ++---- src/ui/Forms/SplitLongLines.cs | 10 ++---- .../Styles/SubStationAlphaStylesExport.cs | 10 ++---- src/ui/Forms/VTT/WebVttImportExport.cs | 10 ++---- src/ui/Forms/VTT/WebVttStylePicker.cs | 10 ++---- src/ui/Logic/UiUtil.cs | 30 ++++++++++++++++ 19 files changed, 70 insertions(+), 181 deletions(-) diff --git a/src/ui/Forms/ApplyDurationLimits.cs b/src/ui/Forms/ApplyDurationLimits.cs index e0beff8c3..efb6d40b6 100644 --- a/src/ui/Forms/ApplyDurationLimits.cs +++ b/src/ui/Forms/ApplyDurationLimits.cs @@ -338,21 +338,9 @@ namespace Nikse.SubtitleEdit.Forms } } - private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) - { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } - } + private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) => listViewFixes.CheckAll(); - private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) - { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } - } + private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) => listViewFixes.InvertCheck(); private void checkBoxCheckShotChanges_CheckedChanged(object sender, EventArgs e) { diff --git a/src/ui/Forms/Assa/SubStationAlphaStylesCategoriesImportExport.cs b/src/ui/Forms/Assa/SubStationAlphaStylesCategoriesImportExport.cs index 5b0e01072..2f8664d93 100644 --- a/src/ui/Forms/Assa/SubStationAlphaStylesCategoriesImportExport.cs +++ b/src/ui/Forms/Assa/SubStationAlphaStylesCategoriesImportExport.cs @@ -125,20 +125,8 @@ namespace Nikse.SubtitleEdit.Forms.Assa } } - private void ToolStripMenuItemSelectAll_Click(object sender, EventArgs e) - { - foreach (ListViewItem item in listViewCategories.Items) - { - item.Checked = true; - } - } + private void ToolStripMenuItemSelectAll_Click(object sender, EventArgs e) => listViewCategories.CheckAll(); - private void ToolStripMenuItemInverseSelection_Click(object sender, EventArgs e) - { - foreach (ListViewItem item in listViewCategories.Items) - { - item.Checked = !item.Checked; - } - } + private void ToolStripMenuItemInverseSelection_Click(object sender, EventArgs e) => listViewCategories.InvertCheck(); } } diff --git a/src/ui/Forms/AutoBreakUnbreakLines.cs b/src/ui/Forms/AutoBreakUnbreakLines.cs index 430b6f3fa..08a185aa7 100644 --- a/src/ui/Forms/AutoBreakUnbreakLines.cs +++ b/src/ui/Forms/AutoBreakUnbreakLines.cs @@ -251,18 +251,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void AutoBreakUnbreakLines_FormClosing(object sender, FormClosingEventArgs e) diff --git a/src/ui/Forms/BatchConvert.cs b/src/ui/Forms/BatchConvert.cs index 6fefb758c..9e8ec11fc 100644 --- a/src/ui/Forms/BatchConvert.cs +++ b/src/ui/Forms/BatchConvert.cs @@ -3977,18 +3977,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewConvertOptions.Items) - { - item.Checked = true; - } + listViewConvertOptions.CheckAll(); } private void inverseSelectionToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewConvertOptions.Items) - { - item.Checked = !item.Checked; - } + listViewConvertOptions.InvertCheck(); } private void listViewInputFiles_ColumnClick(object sender, ColumnClickEventArgs e) diff --git a/src/ui/Forms/ChangeCasingNames.cs b/src/ui/Forms/ChangeCasingNames.cs index 5fb3da6ed..f796880ce 100644 --- a/src/ui/Forms/ChangeCasingNames.cs +++ b/src/ui/Forms/ChangeCasingNames.cs @@ -335,18 +335,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void toolStripMenuItem1SelectAll_Click(object sender, EventArgs e) diff --git a/src/ui/Forms/FixCommonErrors.cs b/src/ui/Forms/FixCommonErrors.cs index ea9b2a18f..65d35f812 100644 --- a/src/ui/Forms/FixCommonErrors.cs +++ b/src/ui/Forms/FixCommonErrors.cs @@ -1145,21 +1145,9 @@ namespace Nikse.SubtitleEdit.Forms listViewFixes.Sort(); } - private void ButtonSelectAllClick(object sender, EventArgs e) - { - foreach (ListViewItem item in listView1.Items) - { - item.Checked = true; - } - } + private void ButtonSelectAllClick(object sender, EventArgs e) => listView1.CheckAll(); - private void ButtonInverseSelectionClick(object sender, EventArgs e) - { - foreach (ListViewItem item in listView1.Items) - { - item.Checked = !item.Checked; - } - } + private void ButtonInverseSelectionClick(object sender, EventArgs e) => listView1.InvertCheck(); private void ListViewFixesSelectedIndexChanged(object sender, EventArgs e) { @@ -1370,18 +1358,12 @@ namespace Nikse.SubtitleEdit.Forms private void ButtonFixesSelectAllClick(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void ButtonFixesInverseClick(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void ButtonFixesApplyClick(object sender, EventArgs e) @@ -1948,18 +1930,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void setCurrentFixesAsDefaultToolStripMenuItem_Click(object sender, EventArgs e) diff --git a/src/ui/Forms/MergeShortLines.cs b/src/ui/Forms/MergeShortLines.cs index 71ff16387..25e2d662c 100644 --- a/src/ui/Forms/MergeShortLines.cs +++ b/src/ui/Forms/MergeShortLines.cs @@ -346,18 +346,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } } } diff --git a/src/ui/Forms/MergeTextWithSameTimeCodes.cs b/src/ui/Forms/MergeTextWithSameTimeCodes.cs index 7d10bb2fb..3ce97098b 100644 --- a/src/ui/Forms/MergeTextWithSameTimeCodes.cs +++ b/src/ui/Forms/MergeTextWithSameTimeCodes.cs @@ -297,18 +297,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void checkBoxMakeDialog_CheckedChanged(object sender, EventArgs e) diff --git a/src/ui/Forms/ModifySelection.cs b/src/ui/Forms/ModifySelection.cs index 2419ff755..a25b2b9c9 100644 --- a/src/ui/Forms/ModifySelection.cs +++ b/src/ui/Forms/ModifySelection.cs @@ -642,18 +642,12 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void ModifySelection_Resize(object sender, EventArgs e) diff --git a/src/ui/Forms/MultipleReplace.cs b/src/ui/Forms/MultipleReplace.cs index bc42e7280..a7ab11a9e 100644 --- a/src/ui/Forms/MultipleReplace.cs +++ b/src/ui/Forms/MultipleReplace.cs @@ -570,18 +570,12 @@ namespace Nikse.SubtitleEdit.Forms private void buttonReplacesSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); } private void buttonReplacesInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); } private void contextMenuStrip1_Opening(object sender, System.ComponentModel.CancelEventArgs e) @@ -1446,18 +1440,12 @@ namespace Nikse.SubtitleEdit.Forms private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewRules.Items) - { - item.Checked = true; - } + listViewRules.CheckAll(); } private void inverseSelectionToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewRules.Items) - { - item.Checked = !item.Checked; - } + listViewRules.InvertCheck(); } private void ContextMenuStripListViewFixesOpening(object sender, System.ComponentModel.CancelEventArgs e) diff --git a/src/ui/Forms/MultipleReplaceExportImport.cs b/src/ui/Forms/MultipleReplaceExportImport.cs index f1886962a..e71207e71 100644 --- a/src/ui/Forms/MultipleReplaceExportImport.cs +++ b/src/ui/Forms/MultipleReplaceExportImport.cs @@ -136,18 +136,12 @@ namespace Nikse.SubtitleEdit.Forms private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = true; - } + listViewExportStyles.CheckAll(); } private void inverseSelectionToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = !item.Checked; - } + listViewExportStyles.InvertCheck(); } } } diff --git a/src/ui/Forms/Ocr/BinaryOcrTrain.cs b/src/ui/Forms/Ocr/BinaryOcrTrain.cs index 096bcc4c0..675dd8757 100644 --- a/src/ui/Forms/Ocr/BinaryOcrTrain.cs +++ b/src/ui/Forms/Ocr/BinaryOcrTrain.cs @@ -328,12 +328,7 @@ namespace Nikse.SubtitleEdit.Forms.Ocr private void SelectAll_Click(object sender, EventArgs e) { - listViewFonts.BeginUpdate(); - foreach (ListViewItem fontItem in listViewFonts.Items) - { - fontItem.Checked = true; - } - listViewFonts.EndUpdate(); + listViewFonts.CheckAll(); } public void InitializeDetectFont(BinaryOcrBitmap bob, string text) diff --git a/src/ui/Forms/Ocr/VobSubCharactersImport.cs b/src/ui/Forms/Ocr/VobSubCharactersImport.cs index 89881497f..47ef345d6 100644 --- a/src/ui/Forms/Ocr/VobSubCharactersImport.cs +++ b/src/ui/Forms/Ocr/VobSubCharactersImport.cs @@ -143,11 +143,7 @@ namespace Nikse.SubtitleEdit.Forms.Ocr private void buttonFixesSelectAll_Click(object sender, EventArgs e) { listView1.ItemChecked -= listView1_ItemChecked; - - foreach (ListViewItem item in listView1.Items) - { - item.Checked = true; - } + listView1.CheckAll(); foreach (ListViewData d in _data) { diff --git a/src/ui/Forms/Options/SettingsProfileExport.cs b/src/ui/Forms/Options/SettingsProfileExport.cs index d0a79dda4..f7518faac 100644 --- a/src/ui/Forms/Options/SettingsProfileExport.cs +++ b/src/ui/Forms/Options/SettingsProfileExport.cs @@ -76,18 +76,12 @@ namespace Nikse.SubtitleEdit.Forms.Options private void selectAllToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = true; - } + listViewExportStyles.CheckAll(); } private void inverseSelectionToolStripMenuItem_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = !item.Checked; - } + listViewExportStyles.InvertCheck(); } } } diff --git a/src/ui/Forms/SplitLongLines.cs b/src/ui/Forms/SplitLongLines.cs index 3772883ec..9bf788e6c 100644 --- a/src/ui/Forms/SplitLongLines.cs +++ b/src/ui/Forms/SplitLongLines.cs @@ -718,10 +718,7 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { listViewFixes.ItemChecked -= listViewFixes_ItemChecked; - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = true; - } + listViewFixes.CheckAll(); listViewFixes.ItemChecked += listViewFixes_ItemChecked; GeneratePreview(false); } @@ -729,10 +726,7 @@ namespace Nikse.SubtitleEdit.Forms private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { listViewFixes.ItemChecked -= listViewFixes_ItemChecked; - foreach (ListViewItem item in listViewFixes.Items) - { - item.Checked = !item.Checked; - } + listViewFixes.InvertCheck(); listViewFixes.ItemChecked += listViewFixes_ItemChecked; GeneratePreview(false); } diff --git a/src/ui/Forms/Styles/SubStationAlphaStylesExport.cs b/src/ui/Forms/Styles/SubStationAlphaStylesExport.cs index 738ee61ce..3f5fe4a86 100644 --- a/src/ui/Forms/Styles/SubStationAlphaStylesExport.cs +++ b/src/ui/Forms/Styles/SubStationAlphaStylesExport.cs @@ -186,18 +186,12 @@ namespace Nikse.SubtitleEdit.Forms.Styles private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = true; - } + listViewExportStyles.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = !item.Checked; - } + listViewExportStyles.InvertCheck(); } } } diff --git a/src/ui/Forms/VTT/WebVttImportExport.cs b/src/ui/Forms/VTT/WebVttImportExport.cs index 3868abfa5..6b4d49cd9 100644 --- a/src/ui/Forms/VTT/WebVttImportExport.cs +++ b/src/ui/Forms/VTT/WebVttImportExport.cs @@ -145,18 +145,12 @@ namespace Nikse.SubtitleEdit.Forms.VTT private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = true; - } + listViewExportStyles.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = !item.Checked; - } + listViewExportStyles.InvertCheck(); } } } diff --git a/src/ui/Forms/VTT/WebVttStylePicker.cs b/src/ui/Forms/VTT/WebVttStylePicker.cs index 43eead8b8..31319e4e4 100644 --- a/src/ui/Forms/VTT/WebVttStylePicker.cs +++ b/src/ui/Forms/VTT/WebVttStylePicker.cs @@ -57,18 +57,12 @@ namespace Nikse.SubtitleEdit.Forms.VTT private void toolStripMenuItemSelectAll_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = true; - } + listViewExportStyles.CheckAll(); } private void toolStripMenuItemInverseSelection_Click(object sender, EventArgs e) { - foreach (ListViewItem item in listViewExportStyles.Items) - { - item.Checked = !item.Checked; - } + listViewExportStyles.InvertCheck(); } private void listViewExportStyles_SelectedIndexChanged(object sender, EventArgs e) diff --git a/src/ui/Logic/UiUtil.cs b/src/ui/Logic/UiUtil.cs index 9155a4e07..7f978693c 100644 --- a/src/ui/Logic/UiUtil.cs +++ b/src/ui/Logic/UiUtil.cs @@ -1121,6 +1121,36 @@ namespace Nikse.SubtitleEdit.Logic public static void AutoSizeLastColumn(this ListView listView) => listView.Columns[listView.Columns.Count - 1].Width = -2; + public static void CheckAll(this ListView lv) + { + lv.BeginUpdate(); + foreach (ListViewItem item in lv.Items) + { + item.Checked = true; + } + lv.EndUpdate(); + } + + public static void InvertCheck(this ListView lv) + { + lv.BeginUpdate(); + foreach (ListViewItem item in lv.Items) + { + item.Checked = !item.Checked; + } + lv.EndUpdate(); + } + + public static void UncheckAll(this ListView lv) + { + lv.BeginUpdate(); + foreach (ListViewItem item in lv.Items) + { + item.Checked = false; + } + lv.EndUpdate(); + } + public static void SelectAll(this ListView lv) { lv.BeginUpdate(); From 9d39d1c9aa17e68b5caf1700b16cbb9ebf72b13e Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Wed, 24 Apr 2024 18:27:21 +0200 Subject: [PATCH 02/28] Add new format (LRC with ms) - thx eadmaster :) Work on #8240 --- src/libse/SubtitleFormats/Lrc3DigitsMs.cs | 339 ++++++++++++++++++++ src/libse/SubtitleFormats/SubtitleFormat.cs | 1 + src/ui/Forms/Main.cs | 2 + src/ui/Logic/VideoPreviewGenerator.cs | 2 +- 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 src/libse/SubtitleFormats/Lrc3DigitsMs.cs diff --git a/src/libse/SubtitleFormats/Lrc3DigitsMs.cs b/src/libse/SubtitleFormats/Lrc3DigitsMs.cs new file mode 100644 index 000000000..f1aafe172 --- /dev/null +++ b/src/libse/SubtitleFormats/Lrc3DigitsMs.cs @@ -0,0 +1,339 @@ +using Nikse.SubtitleEdit.Core.Common; +using Nikse.SubtitleEdit.Core.Enums; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + /// + /// LRC is a format that synchronizes song lyrics with an audio/video file, [mm:ss.xxx] where mm is minutes, ss is seconds and xx is milliseconds. + /// + /// https://wiki.nicksoft.info/specifications:lrc-file + /// + /// Tags: + /// [al:''Album where the song is from''] + /// [ar:''Lyrics artist''] + /// [by:''Creator of the LRC file''] + /// [offset:''+/- Overall timestamp adjustment in milliseconds, + shifts time up, - shifts down''] + /// [re:''The player or editor that creates LRC file''] + /// [ti:''Lyrics(song) title''] + /// [ve:''version of program''] + /// + public class Lrc3DigitsMs : SubtitleFormat + { + private static readonly Regex RegexTimeCodes = new Regex(@"^\[\d+:\d\d\.\d\d\d\].*$", RegexOptions.Compiled); + + public override string Extension => ".lrc"; + + public override string Name => "LRC Lyrics ms"; + + public override bool IsMine(List lines, string fileName) + { + var subtitle = new Subtitle(); + LoadSubtitle(subtitle, lines, fileName); + + if (subtitle.Paragraphs.Count > 4) + { + var allStartWithNumber = true; + foreach (var p in subtitle.Paragraphs) + { + if (p.Text.Length > 1 && !Utilities.IsInteger(p.Text.Substring(0, 2))) + { + allStartWithNumber = false; + break; + } + } + if (allStartWithNumber) + { + return false; + } + } + + if (subtitle.Paragraphs.Count > _errorCount) + { + return !new UnknownSubtitle33().IsMine(lines, fileName) && + !new UnknownSubtitle36().IsMine(lines, fileName) && + !new TMPlayer().IsMine(lines, fileName) && + !new Lrc().IsMine(lines, fileName) && + !new LrcNoEndTime().IsMine(lines, fileName); + } + + return false; + } + + public override string ToText(Subtitle subtitle, string title) + { + var header = RemoveSoftwareAndVersion(subtitle.Header); + var sb = new StringBuilder(); + if (!string.IsNullOrEmpty(header) && (header.Contains("[ar:") || header.Contains("[ti:") || header.Contains("[by:") || header.Contains("[id:"))) + { + sb.AppendLine(header); + } + else if (!string.IsNullOrEmpty(title)) + { + sb.AppendLine("[ti:" + title.Replace("[", string.Empty).Replace("]", string.Empty) + "]"); + } + + if (!header.Contains("[re:", StringComparison.Ordinal)) + { + sb.AppendLine("[re: Subtitle Edit]"); + } + + if (!header.Contains("[ve:", StringComparison.Ordinal)) + { + sb.AppendLine($"[ve: {Utilities.AssemblyVersion}]"); + } + + const string timeCodeFormat = "[{0:00}:{1:00}.{2:000}]{3}"; + for (var i = 0; i < subtitle.Paragraphs.Count; i++) + { + var p = subtitle.Paragraphs[i]; + var next = subtitle.GetParagraphOrDefault(i + 1); + + var text = HtmlUtil.RemoveHtmlTags(p.Text); + text = text.Replace(Environment.NewLine, " "); + var fraction = p.StartTime.Milliseconds; + if (fraction >= 100) + { + var ms = new TimeCode(p.StartTime.Hours, p.StartTime.Minutes, p.StartTime.Seconds, 0).TotalMilliseconds; + ms += 1000; + p = new Paragraph(p.Text, ms, p.EndTime.TotalMilliseconds); + fraction = 0; + } + + sb.AppendLine(string.Format(timeCodeFormat, p.StartTime.Hours * 60 + p.StartTime.Minutes, p.StartTime.Seconds, fraction, text)); + + if (next == null || next.StartTime.TotalMilliseconds - p.EndTime.TotalMilliseconds > 100) + { + var tc = new TimeCode(p.EndTime.TotalMilliseconds); + sb.AppendLine(string.Format(timeCodeFormat, tc.Hours * 60 + tc.Minutes, tc.Seconds, tc.Milliseconds, string.Empty)); + } + } + + return sb.ToString().Trim(); + } + + public static string RemoveSoftwareAndVersion(string s) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + + var sb = new StringBuilder(); + foreach (var line in s.SplitToLines()) + { + if (line.Trim().StartsWith("[re:") || line.Trim().StartsWith("[ve:")) + { + continue; + } + + sb.AppendLine(line.Trim()); + } + + return sb.ToString().Trim(); + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { //[01:05.999]I've been walking in the same way as I do + _errorCount = 0; + var offsetInMilliseconds = 0.0d; + var header = new StringBuilder(); + char[] splitChars = { ':', '.' }; + foreach (var line in lines) + { + if (line.StartsWith('[') && RegexTimeCodes.Match(line).Success) + { + var s = line.Substring(1, 8); + var parts = s.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + if (parts.Length == 3) + { + try + { + var minutes = int.Parse(parts[0]); + var seconds = int.Parse(parts[1]); + var milliseconds = int.Parse(parts[2]); + var text = line.Remove(0, 10).Trim().TrimStart(']').Trim(); + var start = new TimeCode(0, minutes, seconds, milliseconds); + var p = new Paragraph(start, new TimeCode(), text); + subtitle.Paragraphs.Add(p); + } + catch + { + _errorCount++; + } + } + else + { + _errorCount++; + } + } + else if (line.StartsWith("[ar:", StringComparison.Ordinal)) // [ar:Lyrics artist] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[id:", StringComparison.Ordinal)) // [ar:Lyrics artist] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[al:", StringComparison.Ordinal)) // [al:Album where the song is from] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[ti:", StringComparison.Ordinal)) // [ti:Lyrics (song) title] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[au:", StringComparison.Ordinal)) // [au:Creator of the song text] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[length:", StringComparison.Ordinal)) // [length:How long the song is] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (line.StartsWith("[offset:", StringComparison.Ordinal)) // [length:How long the song is] + { + var temp = line.Replace("[offset:", string.Empty).Replace("]", string.Empty).Replace("'", string.Empty).RemoveChar(' ').TrimEnd(); + if (double.TryParse(temp, out var d)) + { + offsetInMilliseconds = d; + } + } + else if (line.StartsWith("[by:", StringComparison.Ordinal)) // [by:Creator of the LRC file] + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + else if (!string.IsNullOrWhiteSpace(line)) + { + if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + + _errorCount++; + } + else if (subtitle.Paragraphs.Count < 1) + { + header.AppendLine(line); + } + } + + header = new StringBuilder(Lrc.RemoveSoftwareAndVersion(header.ToString())); + header.AppendLine(); + + if (!header.ToString().Contains("[re:", StringComparison.Ordinal)) + { + header.AppendLine("[re: Subtitle Edit]"); + } + + if (!header.ToString().Contains("[ve:", StringComparison.Ordinal)) + { + header.AppendLine($"[ve: {Utilities.AssemblyVersion}]"); + } + + subtitle.Header = header.ToString(); + + var max = subtitle.Paragraphs.Count; + for (var i = 0; i < max; i++) + { + var p = subtitle.Paragraphs[i]; + while (RegexTimeCodes.Match(p.Text).Success) + { + var s = p.Text.Substring(1, 9); + p.Text = p.Text.Remove(0, 11).Trim(); + var parts = s.Split(splitChars, StringSplitOptions.RemoveEmptyEntries); + try + { + var minutes = int.Parse(parts[0]); + var seconds = int.Parse(parts[1]); + var milliseconds = int.Parse(parts[2]) * 10; + var text = GetTextAfterTimeCodes(p.Text); + var start = new TimeCode(0, minutes, seconds, milliseconds); + var newParagraph = new Paragraph(start, new TimeCode(), text); + subtitle.Paragraphs.Add(newParagraph); + } + catch + { + _errorCount++; + } + } + } + + subtitle.Sort(SubtitleSortCriteria.StartTime); + + var index = 0; + foreach (var p in subtitle.Paragraphs) + { + p.Text = Utilities.AutoBreakLine(p.Text); + var next = subtitle.GetParagraphOrDefault(index + 1); + if (next != null) + { + if (string.IsNullOrEmpty(next.Text)) + { + p.EndTime = new TimeCode(next.StartTime.TotalMilliseconds); + } + else + { + p.EndTime.TotalMilliseconds = next.StartTime.TotalMilliseconds - Configuration.Settings.General.MinimumMillisecondsBetweenLines; + } + if (p.DurationTotalMilliseconds > Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds) + { + double duration = Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; + p.EndTime = new TimeCode(p.StartTime.TotalMilliseconds + duration); + } + } + else + { + var duration = Utilities.GetOptimalDisplayMilliseconds(p.Text, 16) + 1500; + p.EndTime = new TimeCode(p.StartTime.TotalMilliseconds + duration); + } + index++; + } + + subtitle.RemoveEmptyLines(); + subtitle.Renumber(); + if (Math.Abs(offsetInMilliseconds) > 0.01) + { + foreach (var paragraph in subtitle.Paragraphs) + { + paragraph.StartTime.TotalMilliseconds += offsetInMilliseconds; + paragraph.EndTime.TotalMilliseconds += offsetInMilliseconds; + } + } + } + + private static string GetTextAfterTimeCodes(string s) + { + while (RegexTimeCodes.IsMatch(s)) + { + s = s.Remove(0, 11).Trim(); + } + + return s; + } + } +} diff --git a/src/libse/SubtitleFormats/SubtitleFormat.cs b/src/libse/SubtitleFormats/SubtitleFormat.cs index 4bb3a2569..b3f90c5aa 100644 --- a/src/libse/SubtitleFormats/SubtitleFormat.cs +++ b/src/libse/SubtitleFormats/SubtitleFormat.cs @@ -154,6 +154,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats new KanopyHtml(), new LambdaCap(), new Lrc(), + new Lrc3DigitsMs(), new LrcNoEndTime(), new MacSub(), new MagicVideoTitler(), diff --git a/src/ui/Forms/Main.cs b/src/ui/Forms/Main.cs index 7f5e1b895..6177b58b1 100644 --- a/src/ui/Forms/Main.cs +++ b/src/ui/Forms/Main.cs @@ -345,6 +345,7 @@ namespace Nikse.SubtitleEdit.Forms UiUtil.InitializeSubtitleFont(textBoxListViewText); UiUtil.InitializeSubtitleFont(textBoxListViewTextOriginal); UiUtil.InitializeSubtitleFont(SubtitleListview1); + UiUtil.InitializeSubtitleFont(textBoxSource); } private static string GetArgumentAfterColon(IEnumerable commandLineArguments, string requestedArgumentName) @@ -5783,6 +5784,7 @@ namespace Nikse.SubtitleEdit.Forms SubtitleListview1.BackColor = Configuration.Settings.General.SubtitleBackgroundColor; UiUtil.InitializeSubtitleFont(SubtitleListview1); + UiUtil.InitializeSubtitleFont(textBoxSource); mediaPlayer.SetSubtitleFont(); ShowSubtitle(); } diff --git a/src/ui/Logic/VideoPreviewGenerator.cs b/src/ui/Logic/VideoPreviewGenerator.cs index 00cadb04c..d24160279 100644 --- a/src/ui/Logic/VideoPreviewGenerator.cs +++ b/src/ui/Logic/VideoPreviewGenerator.cs @@ -271,7 +271,7 @@ namespace Nikse.SubtitleEdit.Logic StartInfo = { FileName = GetFfmpegLocation(), - Arguments = $"{cutStart}-i \"{inputVideoFileName}\"{cutEnd}-vf \"ass={Path.GetFileName(assaSubtitleFileName)}\",yadif,format=yuv420p -g 30 -bf 2 -s {width}x{height} {videoEncodingSettings} {passSettings} {presetSettings} {crfSettings} {audioSettings}{tuneParameter} -use_editlist 0 -movflags +faststart {outputVideoFileName}".TrimStart(), + Arguments = $"{cutStart}-i \"{inputVideoFileName}\"{cutEnd} -vf scale={width}:{height} -vf \"ass={Path.GetFileName(assaSubtitleFileName)}\",yadif,format=yuv420p -g 30 -bf 2 -s {width}x{height} {videoEncodingSettings} {passSettings} {presetSettings} {crfSettings} {audioSettings}{tuneParameter} -use_editlist 0 -movflags +faststart {outputVideoFileName}".TrimStart(), UseShellExecute = false, CreateNoWindow = true, WorkingDirectory = Path.GetDirectoryName(assaSubtitleFileName) ?? string.Empty, From 890c6af3e6fbb9d07ec555b18634e4abfd1ff375 Mon Sep 17 00:00:00 2001 From: May Kittens Devour Your Soul Date: Thu, 25 Apr 2024 10:57:54 +0200 Subject: [PATCH 03/28] Update hrv_OCRFixReplaceList.xml --- Dictionaries/hrv_OCRFixReplaceList.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Dictionaries/hrv_OCRFixReplaceList.xml b/Dictionaries/hrv_OCRFixReplaceList.xml index a50ec1b95..32dba5b8a 100644 --- a/Dictionaries/hrv_OCRFixReplaceList.xml +++ b/Dictionaries/hrv_OCRFixReplaceList.xml @@ -417,6 +417,7 @@ + @@ -2041,6 +2042,8 @@ + + @@ -4055,6 +4058,7 @@ + From ecdcb7fe7e38a3db69c8b0ae00e98f60730a7bae Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Fri, 26 Apr 2024 06:53:15 +0200 Subject: [PATCH 04/28] Add CPS option to modify selection - thx uckthis :) Work on #8148 --- Changelog.txt | 2 + LanguageBaseEnglish.xml | 2 + src/ui/Forms/ModifySelection.cs | 87 ++++++++++++++++++++++------ src/ui/Logic/Language.cs | 2 + src/ui/Logic/LanguageDeserializer.cs | 6 ++ src/ui/Logic/LanguageStructure.cs | 2 + 6 files changed, 82 insertions(+), 19 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 97eba352a..528d11723 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -4,6 +4,7 @@ * NEW: * Add "Text To Speech" - thx darnn/cvrle77/msjsc001 * Add burn-in batch mode - thx Leon/David + * Add new LRC with milliseconds format - thx eadmaster * IMPROVED: * Update Portuguese translation - thx hugok79 * Update Bulgarian translation - thx Калин @@ -20,6 +21,7 @@ * Fix possible crash in teletext reading - thx yellobyte * Fix unwanted text boxes background color change - thx Leon * Fix Whisper on selection in waveform in "translation mode" - thx rRobis + * Fix image export shadow when border is zero - thx pixelhunterX 4.0.5 (13th April 2024) diff --git a/LanguageBaseEnglish.xml b/LanguageBaseEnglish.xml index 7543a0b61..ee2497507 100644 --- a/LanguageBaseEnglish.xml +++ b/LanguageBaseEnglish.xml @@ -2129,6 +2129,8 @@ Download and continue? Even-numbered lines Duration less than Duration greater than + CPS less than + CPS greater than Exactly one line Exactly two lines More than two lines diff --git a/src/ui/Forms/ModifySelection.cs b/src/ui/Forms/ModifySelection.cs index 2419ff755..63957bdba 100644 --- a/src/ui/Forms/ModifySelection.cs +++ b/src/ui/Forms/ModifySelection.cs @@ -28,13 +28,15 @@ namespace Nikse.SubtitleEdit.Forms private const int FunctionEven = 7; private const int FunctionDurationLessThan = 8; private const int FunctionDurationGreaterThan = 9; - private const int FunctionExactlyOneLine = 10; - private const int FunctionExactlyTwoLines = 11; - private const int FunctionMoreThanTwoLines = 12; - private const int FunctionBookmarked = 13; - private const int FunctionBlankLines = 14; - private const int FunctionStyle = 15; - private const int FunctionActor = 16; + private const int FunctionCpsLessThan = 10; + private const int FunctionCpsGreaterThan = 11; + private const int FunctionExactlyOneLine = 12; + private const int FunctionExactlyTwoLines = 13; + private const int FunctionMoreThanTwoLines = 14; + private const int FunctionBookmarked = 15; + private const int FunctionBlankLines = 16; + private const int FunctionStyle = 17; + private const int FunctionActor = 18; private const string ContainsString = "Contains"; private const string StartsWith = "Starts with"; @@ -46,6 +48,8 @@ namespace Nikse.SubtitleEdit.Forms private const string Even = "Even"; private const string DurationLessThan = "Duration <"; private const string DurationGreaterThan = "Duration >"; + private const string CpsLessThan = "CPS <"; + private const string CpsGreaterThan = "CPS >"; private const string ExactlyOneLine = "Exactly one line"; private const string ExactlyTwoLines = "Exactly two lines"; private const string MoreThanTwoLines = "More than two lines"; @@ -97,6 +101,8 @@ namespace Nikse.SubtitleEdit.Forms comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.EvenLines); comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.DurationLessThan); comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.DurationGreaterThan); + comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.CpsLessThan); + comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.CpsGreaterThan); comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.ExactlyOneLine); comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.ExactlyTwoLines); comboBoxRule.Items.Add(LanguageSettings.Current.ModifySelection.MoreThanTwoLines); @@ -142,6 +148,12 @@ namespace Nikse.SubtitleEdit.Forms case DurationGreaterThan: comboBoxRule.SelectedIndex = FunctionDurationGreaterThan; break; + case CpsLessThan: + comboBoxRule.SelectedIndex = FunctionCpsLessThan; + break; + case CpsGreaterThan: + comboBoxRule.SelectedIndex = FunctionCpsGreaterThan; + break; case ExactlyOneLine: comboBoxRule.SelectedIndex = FunctionExactlyOneLine; break; @@ -226,6 +238,12 @@ namespace Nikse.SubtitleEdit.Forms case FunctionDurationGreaterThan: Configuration.Settings.Tools.ModifySelectionRule = DurationGreaterThan; break; + case FunctionCpsLessThan: + Configuration.Settings.Tools.ModifySelectionRule = CpsLessThan; + break; + case FunctionCpsGreaterThan: + Configuration.Settings.Tools.ModifySelectionRule = CpsGreaterThan; + break; case FunctionExactlyOneLine: Configuration.Settings.Tools.ModifySelectionRule = ExactlyOneLine; break; @@ -311,7 +329,7 @@ namespace Nikse.SubtitleEdit.Forms } } - for (int i = 0; i < _subtitle.Paragraphs.Count; i++) + for (var i = 0; i < _subtitle.Paragraphs.Count; i++) { if ((radioButtonSubtractFromSelection.Checked || radioButtonIntersect.Checked) && _subtitleListView.Items[i].Selected || !radioButtonSubtractFromSelection.Checked && !radioButtonIntersect.Checked) @@ -397,6 +415,20 @@ namespace Nikse.SubtitleEdit.Forms listViewItems.Add(MakeListViewItem(p, i)); } } + else if (comboBoxRule.SelectedIndex == FunctionCpsLessThan) // Cps less than + { + if (Utilities.GetCharactersPerSecond(p) < (double)numericUpDownDuration.Value) + { + listViewItems.Add(MakeListViewItem(p, i)); + } + } + else if (comboBoxRule.SelectedIndex == FunctionCpsGreaterThan) // Cps greater than + { + if (Utilities.GetCharactersPerSecond(p) > (double)numericUpDownDuration.Value) + { + listViewItems.Add(MakeListViewItem(p, i)); + } + } else if (comboBoxRule.SelectedIndex == FunctionExactlyOneLine) { if (p.Text.SplitToLines().Count == 1) @@ -504,28 +536,37 @@ namespace Nikse.SubtitleEdit.Forms { textBoxText.Visible = true; listViewStyles.Visible = false; - numericUpDownDuration.Visible = comboBoxRule.SelectedIndex == FunctionDurationLessThan || comboBoxRule.SelectedIndex == FunctionDurationGreaterThan; + numericUpDownDuration.Visible = + comboBoxRule.SelectedIndex == FunctionDurationLessThan || + comboBoxRule.SelectedIndex == FunctionDurationGreaterThan || + comboBoxRule.SelectedIndex == FunctionCpsLessThan || + comboBoxRule.SelectedIndex == FunctionCpsGreaterThan; + if (comboBoxRule.SelectedIndex == FunctionRegEx) // RegEx { - // creat new context menu only if textBoxText doesn't already has one, this will prevent unnecessary + // create new context menu only if textBoxText doesn't already has one, this will prevent unnecessary // allocation regex option is already selected and user re-select it - textBoxText.ContextMenuStrip = textBoxText.ContextMenuStrip ?? + textBoxText.ContextMenuStrip = textBoxText.ContextMenuStrip ?? FindReplaceDialogHelper.GetRegExContextMenu(new NativeTextBoxAdapter(textBoxText)); checkBoxCaseSensitive.Enabled = false; } - else if (comboBoxRule.SelectedIndex == FunctionOdd || - comboBoxRule.SelectedIndex == FunctionEven || - comboBoxRule.SelectedIndex == FunctionExactlyOneLine || - comboBoxRule.SelectedIndex == FunctionExactlyTwoLines || - comboBoxRule.SelectedIndex == FunctionMoreThanTwoLines || - comboBoxRule.SelectedIndex == FunctionBookmarked || + else if (comboBoxRule.SelectedIndex == FunctionOdd || + comboBoxRule.SelectedIndex == FunctionEven || + comboBoxRule.SelectedIndex == FunctionExactlyOneLine || + comboBoxRule.SelectedIndex == FunctionExactlyTwoLines || + comboBoxRule.SelectedIndex == FunctionMoreThanTwoLines || + comboBoxRule.SelectedIndex == FunctionBookmarked || comboBoxRule.SelectedIndex == FunctionBlankLines) { checkBoxCaseSensitive.Enabled = false; textBoxText.ContextMenuStrip = null; textBoxText.Visible = false; } - else if (comboBoxRule.SelectedIndex == FunctionDurationLessThan || comboBoxRule.SelectedIndex == FunctionDurationGreaterThan || comboBoxRule.SelectedIndex == FunctionAllUppercase) + else if (comboBoxRule.SelectedIndex == FunctionDurationLessThan || + comboBoxRule.SelectedIndex == FunctionDurationGreaterThan || + comboBoxRule.SelectedIndex == FunctionCpsLessThan || + comboBoxRule.SelectedIndex == FunctionCpsGreaterThan || + comboBoxRule.SelectedIndex == FunctionAllUppercase) { checkBoxCaseSensitive.Enabled = false; listViewStyles.Visible = false; @@ -539,7 +580,7 @@ namespace Nikse.SubtitleEdit.Forms numericUpDownDuration.Value = Configuration.Settings.General.SubtitleMinimumDisplayMilliseconds; } } - else + else if (comboBoxRule.SelectedIndex == FunctionDurationGreaterThan) { if (numericUpDownDuration.Value == 0 && Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds >= numericUpDownDuration.Minimum && @@ -548,6 +589,14 @@ namespace Nikse.SubtitleEdit.Forms numericUpDownDuration.Value = Configuration.Settings.General.SubtitleMaximumDisplayMilliseconds; } } + else if (comboBoxRule.SelectedIndex == FunctionCpsLessThan) + { + numericUpDownDuration.Value = (int)Math.Round(Math.Max(10, Configuration.Settings.General.SubtitleOptimalCharactersPerSeconds - 5), MidpointRounding.AwayFromZero); + } + else if (comboBoxRule.SelectedIndex == FunctionCpsGreaterThan) + { + numericUpDownDuration.Value = (int)Math.Round(Configuration.Settings.General.SubtitleMaximumCharactersPerSeconds, MidpointRounding.AwayFromZero); + } } else if (comboBoxRule.SelectedIndex == FunctionStyle) { diff --git a/src/ui/Logic/Language.cs b/src/ui/Logic/Language.cs index 44da8cc9c..3c387d048 100644 --- a/src/ui/Logic/Language.cs +++ b/src/ui/Logic/Language.cs @@ -2461,6 +2461,8 @@ namespace Nikse.SubtitleEdit.Logic EvenLines = "Even-numbered lines", DurationLessThan = "Duration less than", DurationGreaterThan = "Duration greater than", + CpsLessThan = "CPS less than", + CpsGreaterThan = "CPS greater than", ExactlyOneLine = "Exactly one line", ExactlyTwoLines = "Exactly two lines", MoreThanTwoLines = "More than two lines", diff --git a/src/ui/Logic/LanguageDeserializer.cs b/src/ui/Logic/LanguageDeserializer.cs index 643f3c6d8..0314a86ec 100644 --- a/src/ui/Logic/LanguageDeserializer.cs +++ b/src/ui/Logic/LanguageDeserializer.cs @@ -5770,6 +5770,12 @@ namespace Nikse.SubtitleEdit.Logic case "ModifySelection/DurationGreaterThan": language.ModifySelection.DurationGreaterThan = reader.Value; break; + case "ModifySelection/CpsLessThan": + language.ModifySelection.CpsLessThan = reader.Value; + break; + case "ModifySelection/CpsGreaterThan": + language.ModifySelection.CpsGreaterThan = reader.Value; + break; case "ModifySelection/ExactlyOneLine": language.ModifySelection.ExactlyOneLine = reader.Value; break; diff --git a/src/ui/Logic/LanguageStructure.cs b/src/ui/Logic/LanguageStructure.cs index 4a7e81594..a90171f46 100644 --- a/src/ui/Logic/LanguageStructure.cs +++ b/src/ui/Logic/LanguageStructure.cs @@ -2271,6 +2271,8 @@ public string EvenLines { get; set; } public string DurationLessThan { get; set; } public string DurationGreaterThan { get; set; } + public string CpsLessThan { get; set; } + public string CpsGreaterThan { get; set; } public string ExactlyOneLine { get; set; } public string ExactlyTwoLines { get; set; } public string MoreThanTwoLines { get; set; } From 0143e60c24ee05aa0bf346c01fd90a8af508374f Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Fri, 26 Apr 2024 16:24:22 +0200 Subject: [PATCH 05/28] Update Itlian language --- src/ui/Languages/it-IT.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/Languages/it-IT.xml b/src/ui/Languages/it-IT.xml index c8d5edbfb..21a5d2786 100644 --- a/src/ui/Languages/it-IT.xml +++ b/src/ui/Languages/it-IT.xml @@ -3,7 +3,7 @@ Subtitle Edit 4.0.5 - Tradotto da NAMP e bovirus - Data traduzione: 15.04.2024 + Tradotto da NAMP e bovirus - Data traduzione: 26.04.2024 it-IT OK @@ -2129,6 +2129,8 @@ Vuoi scaricare mpv e youtuibe-dl e continuare? Righe uguali Durata inferiore a Durata maggiore di + CPS inferiore a + CPS superiore a Esattamente una linea Esattamente due linee Più di due righe From 0c6e8fb10f15a89a598e8e06b6096f38fcc519d0 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sat, 27 Apr 2024 19:07:14 +0200 Subject: [PATCH 06/28] Add "refresh voices" context menu for Piper/ElevenLabs --- src/Test/Core/SeJsonParserTest.cs | 45 ++ src/libse/Common/SeJsonParser.cs | 387 +++++++++++++- src/libse/Common/Settings.cs | 17 + src/libse/TextToSpeech/PiperModel.cs | 30 ++ src/libse/TextToSpeech/PiperModels.cs | 105 ---- src/ui/Controls/NikseComboBoxCollection.cs | 5 + src/ui/Forms/Tts/TextToSpeech.Designer.cs | 282 ++++++---- src/ui/Forms/Tts/TextToSpeech.cs | 567 ++++++++++++++++++++- src/ui/Forms/Tts/TextToSpeech.resx | 3 + src/ui/Resources/AzureVoices.zip | Bin 0 -> 17493 bytes src/ui/Resources/PiperVoices.zip | Bin 0 -> 25477 bytes src/ui/SubtitleEdit.csproj | 2 + 12 files changed, 1219 insertions(+), 224 deletions(-) create mode 100644 src/libse/TextToSpeech/PiperModel.cs delete mode 100644 src/libse/TextToSpeech/PiperModels.cs create mode 100644 src/ui/Resources/AzureVoices.zip create mode 100644 src/ui/Resources/PiperVoices.zip diff --git a/src/Test/Core/SeJsonParserTest.cs b/src/Test/Core/SeJsonParserTest.cs index aacef6e02..5def62a18 100644 --- a/src/Test/Core/SeJsonParserTest.cs +++ b/src/Test/Core/SeJsonParserTest.cs @@ -192,5 +192,50 @@ namespace Test.Core }".Replace('\'', '"'), "items"); Assert.AreEqual(4, result.Count); } + + [TestMethod] + public void GetRootElements_Simple_Value() + { + var parser = new SeJsonParser(); + var result = parser.GetRootElements("{ \"tag\": \"hi there!\" }"); + Assert.AreEqual(1, result.Count); + Assert.AreEqual("tag", result[0].Name); + Assert.AreEqual("hi there!", result[0].Json); + } + + [TestMethod] + public void GetRootElements_Simple_Object() + { + var parser = new SeJsonParser(); + var result = parser.GetRootElements("{ \"tag\": { \"name\" : \"Joe\" } }"); + Assert.AreEqual(1, result.Count); + Assert.AreEqual("tag", result[0].Name); + Assert.AreEqual("{ \"name\" : \"Joe\" }", result[0].Json); + } + + + [TestMethod] + public void GetRootElements_Simple_Value_And_Object() + { + var parser = new SeJsonParser(); + var result = parser.GetRootElements("{ \"tag1\": \"hi there!\", \"tag2\": { \"name\" : \"Joe\" } }"); + Assert.AreEqual(2, result.Count); + Assert.AreEqual("tag1", result[0].Name); + Assert.AreEqual("hi there!", result[0].Json); + Assert.AreEqual("tag2", result[1].Name); + Assert.AreEqual("{ \"name\" : \"Joe\" }", result[1].Json); + } + + [TestMethod] + public void GetRootElements_Two_Simple_Value() + { + var parser = new SeJsonParser(); + var result = parser.GetRootElements("{ \"tag\": \"hi there!\",\"tag2\": \"hi!\", }"); + Assert.AreEqual(2, result.Count); + Assert.AreEqual("tag", result[0].Name); + Assert.AreEqual("hi there!", result[0].Json); + Assert.AreEqual("tag2", result[1].Name); + Assert.AreEqual("hi!", result[1].Json); + } } } \ No newline at end of file diff --git a/src/libse/Common/SeJsonParser.cs b/src/libse/Common/SeJsonParser.cs index 04d4cc3a2..c633a8bc3 100644 --- a/src/libse/Common/SeJsonParser.cs +++ b/src/libse/Common/SeJsonParser.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Nikse.SubtitleEdit.Core.ContainerFormats.Ebml; +using System.Collections.Generic; using System.Text; namespace Nikse.SubtitleEdit.Core.Common @@ -1300,5 +1301,389 @@ namespace Nikse.SubtitleEdit.Core.Common } return string.Empty; } + + public class RootElement + { + public string Name { get; set; } + public string Json { get; set; } + } + + public List GetRootElements(string content) + { + Errors = new List(); + var list = new List(); + var i = 0; + var max = content.Length; + var state = new Stack(); + var objectName = string.Empty; + var start = -1; + while (i < max) + { + var ch = content[i]; + if (_whiteSpace.Contains(ch)) // ignore white space + { + i++; + } + + else if (state.Count == 0) // root + { + if (ch == '{') + { + state.Push(new StateElement + { + Name = "Root", + State = SeJsonState.Object + }); + i++; + } + else if (ch == '[') + { + state.Push(new StateElement + { + Name = "Root", + State = SeJsonState.Array + }); + i++; + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + + else if (state.Peek().State == SeJsonState.Object) // after '{' + { + if (ch == '"') + { + i++; + int end = content.IndexOf('"', i); + objectName = content.Substring(i, end - i).Trim(); + int colon = content.IndexOf(':', end); + if (colon < 0) + { + Errors.Add($"Fatal - expected char : after position {end}"); + return list; + } + + i += colon - i + 1; + state.Push(new StateElement + { + Name = objectName, + State = SeJsonState.Value + }); + + if (state.Count == 2) + { + start = i; // element in root + } + } + else if (ch == '}') + { + i++; + state.Pop(); + + if (state.Count == 2 && start >= 0) + { + var str = content.Substring(start, i - start).Trim(); + list.Add(new RootElement() + { + Name = state.Peek().Name, + Json = str, + }); + + start = -1; + } + } + else if (ch == ',') // next object + { + if (state.Count == 1 && start >= 0) + { + var str = content.Substring(start, i - start).Trim(); + list.Add(new RootElement() + { + Name = state.Peek().Name, + Json = str, + }); + + start = i + 1; + } + + i++; + if (state.Peek().Count > 0) + { + state.Peek().Count++; + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + else if (ch == ']') // next object + { + i++; + if (state.Peek().Count > 0) + { + state.Pop(); + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + + else if (state.Peek().State == SeJsonState.Value) // value - string/ number / object / array / true / false / null + "," + "}" + { + if (ch == '"') // string + { + i++; + var skip = true; + int end = 0; + var endSeek = i; + while (skip) + { + end = content.IndexOf('"', endSeek); + if (end < 0) + { + Errors.Add($"Fatal - expected char \" after position {endSeek}"); + return list; + } + skip = content[end - 1] == '\\'; + if (skip) + { + endSeek = end + 1; + } + if (endSeek >= max) + { + Errors.Add($"Fatal - expected end tag after position {endSeek}"); + return list; + } + } + + if (state.Count == 2) + { + var objectValue = content.Substring(i, end - i).Trim(); + var x = state.Peek(); + list.Add(new RootElement(){ Name = x.Name, Json = objectValue }); + start = -1; + } + + i += end - i + 1; + state.Pop(); + if (state.Count > 0) + { + state.Peek().Count++; + } + } + else if (ch == '}') // empty value + { + i++; + var value = state.Pop(); + if (state.Count > 0) + { + if (value.State == SeJsonState.Value) + { + var st = state.Pop(); + + if (state.Count == 2) + { + var str = content.Substring(start, i - start).Trim(); + list.Add(new RootElement() + { + Name = state.Peek().Name, + Json = str, + }); + + start = -1; + } + } + else + { + state.Peek().Count++; + } + } + } + else if (ch == ',') // next object + { + i++; + state.Pop(); + if (state.Count > 0 && state.Peek().Count > 0) + { + state.Peek().Count++; + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + else if (ch == 'n' && max > i + 3 && content[i + 1] == 'u' && content[i + 2] == 'l' && content[i + 3] == 'l') + { + i += 4; + state.Pop(); + if (state.Count > 0) + { + state.Peek().Count++; + } + //if (objectName == name) + //{ + // list.Add(null); + //} + + } + else if (ch == 't' && max > i + 3 && content[i + 1] == 'r' && content[i + 2] == 'u' && content[i + 3] == 'e') + { + i += 4; + state.Pop(); + if (state.Count > 0) + { + state.Peek().Count++; + } + //if (objectName == name) + //{ + // list.Add("true"); + //} + } + else if (ch == 'f' && max > i + 4 && content[i + 1] == 'a' && content[i + 2] == 'l' && content[i + 3] == 's' && content[i + 4] == 'e') + { + i += 5; + state.Pop(); + if (state.Count > 0) + { + state.Peek().Count++; + } + //if (objectName == name) + //{ + // list.Add("false"); + //} + } + else if ("+-0123456789".IndexOf(ch) >= 0) + { + var sb = new StringBuilder(); + while (i < max && "+-0123456789.Ee".IndexOf(content[i]) >= 0) + { + sb.Append(content[i]); + i++; + } + state.Pop(); + if (state.Count > 0) + { + state.Peek().Count++; + } + //if (objectName == name) + //{ + // list.Add(sb.ToString()); + //} + } + else if (ch == '{') + { + if (state.Count > 1) + { + var value = state.Pop(); + state.Peek().Count++; + state.Push(value); + } + state.Push(new StateElement + { + State = SeJsonState.Object, + Name = objectName + }); + i++; + } + else if (ch == '[') + { + if (state.Count > 1) + { + var value = state.Pop(); + state.Peek().Count++; + state.Push(value); + } + state.Push(new StateElement + { + State = SeJsonState.Array, + Name = objectName + }); + i++; + //if (start < 0 && objectName == name) + //{ + // start = i; + //} + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + } + + else if (state.Peek().State == SeJsonState.Array) // array, after '[' + { + if (ch == ']') + { + state.Pop(); + i++; + //if (state.Count > 0 && state.Peek().Name == name && start > -1) + //{ + // list.Add(content.Substring(start, i - start - 1).Trim()); + // start = -1; + //} + } + else if (ch == ',' && state.Peek().Count > 0) + { + //if (start >= 0 && state.Peek().State == SeJsonState.Array && state.Peek().Name == name) + //{ + // list.Add(content.Substring(start, i - start).Trim()); + // start = i + 1; + //} + if (state.Count > 0 && state.Peek().Count > 0) + { + state.Peek().Count++; + } + else + { + Errors.Add($"Unexpected char {ch} at position {i}"); + return list; + } + i++; + } + else if (ch == '{') + { + if (state.Count > 0) + { + state.Peek().Count++; + } + state.Push(new StateElement + { + Name = objectName, + State = SeJsonState.Object + }); + i++; + } + else + { + if (state.Count > 0) + { + state.Peek().Count++; + } + state.Push(new StateElement + { + Name = objectName + "_array_value", + State = SeJsonState.Value + }); + } + } + + } + + return list; + } } } diff --git a/src/libse/Common/Settings.cs b/src/libse/Common/Settings.cs index ec7164c0f..4c7804703 100644 --- a/src/libse/Common/Settings.cs +++ b/src/libse/Common/Settings.cs @@ -193,6 +193,8 @@ namespace Nikse.SubtitleEdit.Core.Common public string TextToSpeechEngine { get; set; } public string TextToSpeechLastVoice { get; set; } public string TextToSpeechElevenLabsApiKey { get; set; } + public string TextToSpeechAzureApiKey { get; set; } + public string TextToSpeechAzureRegion { get; set; } public bool DisableVidoInfoViaLabel { get; set; } public bool ListViewSyntaxColorDurationSmall { get; set; } public bool ListViewSyntaxColorDurationBig { get; set; } @@ -557,6 +559,7 @@ namespace Nikse.SubtitleEdit.Core.Common AnthropicApiUrl = "https://api.anthropic.com/v1/messages"; AnthropicPrompt = "Translate from {0} to {1}, keep sentences in {1} as they are, do not censor the translation, give only the output without commenting on what you read:"; AnthropicApiModel = "claude-3-opus-20240229"; + TextToSpeechAzureRegion = "westeurope"; TranslateAllowSplit = true; TranslateViaCopyPasteAutoCopyToClipboard = true; TranslateViaCopyPasteMaxSize = 5000; @@ -5481,6 +5484,18 @@ $HorzAlign = Center settings.Tools.TextToSpeechElevenLabsApiKey = subNode.InnerText; } + subNode = node.SelectSingleNode("TextToSpeechAzureApiKey"); + if (subNode != null) + { + settings.Tools.TextToSpeechAzureApiKey = subNode.InnerText; + } + + subNode = node.SelectSingleNode("TextToSpeechAzureRegion"); + if (subNode != null) + { + settings.Tools.TextToSpeechAzureRegion = subNode.InnerText; + } + subNode = node.SelectSingleNode("TranslateViaCopyPasteAutoCopyToClipboard"); if (subNode != null) { @@ -12023,6 +12038,8 @@ $HorzAlign = Center textWriter.WriteElementString("TextToSpeechEngine", settings.Tools.TextToSpeechEngine); textWriter.WriteElementString("TextToSpeechLastVoice", settings.Tools.TextToSpeechLastVoice); textWriter.WriteElementString("TextToSpeechElevenLabsApiKey", settings.Tools.TextToSpeechElevenLabsApiKey); + textWriter.WriteElementString("TextToSpeechAzureApiKey", settings.Tools.TextToSpeechAzureApiKey); + textWriter.WriteElementString("TextToSpeechAzureRegion", settings.Tools.TextToSpeechAzureRegion); textWriter.WriteElementString("DisableVidoInfoViaLabel", settings.Tools.DisableVidoInfoViaLabel.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationSmall", settings.Tools.ListViewSyntaxColorDurationSmall.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationBig", settings.Tools.ListViewSyntaxColorDurationBig.ToString(CultureInfo.InvariantCulture)); diff --git a/src/libse/TextToSpeech/PiperModel.cs b/src/libse/TextToSpeech/PiperModel.cs new file mode 100644 index 000000000..005771548 --- /dev/null +++ b/src/libse/TextToSpeech/PiperModel.cs @@ -0,0 +1,30 @@ +using System.Linq; + +namespace Nikse.SubtitleEdit.Core.TextToSpeech +{ + public class PiperModel + { + public string Voice { get; set; } + public string Language { get; set; } + public string Quality { get; set; } + public string Model { get; set; } + public string ModelShort => Model.Split('/').Last(); + + public string Config { get; set; } + public string ConfigShort => Config.Split('/').Last(); + + public override string ToString() + { + return $"{Language} - {Voice} ({Quality})"; + } + + public PiperModel(string voice, string language, string quality, string model, string config) + { + Voice = voice; + Language = language; + Quality = quality; + Model = model; + Config = config; + } + } +} diff --git a/src/libse/TextToSpeech/PiperModels.cs b/src/libse/TextToSpeech/PiperModels.cs deleted file mode 100644 index 7e4841601..000000000 --- a/src/libse/TextToSpeech/PiperModels.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System.Collections.Generic; -using System.Linq; - -namespace Nikse.SubtitleEdit.Core.TextToSpeech -{ - public class PiperModels - { - public string Voice { get; set; } - public string Language { get; set; } - public string Quality { get; set; } - public string Model { get; set; } - public string ModelShort => Model.Split('/').Last(); - - public string Config { get; set; } - public string ConfigShort => Config.Split('/').Last(); - - public override string ToString() - { - return $"{Language} - {Voice} ({Quality})"; - } - - public PiperModels(string voice, string language, string quality, string model, string config) - { - Voice = voice; - Language = language; - Quality = quality; - Model = model; - Config = config; - } - - public static List GetVoices() - { - var models = new List - { - new PiperModels("kareem", "Arabic", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ar/ar_JO/kareem/medium/ar_JO-kareem-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ar/ar_JO/kareem/medium/ar_JO-kareem-medium.onnx.json"), - new PiperModels("upc_ona", "Catalan", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ca/ca_ES/upc_ona/medium/ca_ES-upc_ona-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ca/ca_ES/upc_ona/medium/ca_ES-upc_ona-medium.onnx.json"), - new PiperModels("jirka", "Czech", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/cs/cs_CZ/jirka/medium/cs_CZ-jirka-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/cs/cs_CZ/jirka/medium/cs_CZ-jirka-medium.onnx.json"), - new PiperModels("talesyntese", "Danish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/da/da_DK/talesyntese/medium/da_DK-talesyntese-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/da/da_DK/talesyntese/medium/da_DK-talesyntese-medium.onnx.json"), - new PiperModels("eva_k", "German", "low", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/de/de_DE/eva_k/x_low/de_DE-eva_k-x_low.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/de/de_DE/eva_k/x_low/de_DE-eva_k-x_low.onnx.json"), - new PiperModels("rapunzelina", "Greek", "low", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/el/el_GR/rapunzelina/low/el_GR-rapunzelina-low.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/el/el_GR/rapunzelina/low/el_GR-rapunzelina-low.onnx.json"), - new PiperModels("alan", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/alan/medium/en_GB-alan-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/alan/medium/en_GB-alan-medium.onnx.json"), - new PiperModels("alba", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/alba/medium/en_GB-alba-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/alba/medium/en_GB-alba-medium.onnx.json"), - new PiperModels("cori", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/cori/high/en_GB-cori-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/cori/medium/en_GB-cori-medium.onnx.json"), - new PiperModels("jenny_dioco", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/jenny_dioco/medium/en_GB-jenny_dioco-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/jenny_dioco/medium/en_GB-jenny_dioco-medium.onnx.json"), - new PiperModels("northern_english_male", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/northern_english_male/medium/en_GB-northern_english_male-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/northern_english_male/medium/en_GB-northern_english_male-medium.onnx.json"), - new PiperModels("semaine", "English GB", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/semaine/medium/en_GB-semaine-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_GB/semaine/medium/en_GB-semaine-medium.onnx.json"), - new PiperModels("amy", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/amy/medium/en_US-amy-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/amy/medium/en_US-amy-medium.onnx.json"), - new PiperModels("arctic", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/arctic/medium/en_US-arctic-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/arctic/medium/en_US-arctic-medium.onnx.json"), - new PiperModels("hfc_female", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/hfc_female/medium/en_US-hfc_female-medium.onnx.json"), - new PiperModels("hfc_male", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/hfc_male/medium/en_US-hfc_male-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/hfc_male/medium/en_US-hfc_male-medium.onnx.json"), - new PiperModels("joe", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/joe/medium/en_US-joe-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/joe/medium/en_US-joe-medium.onnx.json"), - new PiperModels("kristin", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/kristin/medium/en_US-kristin-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/kristin/medium/en_US-kristin-medium.onnx.json"), - new PiperModels("kusal", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/kusal/medium/en_US-kusal-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/kusal/medium/en_US-kusal-medium.onnx.json"), - new PiperModels("l2arctic", "English US", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/l2arctic/medium/en_US-l2arctic-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/l2arctic/medium/en_US-l2arctic-medium.onnx.json"), - new PiperModels("lessac", "English US", "high", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/lessac/high/en_US-lessac-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/lessac/high/en_US-lessac-high.onnx.json"), - new PiperModels("libritts", "English US", "high", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/libritts/high/en_US-libritts-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/libritts/high/en_US-libritts-high.onnx.json"), - new PiperModels("ljspeech", "English US", "high", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ljspeech/high/en_US-ljspeech-high.onnx.json"), - new PiperModels("ryan", "English US", "high", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ryan/high/en_US-ryan-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/en/en_US/ryan/high/en_US-ryan-high.onnx.json"), - new PiperModels("davefx", "Spanish ES", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/es/es_ES/davefx/medium/es_ES-davefx-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/es/es_ES/davefx/medium/es_ES-davefx-medium.onnx.json"), - new PiperModels("claude", "Spanish MX", "high", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/es/es_MX/claude/high/es_MX-claude-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/es/es_MX/claude/high/es_MX-claude-high.onnx.json"), - new PiperModels("amir", "Farsi", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fa/fa_IR/amir/medium/fa_IR-amir-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fa/fa_IR/amir/medium/fa_IR-amir-medium.onnx.json"), - new PiperModels("gyro", "Farsi", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fa/fa_IR/gyro/medium/fa_IR-gyro-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fa/fa_IR/gyro/medium/fa_IR-gyro-medium.onnx.json"), - new PiperModels("harri", "Finnish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fi/fi_FI/harri/medium/fi_FI-harri-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fi/fi_FI/harri/medium/fi_FI-harri-medium.onnx.json"), - new PiperModels("mls", "French", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/mls/medium/fr_FR-mls-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/mls/medium/fr_FR-mls-medium.onnx.json"), - new PiperModels("siwis", "French", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/siwis/medium/fr_FR-siwis-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/siwis/medium/fr_FR-siwis-medium.onnx.json"), - new PiperModels("tom", "French", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/tom/medium/fr_FR-tom-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/tom/medium/fr_FR-tom-medium.onnx.json"), - new PiperModels("upmc", "French", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/upmc/medium/fr_FR-upmc-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/fr/fr_FR/upmc/medium/fr_FR-upmc-medium.onnx.json?"), - new PiperModels("berta", "Hungarian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/hu/hu_HU/berta/medium/hu_HU-berta-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/hu/hu_HU/berta/medium/hu_HU-berta-medium.onnx.json"), - new PiperModels("imre", "Hungarian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/hu/hu_HU/imre/medium/hu_HU-imre-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/hu/hu_HU/imre/medium/hu_HU-imre-medium.onnx.json"), - new PiperModels("bui", "Icelandic", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/bui/medium/is_IS-bui-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/bui/medium/is_IS-bui-medium.onnx.json"), - new PiperModels("salka", "Icelandic", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/salka/medium/is_IS-salka-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/salka/medium/is_IS-salka-medium.onnx.json"), - new PiperModels("steinn", "Icelandic", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/steinn/medium/is_IS-steinn-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/steinn/medium/is_IS-steinn-medium.onnx.json"), - new PiperModels("ugla", "Icelandic", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/ugla/medium/is_IS-ugla-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/is/is_IS/ugla/medium/is_IS-ugla-medium.onnx.json"), - new PiperModels("riccardo", "Italian", "low", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/it/it_IT/riccardo/x_low/it_IT-riccardo-x_low.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/it/it_IT/riccardo/x_low/it_IT-riccardo-x_low.onnx.json"), - new PiperModels("natia", "Georgian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ka/ka_GE/natia/medium/ka_GE-natia-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ka/ka_GE/natia/medium/ka_GE-natia-medium.onnx.json"), - new PiperModels("issai", "Kazakh", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/kk/kk_KZ/issai/high/kk_KZ-issai-high.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/kk/kk_KZ/issai/high/kk_KZ-issai-high.onnx.json"), - new PiperModels("marylux", "Luxembourgish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/lb/lb_LU/marylux/medium/lb_LU-marylux-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/lb/lb_LU/marylux/medium/lb_LU-marylux-medium.onnx.json"), - new PiperModels("google", "Nepali", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ne/ne_NP/google/medium/ne_NP-google-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ne/ne_NP/google/medium/ne_NP-google-medium.onnx.json"), - new PiperModels("nathalie", "Dutch BE", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_BE/nathalie/medium/nl_BE-nathalie-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_BE/nathalie/medium/nl_BE-nathalie-medium.onnx.json"), - new PiperModels("rdh", "Dutch BE", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_BE/rdh/medium/nl_BE-rdh-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_BE/rdh/medium/nl_BE-rdh-medium.onnx.json"), - new PiperModels("mls", "Dutch NL", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_NL/mls/medium/nl_NL-mls-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/nl/nl_NL/mls/medium/nl_NL-mls-medium.onnx.json"), - new PiperModels("talesyntese", "Norwegian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/no/no_NO/talesyntese/medium/no_NO-talesyntese-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/no/no_NO/talesyntese/medium/no_NO-talesyntese-medium.onnx.json"), - new PiperModels("darkman", "Polish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/darkman/medium/pl_PL-darkman-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/darkman/medium/pl_PL-darkman-medium.onnx.json"), - new PiperModels("gosia", "Polish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/gosia/medium/pl_PL-gosia-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/gosia/medium/pl_PL-gosia-medium.onnx.json"), - new PiperModels("mc_speech", "Polish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/mc_speech/medium/pl_PL-mc_speech-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pl/pl_PL/mc_speech/medium/pl_PL-mc_speech-medium.onnx.json"), - new PiperModels("faber", "Portuguese BR", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pt/pt_BR/faber/medium/pt_BR-faber-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pt/pt_BR/faber/medium/pt_BR-faber-medium.onnx.json"), - new PiperModels("tugão", "Portuguese PT", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pt/pt_PT/tug%C3%A3o/medium/pt_PT-tug%C3%A3o-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/pt/pt_PT/tug%C3%A3o/medium/pt_PT-tug%C3%A3o-medium.onnx.json"), - new PiperModels("mihai", "Romanian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ro/ro_RO/mihai/medium/ro_RO-mihai-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ro/ro_RO/mihai/medium/ro_RO-mihai-medium.onnx.json"), - new PiperModels("dmitri", "Russian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ru/ru_RU/dmitri/medium/ru_RU-dmitri-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ru/ru_RU/dmitri/medium/ru_RU-dmitri-medium.onnx.json"), - new PiperModels("irina", "Serbian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ru/ru_RU/irina/medium/ru_RU-irina-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/ru/ru_RU/irina/medium/ru_RU-irina-medium.onnx.json"), - new PiperModels("lili", "Slovak ", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sr/sr_RS/serbski_institut/medium/sr_RS-serbski_institut-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sk/sk_SK/lili/medium/sk_SK-lili-medium.onnx.json"), - new PiperModels("artur", "Slovenian ", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sl/sl_SI/artur/medium/sl_SI-artur-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sl/sl_SI/artur/medium/sl_SI-artur-medium.onnx.json"), - new PiperModels("serbski_institut", "Serbian ", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sr/sr_RS/serbski_institut/medium/sr_RS-serbski_institut-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sr/sr_RS/serbski_institut/medium/sr_RS-serbski_institut-medium.onnx.json"), - new PiperModels("nst", "Swedish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx.json"), - new PiperModels("lanfrica", "Swahili", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sw/sw_CD/lanfrica/medium/sw_CD-lanfrica-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/sw/sw_CD/lanfrica/medium/sw_CD-lanfrica-medium.onnx.json"), - new PiperModels("fettah", "Turkish", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/tr/tr_TR/fettah/medium/tr_TR-fettah-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/tr/tr_TR/fettah/medium/tr_TR-fettah-medium.onnx.json"), - new PiperModels("ukrainian_tts", "Ukrainian", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/uk/uk_UA/ukrainian_tts/medium/uk_UA-ukrainian_tts-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/uk/uk_UA/ukrainian_tts/medium/uk_UA-ukrainian_tts-medium.onnx.json"), - new PiperModels("vais1000", "Vietnamese", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/vi/vi_VN/vais1000/medium/vi_VN-vais1000-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/vi/vi_VN/vais1000/medium/vi_VN-vais1000-medium.onnx.json"), - new PiperModels("huayan", "Chinese", "medium", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx", "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/zh/zh_CN/huayan/medium/zh_CN-huayan-medium.onnx.json"), - }; - - return models.OrderBy(p=>p.ToString()).ToList(); - } - } -} diff --git a/src/ui/Controls/NikseComboBoxCollection.cs b/src/ui/Controls/NikseComboBoxCollection.cs index 1bc96d4ee..43b4f011c 100644 --- a/src/ui/Controls/NikseComboBoxCollection.cs +++ b/src/ui/Controls/NikseComboBoxCollection.cs @@ -23,6 +23,11 @@ namespace Nikse.SubtitleEdit.Controls _items.AddRange(items); } + public void AddRange(string[] items) + { + _items.AddRange(items); + } + public IEnumerator GetEnumerator() { return _items.GetEnumerator(); diff --git a/src/ui/Forms/Tts/TextToSpeech.Designer.cs b/src/ui/Forms/Tts/TextToSpeech.Designer.cs index 722fb7ac4..9358a4f58 100644 --- a/src/ui/Forms/Tts/TextToSpeech.Designer.cs +++ b/src/ui/Forms/Tts/TextToSpeech.Designer.cs @@ -35,23 +35,28 @@ this.progressBar1 = new System.Windows.Forms.ProgressBar(); this.labelEngine = new System.Windows.Forms.Label(); this.groupBoxMsSettings = new System.Windows.Forms.GroupBox(); + this.labelRegion = new System.Windows.Forms.Label(); + this.nikseComboBoxRegion = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.labelVoiceCount = new System.Windows.Forms.Label(); this.checkBoxShowPreview = new System.Windows.Forms.CheckBox(); this.labelApiKey = new System.Windows.Forms.Label(); + this.nikseTextBoxApiKey = new Nikse.SubtitleEdit.Controls.NikseTextBox(); + this.TextBoxTest = new Nikse.SubtitleEdit.Controls.NikseTextBox(); this.buttonTestVoice = new System.Windows.Forms.Button(); this.checkBoxAddToVideoFile = new System.Windows.Forms.CheckBox(); this.labelVoice = new System.Windows.Forms.Label(); + this.nikseComboBoxVoice = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.contextMenuStripVoices = new System.Windows.Forms.ContextMenuStrip(this.components); + this.refreshVoicesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.nikseComboBoxEngine = new Nikse.SubtitleEdit.Controls.NikseComboBox(); this.listViewActors = new System.Windows.Forms.ListView(); this.columnHeaderActor = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeaderVoice = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.contextMenuStripActors = new System.Windows.Forms.ContextMenuStrip(this.components); this.labelActors = new System.Windows.Forms.Label(); this.buttonCancel = new System.Windows.Forms.Button(); - this.labelVoiceCount = new System.Windows.Forms.Label(); - this.nikseTextBoxApiKey = new Nikse.SubtitleEdit.Controls.NikseTextBox(); - this.TextBoxTest = new Nikse.SubtitleEdit.Controls.NikseTextBox(); - this.nikseComboBoxVoice = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.nikseComboBoxEngine = new Nikse.SubtitleEdit.Controls.NikseComboBox(); this.groupBoxMsSettings.SuspendLayout(); + this.contextMenuStripVoices.SuspendLayout(); this.SuspendLayout(); // // buttonOK @@ -112,6 +117,8 @@ // this.groupBoxMsSettings.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); + this.groupBoxMsSettings.Controls.Add(this.labelRegion); + this.groupBoxMsSettings.Controls.Add(this.nikseComboBoxRegion); this.groupBoxMsSettings.Controls.Add(this.labelVoiceCount); this.groupBoxMsSettings.Controls.Add(this.checkBoxShowPreview); this.groupBoxMsSettings.Controls.Add(this.labelApiKey); @@ -130,6 +137,83 @@ this.groupBoxMsSettings.TabStop = false; this.groupBoxMsSettings.Text = "Settings"; // + // labelRegion + // + this.labelRegion.AutoSize = true; + this.labelRegion.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.labelRegion.Location = new System.Drawing.Point(14, 267); + this.labelRegion.Name = "labelRegion"; + this.labelRegion.Size = new System.Drawing.Size(41, 13); + this.labelRegion.TabIndex = 32; + this.labelRegion.Text = "Region"; + // + // nikseComboBoxRegion + // + this.nikseComboBoxRegion.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nikseComboBoxRegion.BackColor = System.Drawing.SystemColors.Window; + this.nikseComboBoxRegion.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); + this.nikseComboBoxRegion.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); + this.nikseComboBoxRegion.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120))))); + this.nikseComboBoxRegion.ButtonForeColor = System.Drawing.SystemColors.ControlText; + this.nikseComboBoxRegion.ButtonForeColorDown = System.Drawing.Color.Orange; + this.nikseComboBoxRegion.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.nikseComboBoxRegion.DropDownHeight = 400; + this.nikseComboBoxRegion.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.nikseComboBoxRegion.DropDownWidth = 0; + this.nikseComboBoxRegion.FormattingEnabled = false; + this.nikseComboBoxRegion.Items.AddRange(new string[] { + "australiaeast", + "brazilsouth", + "canadacentral", + "centralus", + "eastasia", + "eastus", + "eastus2", + "francecentral", + "germanywestcentral", + "centralindia", + "japaneast", + "japanwest", + "jioindiawest", + "koreacentral", + "northcentralus", + "northeurope", + "norwayeast", + "southcentralus", + "southeastasia", + "swedencentral", + "switzerlandnorth", + "switzerlandwest", + "uaenorth", + "usgovarizona", + "usgovvirginia", + "uksouth", + "westcentralus", + "westeurope", + "westus", + "westus2", + "westus3"}); + this.nikseComboBoxRegion.Location = new System.Drawing.Point(17, 283); + this.nikseComboBoxRegion.MaxLength = 32767; + this.nikseComboBoxRegion.Name = "nikseComboBoxRegion"; + this.nikseComboBoxRegion.SelectedIndex = -1; + this.nikseComboBoxRegion.SelectedItem = null; + this.nikseComboBoxRegion.SelectedText = ""; + this.nikseComboBoxRegion.Size = new System.Drawing.Size(351, 23); + this.nikseComboBoxRegion.TabIndex = 31; + this.nikseComboBoxRegion.UsePopupWindow = false; + // + // labelVoiceCount + // + this.labelVoiceCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelVoiceCount.Location = new System.Drawing.Point(268, 84); + this.labelVoiceCount.Name = "labelVoiceCount"; + this.labelVoiceCount.Size = new System.Drawing.Size(100, 23); + this.labelVoiceCount.TabIndex = 29; + this.labelVoiceCount.Text = "255"; + this.labelVoiceCount.TextAlign = System.Drawing.ContentAlignment.BottomRight; + // // checkBoxShowPreview // this.checkBoxShowPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -147,15 +231,37 @@ // this.labelApiKey.AutoSize = true; this.labelApiKey.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.labelApiKey.Location = new System.Drawing.Point(20, 242); + this.labelApiKey.Location = new System.Drawing.Point(20, 224); this.labelApiKey.Name = "labelApiKey"; this.labelApiKey.Size = new System.Drawing.Size(44, 13); this.labelApiKey.TabIndex = 28; this.labelApiKey.Text = "API key"; // + // nikseTextBoxApiKey + // + this.nikseTextBoxApiKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nikseTextBoxApiKey.FocusedColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.nikseTextBoxApiKey.Location = new System.Drawing.Point(17, 240); + this.nikseTextBoxApiKey.Name = "nikseTextBoxApiKey"; + this.nikseTextBoxApiKey.Size = new System.Drawing.Size(351, 20); + this.nikseTextBoxApiKey.TabIndex = 27; + // + // TextBoxTest + // + this.TextBoxTest.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.TextBoxTest.FocusedColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.TextBoxTest.Location = new System.Drawing.Point(17, 168); + this.TextBoxTest.Name = "TextBoxTest"; + this.TextBoxTest.Size = new System.Drawing.Size(351, 20); + this.TextBoxTest.TabIndex = 20; + this.TextBoxTest.Text = "Hello, how are you?"; + this.TextBoxTest.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxTest_KeyDown); + // // buttonTestVoice // - this.buttonTestVoice.Location = new System.Drawing.Point(17, 158); + this.buttonTestVoice.Location = new System.Drawing.Point(17, 139); this.buttonTestVoice.Name = "buttonTestVoice"; this.buttonTestVoice.Size = new System.Drawing.Size(150, 23); this.buttonTestVoice.TabIndex = 15; @@ -180,12 +286,80 @@ // this.labelVoice.AutoSize = true; this.labelVoice.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.labelVoice.Location = new System.Drawing.Point(14, 108); + this.labelVoice.Location = new System.Drawing.Point(14, 94); this.labelVoice.Name = "labelVoice"; this.labelVoice.Size = new System.Drawing.Size(34, 13); this.labelVoice.TabIndex = 16; this.labelVoice.Text = "Voice"; // + // nikseComboBoxVoice + // + this.nikseComboBoxVoice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nikseComboBoxVoice.BackColor = System.Drawing.SystemColors.Window; + this.nikseComboBoxVoice.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); + this.nikseComboBoxVoice.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); + this.nikseComboBoxVoice.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120))))); + this.nikseComboBoxVoice.ButtonForeColor = System.Drawing.SystemColors.ControlText; + this.nikseComboBoxVoice.ButtonForeColorDown = System.Drawing.Color.Orange; + this.nikseComboBoxVoice.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.nikseComboBoxVoice.ContextMenuStrip = this.contextMenuStripVoices; + this.nikseComboBoxVoice.DropDownHeight = 400; + this.nikseComboBoxVoice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.nikseComboBoxVoice.DropDownWidth = 0; + this.nikseComboBoxVoice.FormattingEnabled = false; + this.nikseComboBoxVoice.Location = new System.Drawing.Point(17, 110); + this.nikseComboBoxVoice.MaxLength = 32767; + this.nikseComboBoxVoice.Name = "nikseComboBoxVoice"; + this.nikseComboBoxVoice.SelectedIndex = -1; + this.nikseComboBoxVoice.SelectedItem = null; + this.nikseComboBoxVoice.SelectedText = ""; + this.nikseComboBoxVoice.Size = new System.Drawing.Size(351, 23); + this.nikseComboBoxVoice.TabIndex = 10; + this.nikseComboBoxVoice.UsePopupWindow = false; + // + // contextMenuStripVoices + // + this.contextMenuStripVoices.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.refreshVoicesToolStripMenuItem}); + this.contextMenuStripVoices.Name = "contextMenuStripVoices"; + this.contextMenuStripVoices.Size = new System.Drawing.Size(150, 26); + this.contextMenuStripVoices.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripVoices_Opening); + // + // refreshVoicesToolStripMenuItem + // + this.refreshVoicesToolStripMenuItem.Name = "refreshVoicesToolStripMenuItem"; + this.refreshVoicesToolStripMenuItem.Size = new System.Drawing.Size(149, 22); + this.refreshVoicesToolStripMenuItem.Text = "Refresh voices"; + this.refreshVoicesToolStripMenuItem.Click += new System.EventHandler(this.refreshVoicesToolStripMenuItem_Click); + // + // nikseComboBoxEngine + // + this.nikseComboBoxEngine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.nikseComboBoxEngine.BackColor = System.Drawing.SystemColors.Window; + this.nikseComboBoxEngine.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); + this.nikseComboBoxEngine.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); + this.nikseComboBoxEngine.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120))))); + this.nikseComboBoxEngine.ButtonForeColor = System.Drawing.SystemColors.ControlText; + this.nikseComboBoxEngine.ButtonForeColorDown = System.Drawing.Color.Orange; + this.nikseComboBoxEngine.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); + this.nikseComboBoxEngine.DropDownHeight = 400; + this.nikseComboBoxEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown; + this.nikseComboBoxEngine.DropDownWidth = 391; + this.nikseComboBoxEngine.FormattingEnabled = false; + this.nikseComboBoxEngine.Location = new System.Drawing.Point(17, 40); + this.nikseComboBoxEngine.MaxLength = 32767; + this.nikseComboBoxEngine.Name = "nikseComboBoxEngine"; + this.nikseComboBoxEngine.SelectedIndex = -1; + this.nikseComboBoxEngine.SelectedItem = null; + this.nikseComboBoxEngine.SelectedText = ""; + this.nikseComboBoxEngine.Size = new System.Drawing.Size(351, 23); + this.nikseComboBoxEngine.TabIndex = 5; + this.nikseComboBoxEngine.TabStop = false; + this.nikseComboBoxEngine.Text = "nikseComboBox1"; + this.nikseComboBoxEngine.UsePopupWindow = false; + // // listViewActors // this.listViewActors.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -243,91 +417,6 @@ this.buttonCancel.UseVisualStyleBackColor = true; this.buttonCancel.Click += new System.EventHandler(this.buttonCancel_Click); // - // labelVoiceCount - // - this.labelVoiceCount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.labelVoiceCount.Location = new System.Drawing.Point(268, 98); - this.labelVoiceCount.Name = "labelVoiceCount"; - this.labelVoiceCount.Size = new System.Drawing.Size(100, 23); - this.labelVoiceCount.TabIndex = 29; - this.labelVoiceCount.Text = "255"; - this.labelVoiceCount.TextAlign = System.Drawing.ContentAlignment.BottomRight; - // - // nikseTextBoxApiKey - // - this.nikseTextBoxApiKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.nikseTextBoxApiKey.FocusedColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); - this.nikseTextBoxApiKey.Location = new System.Drawing.Point(17, 258); - this.nikseTextBoxApiKey.Name = "nikseTextBoxApiKey"; - this.nikseTextBoxApiKey.Size = new System.Drawing.Size(351, 20); - this.nikseTextBoxApiKey.TabIndex = 27; - // - // TextBoxTest - // - this.TextBoxTest.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.TextBoxTest.FocusedColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); - this.TextBoxTest.Location = new System.Drawing.Point(17, 187); - this.TextBoxTest.Name = "TextBoxTest"; - this.TextBoxTest.Size = new System.Drawing.Size(351, 20); - this.TextBoxTest.TabIndex = 20; - this.TextBoxTest.Text = "Hello, how are you?"; - this.TextBoxTest.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxTest_KeyDown); - // - // nikseComboBoxVoice - // - this.nikseComboBoxVoice.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.nikseComboBoxVoice.BackColor = System.Drawing.SystemColors.Window; - this.nikseComboBoxVoice.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); - this.nikseComboBoxVoice.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); - this.nikseComboBoxVoice.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120))))); - this.nikseComboBoxVoice.ButtonForeColor = System.Drawing.SystemColors.ControlText; - this.nikseComboBoxVoice.ButtonForeColorDown = System.Drawing.Color.Orange; - this.nikseComboBoxVoice.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); - this.nikseComboBoxVoice.DropDownHeight = 400; - this.nikseComboBoxVoice.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.nikseComboBoxVoice.DropDownWidth = 0; - this.nikseComboBoxVoice.FormattingEnabled = false; - this.nikseComboBoxVoice.Location = new System.Drawing.Point(17, 124); - this.nikseComboBoxVoice.MaxLength = 32767; - this.nikseComboBoxVoice.Name = "nikseComboBoxVoice"; - this.nikseComboBoxVoice.SelectedIndex = -1; - this.nikseComboBoxVoice.SelectedItem = null; - this.nikseComboBoxVoice.SelectedText = ""; - this.nikseComboBoxVoice.Size = new System.Drawing.Size(351, 23); - this.nikseComboBoxVoice.TabIndex = 10; - this.nikseComboBoxVoice.UsePopupWindow = false; - // - // nikseComboBoxEngine - // - this.nikseComboBoxEngine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.nikseComboBoxEngine.BackColor = System.Drawing.SystemColors.Window; - this.nikseComboBoxEngine.BackColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(240)))), ((int)(((byte)(240)))), ((int)(((byte)(240))))); - this.nikseComboBoxEngine.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(171)))), ((int)(((byte)(173)))), ((int)(((byte)(179))))); - this.nikseComboBoxEngine.BorderColorDisabled = System.Drawing.Color.FromArgb(((int)(((byte)(120)))), ((int)(((byte)(120)))), ((int)(((byte)(120))))); - this.nikseComboBoxEngine.ButtonForeColor = System.Drawing.SystemColors.ControlText; - this.nikseComboBoxEngine.ButtonForeColorDown = System.Drawing.Color.Orange; - this.nikseComboBoxEngine.ButtonForeColorOver = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(120)))), ((int)(((byte)(215))))); - this.nikseComboBoxEngine.DropDownHeight = 400; - this.nikseComboBoxEngine.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDown; - this.nikseComboBoxEngine.DropDownWidth = 391; - this.nikseComboBoxEngine.FormattingEnabled = false; - this.nikseComboBoxEngine.Location = new System.Drawing.Point(17, 40); - this.nikseComboBoxEngine.MaxLength = 32767; - this.nikseComboBoxEngine.Name = "nikseComboBoxEngine"; - this.nikseComboBoxEngine.SelectedIndex = -1; - this.nikseComboBoxEngine.SelectedItem = null; - this.nikseComboBoxEngine.SelectedText = ""; - this.nikseComboBoxEngine.Size = new System.Drawing.Size(351, 23); - this.nikseComboBoxEngine.TabIndex = 5; - this.nikseComboBoxEngine.TabStop = false; - this.nikseComboBoxEngine.Text = "nikseComboBox1"; - this.nikseComboBoxEngine.UsePopupWindow = false; - this.nikseComboBoxEngine.SelectedIndexChanged += new System.EventHandler(this.nikseComboBoxEngine_SelectedIndexChanged); - // // TextToSpeech // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -342,7 +431,7 @@ this.Controls.Add(this.labelProgress); this.Controls.Add(this.buttonOK); this.KeyPreview = true; - this.MinimumSize = new System.Drawing.Size(827, 481); + this.MinimumSize = new System.Drawing.Size(860, 520); this.Name = "TextToSpeech"; this.ShowIcon = false; this.ShowInTaskbar = false; @@ -356,6 +445,7 @@ this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextToSpeech_KeyDown); this.groupBoxMsSettings.ResumeLayout(false); this.groupBoxMsSettings.PerformLayout(); + this.contextMenuStripVoices.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -384,5 +474,9 @@ private System.Windows.Forms.CheckBox checkBoxShowPreview; private System.Windows.Forms.Button buttonCancel; private System.Windows.Forms.Label labelVoiceCount; + private System.Windows.Forms.Label labelRegion; + private Controls.NikseComboBox nikseComboBoxRegion; + private System.Windows.Forms.ContextMenuStrip contextMenuStripVoices; + private System.Windows.Forms.ToolStripMenuItem refreshVoicesToolStripMenuItem; } } \ No newline at end of file diff --git a/src/ui/Forms/Tts/TextToSpeech.cs b/src/ui/Forms/Tts/TextToSpeech.cs index 1997a100e..5b1e0eecc 100644 --- a/src/ui/Forms/Tts/TextToSpeech.cs +++ b/src/ui/Forms/Tts/TextToSpeech.cs @@ -33,7 +33,9 @@ namespace Nikse.SubtitleEdit.Forms.Tts private bool _abort; private readonly List _actors; private readonly List _engines; + private readonly List _piperVoices; private readonly List _elevenLabVoices; + private readonly List _azureVoices; private bool _actorsOn; private bool _converting; @@ -65,6 +67,20 @@ namespace Nikse.SubtitleEdit.Forms.Tts } } + public class AzureVoiceModel + { + public string DisplayName { get; set; } + public string LocalName { get; set; } + public string ShortName { get; set; } + public string Gender { get; set; } + public string Locale { get; set; } + + public override string ToString() + { + return $"{Locale} - {DisplayName} ({Gender})"; + } + } + public enum TextToSpeechEngineId { Piper, @@ -72,6 +88,7 @@ namespace Nikse.SubtitleEdit.Forms.Tts Coqui, MsSpeechSynthesizer, ElevenLabs, + AzureTextToSpeech, } public TextToSpeech(Subtitle subtitle, SubtitleFormat subtitleFormat, string videoFileName, VideoInfo videoInfo) @@ -84,7 +101,9 @@ namespace Nikse.SubtitleEdit.Forms.Tts _subtitleFormat = subtitleFormat; _videoFileName = videoFileName; _videoInfo = videoInfo; + _piperVoices = new List(); _elevenLabVoices = new List(); + _azureVoices = new List(); _actors = _subtitle.Paragraphs .Where(p => !string.IsNullOrEmpty(p.Actor)) .Select(p => p.Actor) @@ -108,13 +127,14 @@ namespace Nikse.SubtitleEdit.Forms.Tts _engines = new List(); _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.Piper, "Piper (fast/good)", _engines.Count)); - _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.Tortoise, "Tortoise TTS (very slow/good)", _engines.Count)); + _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.Tortoise, "Tortoise TTS (slow/good)", _engines.Count)); _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.Coqui, "Coqui AI TTS (only one voice)", _engines.Count)); if (Configuration.IsRunningOnWindows) { _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.MsSpeechSynthesizer, "Microsoft SpeechSynthesizer (very fast/robotic)", _engines.Count)); } _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.ElevenLabs, "ElevenLabs TTS (online/pay/good)", _engines.Count)); + _engines.Add(new TextToSpeechEngine(TextToSpeechEngineId.AzureTextToSpeech, "Microsoft Azure TTS (online/pay/good)", _engines.Count)); _actorAndVoices = new List(); nikseComboBoxEngine.DropDownStyle = ComboBoxStyle.DropDownList; @@ -135,7 +155,6 @@ namespace Nikse.SubtitleEdit.Forms.Tts labelActors.Visible = false; listViewActors.Visible = false; - nikseComboBoxEngine_SelectedIndexChanged(null, null); if (!SubtitleFormatHasActors() || !_actors.Any()) { @@ -150,6 +169,8 @@ namespace Nikse.SubtitleEdit.Forms.Tts Width = w; } + nikseComboBoxEngine_SelectedIndexChanged(null, null); + nikseComboBoxEngine.SelectedIndexChanged += nikseComboBoxEngine_SelectedIndexChanged; nikseComboBoxVoice.Text = Configuration.Settings.Tools.TextToSpeechLastVoice; } @@ -307,6 +328,12 @@ namespace Nikse.SubtitleEdit.Forms.Tts return result; } + if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + var result = await GenerateParagraphAudioAzure(subtitle, showProgressBar, overrideFileName); + return result; + } + return false; } @@ -594,7 +621,6 @@ namespace Nikse.SubtitleEdit.Forms.Tts progressBar1.Value = 0; progressBar1.Maximum = subtitle.Paragraphs.Count; progressBar1.Visible = showProgressBar; - var voices = PiperModels.GetVoices(); for (var index = 0; index < subtitle.Paragraphs.Count; index++) { @@ -618,13 +644,13 @@ namespace Nikse.SubtitleEdit.Forms.Tts } } - var voice = voices.First(x => x.ToString() == nikseComboBoxVoice.Text); + var voice = _piperVoices.First(x => x.ToString() == nikseComboBoxVoice.Text); if (_actorAndVoices.Count > 0 && !string.IsNullOrEmpty(p.Actor)) { var f = _actorAndVoices.FirstOrDefault(x => x.Actor == p.Actor); if (f != null && !string.IsNullOrEmpty(f.Voice)) { - voice = voices[f.VoiceIndex]; + voice = _piperVoices[f.VoiceIndex]; } } @@ -900,6 +926,171 @@ namespace Nikse.SubtitleEdit.Forms.Tts return true; } + private async Task> GetAzureVoices(bool useCache) + { + var ttsPath = Path.Combine(Configuration.DataDirectory, "TextToSpeech"); + if (!Directory.Exists(ttsPath)) + { + Directory.CreateDirectory(ttsPath); + } + + var azurePath = Path.Combine(ttsPath, "Azure"); + if (!Directory.Exists(azurePath)) + { + Directory.CreateDirectory(azurePath); + } + + var list = new List(); + var jsonFileName = Path.Combine(azurePath, "AzureVoices.json"); + if (!File.Exists(jsonFileName)) + { + var asm = System.Reflection.Assembly.GetExecutingAssembly(); + var stream = asm.GetManifestResourceStream("Nikse.SubtitleEdit.Resources.AzureVoices.zip"); + if (stream != null) + { + using (var zip = ZipExtractor.Open(stream)) + { + var dir = zip.ReadCentralDir(); + foreach (var entry in dir) + { + var fileName = Path.GetFileName(entry.FilenameInZip); + if (!string.IsNullOrEmpty(fileName)) + { + var name = entry.FilenameInZip; + var path = Path.Combine(azurePath, name.Replace('/', Path.DirectorySeparatorChar)); + zip.ExtractFile(entry, path); + } + } + } + } + } + + if (!useCache) + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Ocp-Apim-Subscription-Key", nikseTextBoxApiKey.Text.Trim()); + + var url = $"https://{nikseComboBoxRegion.Text.Trim()}.tts.speech.microsoft.com/cognitiveservices/voices/list"; + var result = await httpClient.GetAsync(new Uri(url), CancellationToken.None); + var bytes = await result.Content.ReadAsByteArrayAsync(); + + if (!result.IsSuccessStatusCode) + { + Cursor = Cursors.Default; + var error = Encoding.UTF8.GetString(bytes).Trim(); + SeLogger.Error($"Failed getting voices form Azure via url \"{url}\" : Status code={result.StatusCode} {error}"); + MessageBox.Show(this, "Calling url: " + url + Environment.NewLine + "Got error: " + error); + return new List(); + } + + File.WriteAllBytes(jsonFileName, bytes); + } + + var json = File.ReadAllText(jsonFileName); + var parser = new SeJsonParser(); + var arr = parser.GetArrayElements(json); + foreach (var item in arr) + { + var displayName = parser.GetFirstObject(item, "DisplayName"); + var localName = parser.GetFirstObject(item, "LocalName"); + var shortName = parser.GetFirstObject(item, "ShortName"); + var gender = parser.GetFirstObject(item, "Gender"); + var locale = parser.GetFirstObject(item, "Locale"); + + list.Add(new AzureVoiceModel + { + DisplayName = displayName, + LocalName = localName, + ShortName = shortName, + Gender = gender, + Locale = locale, + }); + } + + return list; + } + + private async Task GenerateParagraphAudioAzure(Subtitle subtitle, bool showProgressBar, string overrideFileName) + { + if (string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text)) + { + MessageBox.Show("Please add API key"); + nikseTextBoxApiKey.Focus(); + return false; + } + + if (string.IsNullOrWhiteSpace(nikseComboBoxRegion.Text)) + { + MessageBox.Show("Please add region"); + nikseComboBoxRegion.Focus(); + return false; + } + + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "ssml+xml"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", "audio/mpeg"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("X-Microsoft-OutputFormat", "audio-16khz-32kbitrate-mono-mp3"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "SubtitleEdit"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Ocp-Apim-Subscription-Key", nikseTextBoxApiKey.Text.Trim()); + + progressBar1.Value = 0; + progressBar1.Maximum = subtitle.Paragraphs.Count; + progressBar1.Visible = showProgressBar; + + var voices = _azureVoices; + var v = nikseComboBoxVoice.Text; + + for (var index = 0; index < subtitle.Paragraphs.Count; index++) + { + if (showProgressBar) + { + progressBar1.Value = index + 1; + labelProgress.Text = string.Format(LanguageSettings.Current.TextToSpeech.GeneratingSpeechFromTextXOfY, index + 1, subtitle.Paragraphs.Count); + } + + var p = subtitle.Paragraphs[index]; + var outputFileName = Path.Combine(_waveFolder, string.IsNullOrEmpty(overrideFileName) ? index + ".mp3" : overrideFileName.Replace(".wav", ".mp3")); + + if (_actorAndVoices.Count > 0 && !string.IsNullOrEmpty(p.Actor)) + { + var f = _actorAndVoices.FirstOrDefault(x => x.Actor == p.Actor); + if (f != null && !string.IsNullOrEmpty(f.Voice)) + { + v = f.Voice; + } + } + + var voice = voices.First(x => x.ToString() == v); + + var url = $"https://{nikseComboBoxRegion.Text.Trim()}.tts.speech.microsoft.com/cognitiveservices/v1"; + var data = $"{System.Net.WebUtility.HtmlEncode(p.Text)}"; + var content = new StringContent(data, Encoding.UTF8); + var result = await httpClient.PostAsync(url, content, CancellationToken.None); + var bytes = await result.Content.ReadAsByteArrayAsync(); + + if (!result.IsSuccessStatusCode) + { + var error = Encoding.UTF8.GetString(bytes).Trim(); + SeLogger.Error($"Azure TTS failed calling API on address {url} : Status code={result.StatusCode} {error}" + Environment.NewLine + "Data=" + data); + MessageBox.Show(this, "Calling url: " + url + Environment.NewLine + "With: " + data + Environment.NewLine + Environment.NewLine + "Error: " + error + result); + return false; + } + + File.WriteAllBytes(outputFileName, bytes); + + progressBar1.Refresh(); + labelProgress.Refresh(); + Application.DoEvents(); + } + + progressBar1.Visible = false; + labelProgress.Text = string.Empty; + + return true; + } + private void buttonOK_Click(object sender, EventArgs e) { EditedSubtitle = _subtitle; @@ -913,6 +1104,8 @@ namespace Nikse.SubtitleEdit.Forms.Tts labelApiKey.Visible = false; nikseTextBoxApiKey.Visible = false; + labelRegion.Visible = false; + nikseComboBoxRegion.Visible = false; labelVoice.Text = LanguageSettings.Current.TextToSpeech.Voice; if (SubtitleFormatHasActors() && _actors.Any()) @@ -937,7 +1130,12 @@ namespace Nikse.SubtitleEdit.Forms.Tts if (engine.Id == TextToSpeechEngineId.Piper) { - foreach (var voice in PiperModels.GetVoices()) + if (_piperVoices.Count == 0) + { + _piperVoices.AddRange(GetPiperVoices(true)); + } + + foreach (var voice in _piperVoices) { nikseComboBoxVoice.Items.Add(voice.ToString()); } @@ -983,7 +1181,7 @@ namespace Nikse.SubtitleEdit.Forms.Tts if (_elevenLabVoices.Count == 0) { - _elevenLabVoices.AddRange(GetElevenLabVoices()); + _elevenLabVoices.AddRange(GetElevenLabVoices(true)); } foreach (var voice in _elevenLabVoices) @@ -992,6 +1190,22 @@ namespace Nikse.SubtitleEdit.Forms.Tts } } + if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + nikseTextBoxApiKey.Text = Configuration.Settings.Tools.TextToSpeechAzureApiKey; + nikseComboBoxRegion.Text = Configuration.Settings.Tools.TextToSpeechAzureRegion; + + labelApiKey.Visible = true; + nikseTextBoxApiKey.Visible = true; + + var azureVoices = GetAzureVoices(true).Result; + _azureVoices.AddRange(azureVoices); + nikseComboBoxVoice.Items.AddRange(_azureVoices.Select(p => p.ToString()).ToArray()); + + labelRegion.Visible = true; + nikseComboBoxRegion.Visible = true; + } + if (nikseComboBoxVoice.Items.Count > 0) { nikseComboBoxVoice.SelectedIndex = 0; @@ -1025,7 +1239,7 @@ namespace Nikse.SubtitleEdit.Forms.Tts if (engine.Id == TextToSpeechEngineId.Piper) { - var voices = PiperModels.GetVoices(); + var voices = _piperVoices; foreach (var voiceLanguage in voices .GroupBy(p => p.Language) .OrderBy(p => p.Key)) @@ -1062,7 +1276,98 @@ namespace Nikse.SubtitleEdit.Forms.Tts parent.DropDownItems.Add(tsi); } - DarkTheme.SetDarkTheme(parent); + if (Configuration.Settings.General.UseDarkTheme) + { + DarkTheme.SetDarkTheme(parent); + } + } + } + } + if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + var voices = _azureVoices; + foreach (var voiceLanguage in voices + .GroupBy(p => p.Locale.Substring(0, 2)) + .OrderBy(p => p.Key)) + { + if (voiceLanguage.Count() == 1) + { + var voice = voiceLanguage.First(); + var tsi = new ToolStripMenuItem(); + tsi.Tag = new ActorAndVoice { Voice = voice.ToString(), VoiceIndex = voices.IndexOf(voice) }; + tsi.Text = voice.ToString(); + tsi.Click += (x, args) => + { + var a = (ActorAndVoice)(x as ToolStripItem).Tag; + SetActor(a); + }; + contextMenuStripActors.Items.Add(tsi); + } + else + { + if (voiceLanguage.Count() < 30) + { + var parent = new ToolStripMenuItem(); + parent.Text = voiceLanguage.Key; + contextMenuStripActors.Items.Add(parent); + var tsiList = new List(nikseComboBoxVoice.Items.Count); + foreach (var voice in voiceLanguage.OrderBy(p => p.ToString()).ToList()) + { + var tsi = new ToolStripMenuItem(); + tsi.Tag = new ActorAndVoice { Voice = voice.ToString(), VoiceIndex = voices.IndexOf(voice) }; + tsi.Text = voice.ToString(); + tsi.Click += (x, args) => + { + var a = (ActorAndVoice)(x as ToolStripItem).Tag; + SetActor(a); + }; + tsiList.Add(tsi); + } + parent.DropDownItems.AddRange(tsiList.ToArray()); + + if (Configuration.Settings.General.UseDarkTheme) + { + DarkTheme.SetDarkTheme(parent); + } + } + else + { + var parent = new ToolStripMenuItem(); + parent.Text = voiceLanguage.Key; + contextMenuStripActors.Items.Add(parent); + var subGroup = voiceLanguage.GroupBy(p => p.Locale); + foreach (var subGroupElement in subGroup) + { + var groupParent = new ToolStripMenuItem(); + groupParent.Text = subGroupElement.Key; + parent.DropDownItems.Add(groupParent); + var tsiList = new List(subGroupElement.Count()); + foreach (var voice in subGroupElement.OrderBy(p => p.DisplayName).ToList()) + { + var tsi = new ToolStripMenuItem(); + tsi.Tag = new ActorAndVoice { Voice = voice.ToString(), VoiceIndex = voices.IndexOf(voice) }; + tsi.Text = voice.ToString(); + tsi.Click += (x, args) => + { + var a = (ActorAndVoice)(x as ToolStripItem).Tag; + SetActor(a); + }; + tsiList.Add(tsi); + } + + groupParent.DropDownItems.AddRange(tsiList.ToArray()); + + if (Configuration.Settings.General.UseDarkTheme) + { + DarkTheme.SetDarkTheme(groupParent); + } + + } + if (Configuration.Settings.General.UseDarkTheme) + { + DarkTheme.SetDarkTheme(parent); + } + } } } } @@ -1105,12 +1410,16 @@ namespace Nikse.SubtitleEdit.Forms.Tts parent.DropDownItems.Add(tsi); } - DarkTheme.SetDarkTheme(parent); + if (Configuration.Settings.General.UseDarkTheme) + { + DarkTheme.SetDarkTheme(parent); + } } } } else { + var tsiList = new List(nikseComboBoxVoice.Items.Count); for (var index = 0; index < nikseComboBoxVoice.Items.Count; index++) { var item = nikseComboBoxVoice.Items[index]; @@ -1123,8 +1432,10 @@ namespace Nikse.SubtitleEdit.Forms.Tts var a = (ActorAndVoice)(x as ToolStripItem).Tag; SetActor(a); }; - contextMenuStripActors.Items.Add(tsi); + tsiList.Add(tsi); } + + contextMenuStripActors.Items.AddRange(tsiList.ToArray()); } labelActors.Visible = true; @@ -1134,7 +1445,104 @@ namespace Nikse.SubtitleEdit.Forms.Tts } } - private List GetElevenLabVoices() + private List GetPiperVoices(bool useCache) + { + var ttsPath = Path.Combine(Configuration.DataDirectory, "TextToSpeech"); + if (!Directory.Exists(ttsPath)) + { + Directory.CreateDirectory(ttsPath); + } + + var elevenLabsPath = Path.Combine(ttsPath, "Piper"); + if (!Directory.Exists(elevenLabsPath)) + { + Directory.CreateDirectory(elevenLabsPath); + } + + var result = new List(); + + var jsonFileName = Path.Combine(elevenLabsPath, "voices.json"); + + if (!File.Exists(jsonFileName)) + { + var asm = System.Reflection.Assembly.GetExecutingAssembly(); + var stream = asm.GetManifestResourceStream("Nikse.SubtitleEdit.Resources.PiperVoices.zip"); + if (stream != null) + { + using (var zip = ZipExtractor.Open(stream)) + { + var dir = zip.ReadCentralDir(); + foreach (var entry in dir) + { + var fileName = Path.GetFileName(entry.FilenameInZip); + if (!string.IsNullOrEmpty(fileName)) + { + var name = entry.FilenameInZip; + var path = Path.Combine(elevenLabsPath, name.Replace('/', Path.DirectorySeparatorChar)); + zip.ExtractFile(entry, path); + } + } + } + } + } + + if (!useCache) + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json"); + var url = "https://huggingface.co/rhasspy/piper-voices/resolve/main/voices.json?download=true"; + var res = httpClient.GetAsync(new Uri(url), CancellationToken.None).Result; + var bytes = res.Content.ReadAsByteArrayAsync().Result; + + if (!res.IsSuccessStatusCode) + { + var error = Encoding.UTF8.GetString(bytes).Trim(); + SeLogger.Error($"Failed getting voices form Piper via url \"{url}\" : Status code={res.StatusCode} {error}"); + MessageBox.Show(this, "Calling url: " + url + Environment.NewLine + "Got error: " + error + " " + result); + return result; + } + + File.WriteAllBytes(jsonFileName, bytes); + } + + if (File.Exists(jsonFileName)) + { + var json = File.ReadAllText(jsonFileName); + var parser = new SeJsonParser(); + var arr = parser.GetRootElements(json); + + foreach (var element in arr) + { + var elements = parser.GetRootElements(element.Json); + var name = elements.FirstOrDefault(p => p.Name == "name"); + var quality = elements.FirstOrDefault(p => p.Name == "quality"); + var language = elements.FirstOrDefault(p => p.Name == "language"); + var files = elements.FirstOrDefault(p => p.Name == "files"); + + if (name != null && quality != null && language != null && files != null) + { + var languageDisplay = parser.GetFirstObject(language.Json, "name_english"); + var languageFamily = parser.GetFirstObject(language.Json, "family"); + var languageCode = parser.GetFirstObject(language.Json, "code"); + + var filesElements = parser.GetRootElements(files.Json); + var model = filesElements.FirstOrDefault(p => p.Name.EndsWith(".onnx")); + var config = filesElements.FirstOrDefault(p => p.Name.EndsWith("onnx.json")); + if (model != null && config != null) + { + var modelUrl = "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/" + model.Name; + var configUrl = "https://huggingface.co/rhasspy/piper-voices/resolve/v1.0.0/" + config.Name; + result.Add(new PiperModel(name.Json, languageDisplay, quality.Json, modelUrl, configUrl)); + } + } + } + } + + return result; + } + + private List GetElevenLabVoices(bool useCache) { var ttsPath = Path.Combine(Configuration.DataDirectory, "TextToSpeech"); if (!Directory.Exists(ttsPath)) @@ -1175,6 +1583,33 @@ namespace Nikse.SubtitleEdit.Forms.Tts } } + if (!useCache) + { + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/json"); + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("accept", "application/json"); + + if (!string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text)) + { + httpClient.DefaultRequestHeaders.TryAddWithoutValidation("xi-api-key", nikseTextBoxApiKey.Text.Trim()); + } + + var url = "https://api.elevenlabs.io/v1/voices"; + var res = httpClient.GetAsync(new Uri(url), CancellationToken.None).Result; + var bytes = res.Content.ReadAsByteArrayAsync().Result; + + if (!res.IsSuccessStatusCode) + { + Cursor = Cursors.Default; + var error = Encoding.UTF8.GetString(bytes).Trim(); + SeLogger.Error($"Failed getting voices form ElevenLabs via url \"{url}\" : Status code={res.StatusCode} {error}"); + MessageBox.Show(this, "Calling url: " + url + Environment.NewLine + "Got error: " + error); + return new List(); + } + + File.WriteAllBytes(jsonFileName, bytes); + } + if (File.Exists(jsonFileName)) { var json = File.ReadAllText(jsonFileName); @@ -1332,6 +1767,11 @@ namespace Nikse.SubtitleEdit.Forms.Tts { Configuration.Settings.Tools.TextToSpeechElevenLabsApiKey = nikseTextBoxApiKey.Text; } + else if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + Configuration.Settings.Tools.TextToSpeechAzureApiKey = nikseTextBoxApiKey.Text; + Configuration.Settings.Tools.TextToSpeechAzureRegion = nikseComboBoxRegion.Text; + } Configuration.Settings.Tools.TextToSpeechEngine = engine.Id.ToString(); Configuration.Settings.Tools.TextToSpeechLastVoice = nikseComboBoxVoice.Text; @@ -1356,21 +1796,26 @@ namespace Nikse.SubtitleEdit.Forms.Tts public string GetParagraphAudio(Paragraph paragraph) { - if (_actorsOn) + if (_actorsOn && _actorAndVoices.Count > 0 && !string.IsNullOrEmpty(paragraph.Actor)) { - var engine = _engines.First(p => p.Index == nikseComboBoxEngine.SelectedIndex); - - if (engine.Id == TextToSpeechEngineId.Piper) + var f = _actorAndVoices.FirstOrDefault(x => x.Actor == paragraph.Actor); + if (f != null && !string.IsNullOrEmpty(f.Voice)) { - var voices = PiperModels.GetVoices(); - var voice = voices.First(x => x.ToString() == nikseComboBoxVoice.Text); - if (_actorAndVoices.Count > 0 && !string.IsNullOrEmpty(paragraph.Actor)) + var engine = _engines.First(p => p.Index == nikseComboBoxEngine.SelectedIndex); + + if (engine.Id == TextToSpeechEngineId.Piper) { - var f = _actorAndVoices.FirstOrDefault(x => x.Actor == paragraph.Actor); - if (f != null && !string.IsNullOrEmpty(f.Voice)) - { - return voices[f.VoiceIndex].Voice; - } + return _piperVoices[f.VoiceIndex].ToString(); + } + + if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + return _azureVoices[f.VoiceIndex].ToString(); + } + + if (engine.Id == TextToSpeechEngineId.ElevenLabs) + { + return _elevenLabVoices[f.VoiceIndex].ToString(); } } } @@ -1405,5 +1850,79 @@ namespace Nikse.SubtitleEdit.Forms.Tts nikseComboBoxEngine.DropDownWidth = nikseComboBoxEngine.Width; nikseComboBoxVoice.DropDownWidth = nikseComboBoxVoice.Width; } + + private void RefreshVoices() + { + if (nikseTextBoxApiKey.Visible && string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text)) + { + Cursor = Cursors.Default; + MessageBox.Show("Please add API key"); + nikseTextBoxApiKey.Focus(); + return; + } + + var engine = _engines.First(p => p.Index == nikseComboBoxEngine.SelectedIndex); + if (engine.Id == TextToSpeechEngineId.AzureTextToSpeech) + { + if (string.IsNullOrWhiteSpace(nikseComboBoxRegion.Text)) + { + Cursor = Cursors.Default; + MessageBox.Show("Please add region"); + nikseComboBoxRegion.Focus(); + return; + } + + var _ = GetAzureVoices(false).Result; + nikseComboBoxEngine_SelectedIndexChanged(null, null); + } + else if (engine.Id == TextToSpeechEngineId.ElevenLabs) + { + GetElevenLabVoices(false); + nikseComboBoxEngine_SelectedIndexChanged(null, null); + } + } + + private void contextMenuStripVoices_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + var engine = _engines.First(p => p.Index == nikseComboBoxEngine.SelectedIndex); + if ( + //engine.Id == TextToSpeechEngineId.AzureTextToSpeech || + engine.Id == TextToSpeechEngineId.ElevenLabs || + engine.Id == TextToSpeechEngineId.Piper + ) + { + return; + } + + e.Cancel = true; + } + + private void refreshVoicesToolStripMenuItem_Click(object sender, EventArgs e) + { + var dr = MessageBox.Show(this, "Download updated voice list from the internet?", "Update voices", MessageBoxButtons.YesNoCancel); + if (dr != DialogResult.Yes) + { + return; + } + + try + { + Cursor = Cursors.WaitCursor; + RefreshVoices(); + Cursor = Cursors.Default; + MessageBox.Show(this, "Voice list downloaded"); + } + catch (Exception ex) + { + Cursor = Cursors.Default; + MessageBox.Show(this, "Voice list download failed!" + Environment.NewLine + + Environment.NewLine + + ex.Message); + } + finally + { + Cursor = Cursors.Default; + } + } } } \ No newline at end of file diff --git a/src/ui/Forms/Tts/TextToSpeech.resx b/src/ui/Forms/Tts/TextToSpeech.resx index dec39f79f..78a54a51c 100644 --- a/src/ui/Forms/Tts/TextToSpeech.resx +++ b/src/ui/Forms/Tts/TextToSpeech.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 200, 17 + 17, 17 diff --git a/src/ui/Resources/AzureVoices.zip b/src/ui/Resources/AzureVoices.zip new file mode 100644 index 0000000000000000000000000000000000000000..70b9ac9faa90124e568dc19a213fd15319c278bb GIT binary patch literal 17493 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 literal 0 HcmV?d00001 diff --git a/src/ui/Resources/PiperVoices.zip b/src/ui/Resources/PiperVoices.zip new file mode 100644 index 0000000000000000000000000000000000000000..d5e4478e80d599b1477c412e51055761e96ecda4 GIT binary patch literal 25477 zcmV)=K!m?gO9KQH000080Q*3jSn;93Pw8R+0AbVu01E&B0CsO_V`XzLYIARH?3l~4 z3_%cv_ntynW9ojf6WF+f2zG?%$jZv3nd!syjBD6g2@k;vOG|6K18>9hxQuD76$T4u zb53;rRrPmPfB)&Od;9i(_&EUc>g3r&*I-UGJ2c(9b>PVUb)~CbqdOli^hF21NE(>Y zO0%wRR;JKgUczPCoN`^ky-c6y?S^a=8D{nLJRyAj~IS?U}ZvA%JQMl+c2k@L^5W+`93uu+*C?U`QImO0HnICtavlmHAN)~jq zQFvu!l4P~>fTZDPoEKsr^r*{7=)r*UDa2@8Fd-ww5=|Og_Re`CH@q)YgG%RfDU zy`9$c_qQ1wi>LJgqC;!ol@)N*qh+(sCS_)MM};nNRdHmYuIY=K6nol zQ@QtY?xP#EsiQ?76jcl=9kO4rr5*IZGx^$tdAGB;f|IDK=gPytHxDc~FQMGGvKLXeM9| zykecB2p;s$I4|VghEbQ2t+mc*X2(89skQSldo5VA6Ves(zI_v4n%sSue=ZN@!gW{< z=0Z<5oC}@ce=hvNTYv!o<$-=6Y1A;p|d}d z3vG4m!lA46{2B}%{%vXo@}Mi0r9n6AchaC8PJ^v+{WN&}h~7L|)D4X0!Ck^D18LCH z?LSA?muXt#-#+}qcG7ik(zU%il-k&%ogDoIm10aDG65n-CKOT{8=TTc*ra+$6&T*l z!5^rA!3hZ7DUm^eF^E7qK$I4U${d%U``K}a+5;yvGI~hTL2{&&h@=Qn0g1u@7y|pd zpZ|T?f4u*NGk~ujzx9F}%~M&l3$s428vO8YoV$~#5I zqD>+g7mbJ-UC>}Nc;sk`eV3p3$?*`ipp;Nj6673Hh^#XTrE^fxxnzC`_zczYQ->BX zrS?vsy?qm-vj`MN zPzRH4W@lK0@%<$}nBNwdgfx0EfS6*;T)+b$ViayeGoASg>tSG-_nLAl6g#5`I!Wju zp{HU%T|wgdJNy$CVwg)!Sj4c;f`(4r0;s3NDQga8Xt8C{>*2t>+b~3`npFhUC^rExDehE= zx=C16y*?RGngB=Q1dO#?HAE>#fy5R%;}J(y5{5BurDSo9tQ zURJLF_(O=9Bt;U$K=4c2mHPk3rbBfL*%(*%L|sm*z~ImSCz#iNe+t}hm4(@l3O!(? z*AFXU6K{M3;mOJ$cETze@5h69rC>@DPDxBD!k*$k+=MOh+>hv}0}s!b5#(@Yb~?B+ z4-0S(wGsp8($L=XTqr4IZb=Iw4MS#ef&{C}0kL?mM4*Bg4SCHy@@u=jzM@=Une;aL!GO0O#Cx+d08} zW_jM#?}4auUn<~dotHkQ>A)PT_6E~~b6Y3CZ*K1SyU*R=oLjAX{@m`HJ)d_yM<6)2 z-doPO-}tl4sR@3Td3QhM;0W-u%Wlh0t@(1Ub+zSj?j#)i)PDH9yXphp=gOnNx%O4= zd9Iyw>{JQ!H^8ShB*E>hpuC3Q+-DQuoEts`esg=z7xT@nK6s}*#?qq@oZGfgVS=U{(j;5YaI z-%VEFIvW*xyzk>U_+8};@L~P}T!njMR9EQP{rZHRchr0Z7k62#h|~7*)9kNx4QJbQ ztT?wk{s|6m5nI9KuWKNb%iYUYC<_T$!Q~~@E6#Gq#ELi{9=_tHTjrW#Qza`n-SfQ_ zoE{yYIO&l)$~Zl3a)q8z@`-clYM=14CT%N%g`2E6>91qODYvo}%|c$wn#abxXU%X) zltZ|jVIH%FBgD~v_Et#B5aZv~fo%_r{fuFe%)Uap_G*FOK)JDc1}Y9NkZ1+hm{UoP8QB*dNz zpz;?)gbX|;AaM_F!5#tP3UekBzc7JRKYBdLV^~Dly`Ou!?DqfP?kaZ=$Rtke1T=xO zIg~W}9{goj3X^ze52BO!2aI?G>;TdPPVdSxf!Di|PU7{BJb_zwr#OFiD^Y?8+~Q1g zno+6idnB2_bsaF{B;?2)VCT$`CvYa8HB1oPg97LT>LiLXfiwB2N8sfiqfU~&s^=)i zjl~P;ByQ3($^_2l)7=ry<|7<|IHRRFL2zp!O%tfQh$&7G-9>~jfpd8bJVAE%v7_7* zxCs>U1RKk8oq*gC0u!*ixO4>K`XM+$@KZ~95_N70VFIUj62(c9U-Rh%8+t#gCwsjI z)=AJ2MVZ7c(?iS?u#?oBQeq> z=nOp{0c|)ZPQn{kk|q&H66Q&Q>m}0!>Q2LyCfQ0O!6beFL?_68^d~^y{}f>o^m7d3 zB;*}LWfGThz`H}_ArMIu_{lHKlc<|w7$$I@mxw2DkK#e&1le1(OhOyo*F1^uK699a z90>rEuy@c9CK12p)(L`J1f(#5x?W|x|qRo%!~Ca;@uECQ^zRUT5E>P zTCr%PV8fIGuD|i_D^uUt_b5 zSW9s<4K)S8Do7P7qu=Ac8uWPz)I!o^5HfYsmP@ReMl#Gj2CByEQLpY~&HDlevuY!j z#F9Bx>1+*0;i0LI^ymWhUGVz_>Vtj#^2$!QfNQjA0!RAn0soZ;9LM`_YC8UK9tRM! z!5SGO7__Vbb8R(es>Gu?2UrbxUH@@6=B5aUS#bakmduO=4GR{i7Sd|W>urg`ts$?MwWi{(U%ure7S7^zTK7w+%x^5Uqk-l_KYt!uG8C^0V1ay2ylN#c+dc0^pHkLB{%IYrjmGQNtPRJD{DRSr~l$j zPpC;dfaR7bbdr?1HB~6R>S*s0`P0Al77`f*WJ)cl1(lwRQ_Voxy7D95b9IV8dB^w7 zp7s8Jxa9@;N{D#l~_w&u#)u-t$!heakock#cJ%LfoQ}igt20TW<)f|^szl!&8E_B z#2HhR%Gg7pE;;tvjJl<+qnTKIluh=_&p-X_ql0~Oef49%yf~RG^({|lOz)eldEq?6 z2``-Zv==@&&U@kOpI-ld^Y_c^KVJX(`nT6V@0#MF_pkOnaki`deLqL_%NKL-UfhrU zF}LR~+F72aKssQ+qJc|M2?7Oc>p&5LpzJJ{7J^@YrqZ{l@=^hnoY5jh2^NZo*a)mP zN>BQ3(XaXd@5WhUpa?~<$1)U})y4s1V3Wil%Hw?C>hn)O{>U!vRPMwz*--v1Ta*0! zH%~}@;(w9+$2*e$^p>Cgu5*32)pr0g<(`RDwiR-uy8F!hK67$u`I-Uo+@cU z)XKHAUaaP9t!Ep+31Tgo0T=cBF#eCeDEDH015c{{^kb24^{oB(o*?<>{5+oTUQ%aE zjHIE3L+>T^p`;lItJb281f^yts^y^96Cx?SjnZ1S(ZJFO9H|Ofpi!E3AZ)T8^`aXP z?Axmt5tBryhNVNq*2@T7a}rhUPjYuJc3S7I)hV49d)E2;(0Q?kk|}mBomYs1kz=4z z*oq}k1VL4$fEt`)mV!Q;&RZ!usJG4ti6Vk(AVR^IK|Atl)ayEz{TPF#h-$SkVj<`- zC`E(@qt=}0DRh3g-j(@mbXw&%^jYL@Epl4OX992JGGXh(90(g3Ou&uwCFG6FC8Uk4 zCE`Z962iu}IB@Gz9I*8vj^kTZu1m)kJKy}cOp>gIVK96ZiamvGjXnp( zn(`ns55>&w!DdE*U}%O#FZ;9Nr$335NYCw;OxC`n8A9Mv01F^u8f0PDnpi5RM}H>9pP<^d?wM#|i2L@wBP-|Njdm-9LWK{=L^ zdTJ2?h$W=aGjkP)WzM-?uerD=s&6LeM#ezEl*khPy>P|k~tW-vIG%mF#F zbrfnj#nH`rjpes>?XQA5PmS7Rl|+%SF{zb~Bd~(z1~In_>6<>YhwmcdB*Qqv!<_Qo z;xdc;bQZyl7V49yFSar8VuN=f@);yP&uoWAhkd%O-^1CHeBECT*`Un(;$qTIN;!!gh13J#fTV2 zgr;NP_x)r8Khro00jUIQD8b5rYAs2$BFG~nUqrHb{Nn5EIcz2U zH~(Ob)_1PMT32*-VK06q7iDf@9lN4x8;ll`2vcvCdg;*x;YEwuU+<;)RFx1a5?dar zHf)jW(5TfCb5tuAplY{>;&Y#+w4AkR1yM{kvQkE384)9=d;v?qzN6=SRoKh+{QeJK zg}l{K0-D2@+2*H9sgQSGgA(NR3gj@)xg37KOPj;AcB%Jx&W#SEyu;)$ukV#ThId#T z)``{Q>34QmaGe^@m(1vSnA{81I5~KGncPc-Wl!FIarX8)xmQavxtA+2x!0@fIyt?N z^5oHRj*}ngmC3=|hsn`#!O6*Uo}3+zYaVet;Nf1I2=m&v_e$dik=kCXfBn{jflH_(i%;Dd!H_j+O3d%aO7_j)N#?)3spexP@8 z_M9gdp8{}l^}1woua{v}2aW?z?&CMh>uUFUdp@aD4$)n>T z9R1Y8Gn&+^g=Kx9cG-Kigp+%UY_&B5c~nW2E1aBHENTrr>Sow- zkp!jzM;Tn+ScdNsjkM2IVe&xpV^6rZLAHArXH?F@s=c}3V>Eis69uqVe zT-X{18c-+;Ln7`_Q?DVq=-6;}w|B&N>a9`%$dw{Ut`TajHHac;YDK8F9dGw{R65U) z7F+L%r3hxII;urulsI@~XjXo=PxRv1I=8+j6(d_y2V$o%=jMdg(PWU}!m5Z5mW9)I;8Tv1F@x`>DQ)AsysI|*@TcNFEz;4;vEI?%VaM0!=P`o8be zhif9Yf6?=+zUzDc*^`5&Z+lP=dcF@H-8pFa7JGHj@vZ&ALBsdn!}|yQ-lwmh9<+Oh zJzRCmw@%z!*sGg5T(7shSQx&=cV115-{RL(wrlj-yy1PH_xAlVv3#OiS$KuTH(715 z{lx;zpme^5DN92MAT3#vJjy^83nlQ#$14i=9p7In5K+%-7qK3qcEJ%k)grB94ivi~ z6y&(?`F>K)r(W7Hs+tYC4YgpSB*GM+7j06@xPYYG%cDo%tFfHTQ8J!`moHs^yUv;$!3#A}ouV_@5G#ZlR+$5Kh zbK^aa_baaRyveCU=%KY{B{5~pSwpL+DJH9C7ZBHXM!PlabWQWI?NOY6Ho6S!{NCxh`0wQZGyvz&J}XM9V}}C>I+Slox(JEBr)GxXcLuE+ZU=)1S-+ z?JaxP(&I!7ga1;T#7TUl1!-Xc!FIJgZiye_QE3kg{~tP{mua+Zs#)OBYsT_?V;L9@4jqB#(tFQPR$p%L`w=K+5d zr)vy3S~ATwNhYkB9Dvi(j2xu}^1!$IBPn2J)cYOs{zCkJK>Tm_Gur$fc(TPmnxq9C8{%H%lAV; zQ;A-@r_?G}7rpvsIp%`Wvd2Ex#-WdQMJdOZT-TVg1azD!af(_|qqyQDRI5CWeY`Qs z2_dFttpIsX)Iwwt%PAt7i`)u}Ut(wQ^Wm|BY7Wi%+@G#rnE&^gKLvh9p54$>)c}Mc zM9~}yRu>qVL_Ie0jzfslDf!1?kBcsj&%pAFtLO^~LSgT}OSf zxm&&L$S*!$J$u&CU%Y#}^AYEsU*C1~7cXw!Y&rsrH>)@AHXRAZ)%)9>mooSL_GL$d z@ocjj1vmHc>bj@Fc>QtR(_p+^uX`GdPuCwNFL6Hpd_9fh{HE8_aGHC6wb>2lxtG-x zSeQTO&CN6qFn`k3haE`o<^fpkym*+$e08_!{c5+lp3K8pl8~+MX)Ct=Tv$P( z%^;<+QFD-Tq5v!yi{(I}cs|Iyqu|e%hIsVH(L3{0!6jeJ6=9Z)xpKwK+#rQP^k+7~ zJ*8(2DhUVRR(++GRa2?HW~Z)1^Ghrp`tVTskv6z|%2vQNc8h zdXVTRbky=@m?9!tlEk8cfrPnOD1gzI<%r&YSA1x6q4|IFp7<9#SFjpU3<7^C(HQk} zqDJVs{eN0E^ztaAfw!=Sy@okV3^T+*TxEa6me*Ihy%WSLf`h4a63sax(XnyMkeQui zCzd_s`$zA46Yeoa)riu-u!~1;IgoWQIrgN;Re*w03y zug^_aaKZgy5x;=9Nqkdsr`8b!Fc3s@nFw^Xj{Fnz+clg5Hm)boP~`vZGdTMfW0@E6 z0N}KJ_kiG3N_O@F+ zL^*kGJY+ek%s!!BVLTqzoccbYUqO=(c}`ya4e#Bh?~eEI7$%)6)o40x-6HBFjiKt~ z)2uq>q3RTisFTi)Rj2Jk)~WUmU8gQ)owAo*r!l${KN}EjQGi z`mB(5QcLJNMOY#5By$9wCcRL2a*ILYNv1;MNm*z-#fp(9U5b^bGG?A;r_gy?Z*-o* zln6bob))p;jnGqR8>J_|NT!~2f!32p3$ds6J=C7u47I1uapa!rH0V8*dn5RiW{Kj{ zOdgU?J_4c< zST4uj4?mghKHx^znFn<8_4Dng(8%-gr>Tka^{37S)X@p^fO9QF9`Lza`@xZGR_7B? zp)n8ie!*?H&%O741S-`Y1VP|C=h|dGC<4`az~`<#-+`)Z?6d@ht6kwS9(kZVWaAHv zTgtH&=bA0HIvU;eK^`#$sG~d2_n;Jsuw3p{d9btq;jF@OUL_Fax~fBrv+x7wD=C2l zFKIw>AMPhbQd^Mbrmj!8?&c2J2p50EAVE_037229AY6Y7 zhj0gk8DuyEeIa{fwmdPR-DDl2B;5ru?zK&bQx&Ega@>I@BuN6_Ir3%zeT;@dcnGWu z!qfk4<~;rW6D83!h_P%ycnm}Y!jYfW5I(0;k~2*l!b$MIhV$P?Aj9bq4p|PV%_qrS&^iz$krap>|G@y) z6(oeSFts5`B?uo#k!&P{!%%-RBo+X=ffchSC46h%5G7gr#8~_j=Lu1Va2K=%BxyI1 zg76&V1f-9*RuwWN<^owxudyf1Sy-_U<*C|$a2B=+#QAncAwg??kX-9_2oHiuLHG({ z6v9!U*^s4!rtFgu4kH234RppQoQ5+Eac)o(NF3R2kUZ85q`0TTAkFP=3*j#4X~^=g zHhHqw^Wy_iDv!HDjAW}JP7?M>aKC3jk_y*0kmBGy6NtvZe2~<*wIj78$VHxrmm#gEiXLRN>v2gO4O5Yrvq2DwX(IO2;m5+Z#rl3FA8Aesg@ zfG2PU*uMNOMEl|S5=Xbgno7iUt#3frg7$~FKF}T{bi`jkyJ4px%Rq>OXaKS<(wc?u z6U_l`tdHKoO4wh$g^PBU%GFhNQ?TVCey6 z$kS4Li0*_v1B`|(C^UBG*1Ck<$l}2*k-idfB8C z89mK3PdYRN?6wN+&6}44U}7*%rby z#5AQl5S@in2XU>oJIM9}DWDPX*GTEkaR8l!>5VLnZ~zHFrXkBZm>!7f`)48B;Sg|r zX!ny?9!(*sHPw!20Pbt#Y2KP6tsUYPGWu$lw)U1rFoBrJdxhL(!v|b5`UN5(NVh?> z6V^$j^trVo5`v0TNNbCzKt`m$Yg&iFyo;EA&~`+}q4_5+G7kt%oSR54@pPm#Y$uQ< z3$l^c_sN5dhV2^^b3`T!*}f$KmR5Kg@nyvhBy{<&PnM;44pJf=jp#t^TS)70nLw6f z0pl%}r7)rp4MEpKbSboQL}D=cLFgskNNWFZptR-vkR=728@NX#Zz3k*R)}sz5shdW z_&A~)!Rtm+B=aDe1jh_nI>CI>B4&e(PUJ^)Tsy(BKur5e6mqrPY7iH(ZX}jHw~*9` z&Ow&rVHdI$LLgcQJ-`S&UMr3!=rzPd`V^uQ@lGJR6_^d8TS0XrsgbseY-`a&v=Cw% zSt{WqGRt`5uJh=YHy1J8Soph+gL#0K!TlhXourUlPSg)6jZ77y8F24K^lVVwh|a?< zB2!1?b|JRBPa@h0?hQnnVR<6j3_TjrDyT?AV=!fi#=zzvtyS$8(H!`FQr!&Gdl1u{ zK8f6A$7#fuJNP8@`2>&x;SZ^$O(wE5g7-=55PN}W2>S3r`)KaQK}@9YL+%o`LNo-g z9Es&B#I;5S2!;M6wN1=H zYH3n~JdwB$X^rOxqmPx=& zSH>4{5z9c9BIrg^#Lp)hf#-%ijoNKQtDvorS;jc87DpRiKg2ZBevqs8R)7oKe2A98 zSR+da>>;JYEr7S2!wS((*prAR!7W}bmn4`^Y+0ItE6_QJcEV{Obcx7|`Afk{Ku?t85l5SJyXhLTqCURp3n&duUqt(0 zWgyxI=OMZi)-N==6wXb+1Y!V9fj0+nEzbr*z@HFi?$tV$xfW;sq65PNX zfw`Y(9b`A65$I0D^|AFuV)#YlCPAUK{fCgW4xc zA-E%>b$z(&INFXU5RE}!N0xbDfk+G{KUfN(4M~v`ft2nm4rn3xIY=+Z-9~gPis5bY zh`1lb^akP+%>uiH__Bc-5=+myNNRp`A*GRyKy*94oruJs>Nzrc-VNTTj^=v<+79Oe zOE)Y)Q{WAtdtp^0sds!2QW|Fiwt?t~?uBK8jD|TWL){De4zL^#`yf|j-XNL*nT6;) z^fyTAlerHmktskE!0tm@E@=Zs8~c}meN=8Ew#>Fcu1-pC#FteTh~>N=NQ%@Qq(s6T zdD?6qpec}Zk6un+C2H%{ZbdVCNtbf$z~QE8%sZ^nwKR`DYuV&p#X( zeJ)I>KKlH9K3R5Rb3~tiOd_t?{0h+o_})c_K+Zu*OY=6e9E(0^9i4NLS%kN-qc8Ro zTUxLqO94b7u0b0h^ai|9xlmKorVyycpUEGaO6M&Mi?+SLNEzKH4U&PJ||v;uSp z^aq;%F$d8K@IZ7M{9UpAR`QS*DIa7+GPp*U!LYoD>HAD0SLDS(v;|r|B=n(ukOKD@ zQo43uU`c=|L?h705S@ajNT<*ma392UKs%sQu=)G;)kq)GE7S-v9;uF0cKs~)l}!gdku01wbDu#W?=49!AP&tsKHX*QpSY)64YT0iGYWa$83 zX4G;VdXDHCl>U$_a^69f6qrv!K4!`kk&}+!Qz9_+At#Uj>z*sOlu=9 za`k9+KXHAv3?%ef=R;C=HwS7t^8in4b3SQ}Szct8I1`&b%d;s&bD;W@tMBsw2|zTk z{a|c^~ArkIL>Lh-N zXbe064Z%5v^wPWvkq|V8u347BnnH9Fs5WGYKqvBl|5K}f|KadI?A=SQ8bJ&N(7j{@ z0h(@DO%(DT3&{QlQA}J?bbX}!hM6L0VamP~AD!w$VnQ4L<-=_*`+i(v8#kRIae6y$ z+x2`sp104Bw=d^oJND=PaX(#8@6Q`fwEF#L{#(TT`D5T-lX3qzUH9$rIF9@C@;E(j z$NTy1{Wxx)-`*ele*crF{+s6@UVlO2e|!J@<$l?or_*)&u0Ef)&mWJ|c6@xC&zJol zoPqK43;k=x{im-!3WN8hbzgV-uifKFjKB2m!QDf<2X+tZ9@PCT-2=LZa}VYo%Ds?# z822LX3$h=C`*Q3D;J!utC>6ZDbbI0Uvh7>2k8(>|OW9tqyIQB2!J{kKLZ=Z~P>Gp}(7j8co z_HEm5ahdB#*#D62qmK)=Prtrg`+=`7)_&0IU#flT^|jkdyZ&X`Ctcs9eN;Qb^#$4w zZhd?9QS1-SK8Ch5`$X#tvmem<`ZhDk`ljq7iR+8m1nV2Jk4`SfKDqj4?4#Tw2@|Vt z#Qw-m8>#vx>| z$n=8g<E+RjqlZQhj9wPKD0)itgy`kalcASFPlTQZJqdaW z^aSYX&y%00K2Lm}_B`o%%JYktzh#iV>d2t3%t>~B+47NHMavK9{F3FP@`mN3@~v); zx+@&hn_sGYEO?{xvEX&3#O7}oVDlySPu4uCc}nwy=IP9nnWr*OWS+)6iFpe1)q}~) zFGW69yFvI!v^L~p!ut_XcMI}IjD8J3((>iUPqO^t<0HpgkB?+4RdrzHrOFePrzuZT z{y0IDZ#F*W`(6)7PoA7SHF;w4wB$+2Q<5hnPe-1NJQaB&@-*a0$WxFfAP+zO1NnIB z@x`vI7t~*$Fs_sPHX}Xhir|3@5ot`^6cWUm!+-13wa+l;z$eoTm8Fwn~LfmP% zlW?ctPQaahyZCnM?Zn$*x07zC+)lWiZoAlas_jJEX|{uGr`S%gonAY+c53ay+G(|u zYNymLsGUwbnRY4dMA~Vzi)g3NPN1DWJ9&2M?8Mn=v(Gep1qftz!t8X}$+AmjC(2Hf zog}+Nc7p8m*vYX|V<*Ngi=7m^Bz8jVa@fhRQ(-5-5#ht5a7eu1;HBv^r&V!RmC?$*NOTC#o(}U8Fiib%N^j)XAw+Qx~RAOP!QD zC3QmTOG}SvD=d9r)R&bWg*TO+A58AEp}wH>nD7!620?u>>Cxz|q{kM$)X#xWUq*UF zTNCLq;w_{{s5g)vmA8+6#HFf16Hb?#E;gNNI?;5R=_J!BrWeFJf$1fwPhNUK>Jyi~ z9QE9$*T^Q8z7+MC@kZ2hpZ;N@(zp98rMI9SfnFPqfk|I}dj1w(9hFG>($izY>m?tE z^kt`KpH~kikiO*fSnr0@BhmK?!_?7>O`mY|rKU&ajiyKCZKmghSGFaLUSj&>qAxH# z*1Ns*;qR9-ioUe;$hLZOPZE7u=|`sI45F8mK6&T`rB59Ca?+#Gn@NvQZzVmVt&#L7 zyp8nSsDGRx^d+Rnk~feZBYxdy2EF*20YP6ndK7-T%mn(f(PPG&M&DuxX8(M_==lMq z8aMUxV$mo3e5vTs>QzP3=gUOTOs^J6_?+%J+4E4(ztM5WHVlMN6a-tb+=2UV&e9$4 zlD&zEKtgQrnJo}Kr+F^&9O5~_b9(3G&Z(UfJEwI{>Ri$}p>sOtV$P|Y6FH}GPU4)x zIe~Nf=Hkt%n-e#uZ7$lJvN>UMy5?lfshSftr)f^ooT52Fb9v@N%hPN#%hPO!EKjmc zEKjlxEKjn{D^IcwB~Po)B=2<7wTT4VMDirtK=L%(Jn}@_IPyf>H1b5-F!Ds(Eb>I# zDDp(xB=TPe{NC;IIpk@!G305sDdcIkA>?Vc8RTiULy#xgjzFGb8$h07n?Ignn`fV5 zn@gTz8%v&In@XNy8%o~y`#z5p8?ka)<)q3fl@ltLQ%|V&t;O zNs&_`CqyoXTnsrCaw6n3$Vre(AQwPRe_Z@H^>O0kw8u%0QywQgPIsK_xYTi><21)f zj#C^bI1X=|+&HyyV&k;NNsUt)7c@?1oXj|taU$b1#z~A*7$-1JU!1%+b#db2w8ce> zOBN?APFI|)I8||?;xffaic=H^DE|4<6DKE5O`MoGEpbxfl*9>%%Mm9dPDPxEI1O=zA5J}7csT8F(&3cD35U}S7aJ}$oM<@BaFXE^!wH7d3nv#&Eu2`mtZ-7{ zlEMjv(+MXNP9>a3IE`=;;S$0LgwqEn4^ADNI5=%^(%_WA34_xGCksv$TqrnAaFXB@ z!3l!X11ASg4V)M_EpSrcl)wpr(*YL)P6eC@xD0R-;1s|KfYbjb|4sdy_&4ou(%+Q7 z34hc5Ci^Y*Tj;mUZ;{^=zX^WJ`zH5I?VH%QtZ!1^l)edl)A=UzE#;fYw~TKR-xR(H zeAD+9?@isCxHoNY(cYB3347D^7VAybo2WNTZ;{>-y$O2D^Css_&6}7vEpJlZkh}?b z)A1JLO~sptHw|wR-W0qAc+>AD-%Y)nc(?3s(%qE133tow7TZm=n`k%8Zj#*;y9svF z>n7Jtt(#c4tnQoY{*#}w+ocBWURF_+mFXE;W$9|BDTNlTDHmq- z@eQ1^+vN^+TN{P*m=x2Qo$K0EtnA5bvi|uN4(V;R%cOI+GG)!4HDe7HqiDI=*d=4{ zLwfst(9|6d2`?bpNi0I9ul zy<;>nS@d4EMd%1C!7Mg)M@Z~C0{7@V{OKuBF4L39U@ktq&eh$Ub6LfCRuNU1|Cavr z9MJ1?U`FMr+;m7E8ZAwCrjsj62tVK)c>i&}#l!#S8aVjBTzvNae-HgHyM7y+w?}j~ z>`;*)x_$YtWzTBe08*1}`bb`CDHQ12E) z>@@M=o4s5Qcv~jYS$B}9XL^Pg9@{K@&xi+nROe@De_VzAfAj7GtpCp?&-P!m|F%c} zrTm}&?8UFo=l&tj)Oxw@Q7tLctcOGwUO|n#&{WB`!+C=q9mGB9eZS5-wv$lVoWoO! zP70gaR7+i+a+vqD_VqF#RVuwiVJ8o!HdfVX+cm9iXMIHNZ)Lr1J%025ebux3pV+&W z+{%g=x)9qab9j0c?1TT zmYthZu6K@Ib+26ipNfBr`(F#@ZDhI9CqUBu6F(V{8rvAsdW)6^@=(YwdQz=} zUXucmeWne}Y*Zov7488^YOUOxphkwYhH2bQLuTPf<&hJ!_aCCT4gkyxBMG}r7ze`(woz7n) zv>Gwi7J995q>*WK45k55WK=zoZ5M&B*9n;SkvELF#BL)gLm3*hj)1ADDMPk(drR8%#Px#MoZm$q1q!Wh$-tR;SaPRQK`RA$u?n6Fz zt$v-(e==Z2x^mS<8N+BH2#!)MbTSrEJVBd9;Ol1u`zcm5h%r$Wtk$X2I0_amrPQDa z`#SRVLLl$QmqV<5^wvy?LmrX4sYz&9ScRWj2;9Tptpuj4YNivq;M`8j!nD7r!ZWJE zjQEjTZjsKe2~qN(QCV8$5rulrSUCp?DRwenVXwpfS9S)PQDdo{HLoO>EtDRrf+ItqkR6iP^C zpm1=sd;<72QBmMs^gU>`!U(l@;A3)+w{jIfZ7 zh-z#?YERu+y!qsn-Rx<_bb3rC>}F9R=0pFPLd?H=UWItmzrL>T{x?SmZ$7!On?DG1 zw?6;c3x>owK$IS}1hmQ}j~-C4V>4^XQDqVG`s)SV#mrb^NL&U0w$ziB5%Z`~F;!5I zHO%YGCfJ2&62%55l@2x6W~r*R*q|Yn6!8qqt~d5&>nFb+@?H;d`)Gcl_xyI~S;&2i z`0>jYw?qru0wRWes?>s-Qim}Js<9a$j8xU2cG6^?D(j#Z^LOHXf+rCoKqVShN&p=w zjU?FM-dbBny|^a`c|UhoQ*Bj?TBkez_~(HshTMT%96BWmlQ%S%p6%gQ`SK*@;k~d>L4|C!4b_u#~OPvZq1N1qh929+AoYClVFMzwHHmI$d&{k=LEU7SkJ)kPro_5+voT6I?eCR{KD_&Wpg#!#ZV{I>>p{g^)P*BMEO3`lxIY(Pz0t`sdO5gWoP z;zd5oe!f<&b+C#R>p~^BY}^7tiqa{K)gAZWC-5ht-kVNx{;{O)-Cmf#lh&V&{CCO5 zuhIF>-MXftJ*PON74C|lfg^z#jF6)v)H>|-v$xE6Et!pHo z&wYgChet@hSG!*x?CnqA+x6`((GUG?-RJr7iM9Xd3I1>~!*+2D_)I^4Tc7j4j~^<5 zB{dwNl%wVWYTPSAK`hZ7VXZ=5Px7*hnWbRPar7ZYTaPd>j>I)KDUt#&V_s!8>@%A& z+t5B*fu<^{1>%?s3Jge{o|feg_u)OA=I0!@H}-XXbyVC=*e;7Z6ez_ViY`#xVT(Hy z*CNG=Q{3I9Kyi1cuvl@5I~2ENac7ajA_XqL?|kQ;bN{%>d1f*>ljO;qndE(vym`x0 zIz4^vvUA>@_o&5`w_Ti5GWH_XZ{BE0%)#dcjr=1M%@R@DX)3` z-PqfgkQcWb$)*(vo#Bmwg`usrJ&gA83k7mw{-N+pQRPvTeg>X3#e-pHKWus0gx=@u zbRYeg;cZip3wKzDUlq5-#~Pv(c9Q zQ6Ap?M2JQqK-c5KL z8`_9iGdSFbb9SGMqtw$p+P=c{({B@ob)0QsQ=}yTZ5y2VYqX9`<+sL|HV$TOU_to|z=2M5SFj>8@bdTd|r@PXwX);wS+XGT2nMR~Z zM@6-+bYRx9@G%?BKau;-)_9#waK^Cc*NwagTLO%hdcK}5%d8y!QKcj`oNroUEk(Up zY|mjeez8pRIQzX0G~g;z>jvTk0i)T?ky^B3%TWJfq~h=+w5A22d|bn<=`U!+`1?Zz zSp}&?jcN+#knndlYS2-Fq&>p7_e)uim2zLt1t-+j8gy?KA(I>pe2-J)H#moa%8EEi zz@jPsf;k&0eT1(*=}F{>{wl5xl7SB7_CY5+HfyL*hsO!m0B}(Iic~uolI2UCX6wJf z!j79`<55`QKB8YJ7|rx1GN6gGot*h*}zU#+%mgt~HFKnSRRc#u&Xts1_ur&7k0w|CSOU zF(Kfd(C9^2gb^}!>~Efqq;Jj^4mj^{@!WZOSlrmVEKgkGKhK_&$0WXRIXT}ul8lAp zWIMjTzx(rbo=A*F?<@8f{sq^*?ZPDKxbJ?$C8UuDzj}~rM+~qKM$_>-!O==w_{>ql zELxw(cD%QF>U=Y}?+#-vu}8XXqzjUjL+o-!x4H+M>R=-emuXM@V7YTO`!xynC)h-m z;oL{D%+@mWM>>TZ;&_BUA9@Cc1z`(KwMy^SgZzoPs!agSJD>^9Jb``+M;|H(hVz&@ zhIQBV()!JQo>n9p_}7Ph_m~1w6>6qtOfUSL89ixhtki4 z7uKZBwi#&vL@w1unBki)g+&%rGv>TXP4ge>fVYnVP6tML=?EKp7CwoY1LWCkk(2pq z#5mIZWj-use-69>&LW)>42y*-O!}&P#O^jITrRkWpNLu`-@e?um-xO|xluE;ZUU8a zg~9vp64S@WcZf_jCGuZ(U4FIcO%a*cymuz8vDR;C5}j|oG@(d0B8q1K;^!^~0c!BH zW4(^`_)q?J@>iHH@yME^%NR!mJ0y_GE7V|;3=2(oi*e%OF8a?0Wo1uTX;d>YIQd5p z%JVUt2do8CcZmBuM&kZn(I<)hXx(f~LF}bUVbF@?VsoKK;g~=k)8ElACqWKeOx-$L zvZls)H@sy}gWGjxZ5nL&GF*pec!-;xv}xqIL2~Ag1J(0iAgdW*U#T4JH7{K@k4>JW z)Zfp^D;Fe9qGZ+aexDbs!Y>2+5ydR|cqmIpIN>YSLD^d^4njDv=SAn%4~irGIaE_O zZ)Y?srY@QmTg{iSnH8_l4NYVZ{Kal+9uUc0dsOH}Mf9n0*8#hzrKpu9xj`GO1Pt$6 zysudUQ;+lu$Fdq`6RaKQumDjlYE{IUQ5%i=n>MetOw;Kt<_#z6P%FzoX6ZFJkPl!* z%~hm@LR|`3LIGLkL1E}mTT;W}Q z0NWQ~-!Il3+6~tvW;>SZ68I5gHad9cT@Mcw@GK>+sjvs2*J6Ij9<}1FOC`GMVh-^0 z{+obDF>tcO-7ivHi@aIj_RldF_ih7v;78k&wtcQ9(ILuVo&Me|ej6tLWye-rwVKGO zrG5m>J-cg%xU1jG|IvTxj6|h=L<2*tcGGr1h@2jDK{1MYAUc386LZaXYg#_lA^JEE zIaeP9%TH`OAdKkO;Gs6&@GEgZ%7hQDMLTBePs(U9X+!POQspuAQFNss$gtnwE zxXnUXK@R9NgYWEbCso_cuMh@wMjZ=tRf?GRVOpI;?*_Hez$Vkx!7GI^ve-<5F{2N0 zHi|B@=xZSfc4Obv_-p_sb0O8mo_-h5ZNXCK@6(YtH>eSYL|a4Vs&5ToNZK|RZdzNR zY*iNt)r_*%2W&;o7Og;x$t&>7Bbo28ZX}0eqyYDe=Sr(%1=Y69=j;)SzefHwh3!f4 zlR6*`+x}N9ydw7%H?Mel#pEmEm!7Vh>tEsI6vVgTWhm5hcc>@WlF^Om7MT^;*tb zOh`7D;+YiDWlwp5o_dR+ShU=`%Cmb((iUHC`E?Rb&!0pucXtnSsf7f)+24N=RXeP0 z!H^6+KrXlu!4E=BM1k(N6(an5GE@tA-c5f^;7(6xN@@Y!LvYWC>F|_Utgx=FJ#8

vLzE%vISu1(Z5H;&q=O1CM=-7#ief~s_CNl@MmL~<$Jbd)<2LIYH1uzr6L zq*_=$jBgp0`bfR^`9PCAU^{->(+`T|5JYW`hCF>7)F$z5^{K#?~m&_ZaG29}7=i<(V32CC;vE{#`z- zjubefB0+OFNh{Lud6MK}S**HR|7}FT?~gqjepN(*3n4by4;+WZcTb%AAf?^>Ol@`? zJYGW3EhD3K>=seu%>aK=<)ZZ`^M*ezc7OKnz47Z$B(IL$Y7{e#ewr7(`>0Q2jxoB^ zGz}L13Do8jo@nu^qAje=@aG=-lAfzjWOQDM#L+pOkCHo7?CF09CEVHumoMd$!s4;9 zTMzqsIQAI|-Nlo_P!kyoDFu?ltS-v)P5kQyPFx$JeIAyxm^x1$c=iY{|2=FMvsF%s zJnDWn-Q}ffQKu583|~vW-++n@K%#s&cVpp1Gv%5|n!GG)VanPc;_vuj4>>x_C%C3M z$Ukf_7n4C;vog3s`k^4IxT#0+o{!t>xbC`&(Li4dJ>EU`S?!YnUkhX6-;e%A-Z}}8 z>+aCRy!^nwO>sX8z~mTGJQ?duw#Inh3i>Q4+k_k|kY4Wk5??O+IiGjXB<_dto-Y!q z|7}xC{QH+h)LHKLAtynR#67~dphvkT4!dx(k;s+GdAxC$Y=Ps1q8wtcKkwzz#_^_+ zz)2zOGYs!USU4^Z^{UziDlPzpt9?R%2A;F!E}p z_FlzvcWJ7n4qSp*T&Gy4eY4*CX?zi>R_R(yM%hy)?s5acIJ@{aZm!rIluBfe636m| zP%^AH^vJVipE6(94zVZnK+`-9aB{UN`9sNLh?q5p1be}I`k=LOwj z(@~jJ1u>2RJI7^zUK(8Q^l5Urfx&4q}+FX=cNe z;sa@?KJ*S9_JJgjAA1vS7y5zIkdvT~@~X zuE3uY*3H6l+JY!y&?@FAGk_M$kdMIA?QcEAp_E@|5s8RgLMgsp&#n2L{RN{(!IwJ7 zi|KSEac_2t1Xxmu8hIGKesX`QJszq@n&{|$St=MM1!k4}c;a33+5wo)TgFyG@522y zUp!M&t6OEc_e0Vsi<|3MwWKX!^AM+GXCK1-+%k-J_ZB;(Ag3l3a{j|&_Re|lKBjE} zcxeYx6P*2U>Pw^v554z(GMAf%DIKCFuYs_tN{47AQi+HSiEnkEl|@KvxC2priz@<( z+}kpL$P0+}z4VripGxV20Y^52vDSHO(9;ObEwwGv~P7d4}k}a?*pS}IkGz{V!0hTgXR5E${iS$a?v&#TVrk9$02nV z8!fF^X>ltpzpx@&VCM#m%Pk#P6NYSQRbH=4Y<~h;Imz}7yq*f!^>z8MbJ@=}?hWj3 zL&u!gFl{RJ5e^aD&!W07sO{@@?P|6WKckT>q8y@TNT+#hHcK=goZYyTV&x7xEGRBy z*A6PiV*(`we%Ba||M<|l>F(&uClR^vgYIV&p4}ghK}E6PHG^ucl9h2|-}+Zsy2M-T z$f1KN*@>`0YFKXg^#|D&Y8zF7!=NI{pgo%o>r`KhK`A6=!=Pr~YYE{<39{mFqd4jf zF{vk70V1ro!1zaPJ)>WM9~Q|>D$>X?&KoJ}leiI+RVw$B*oS^EqRWu`GaOT~Py7;n z(KEE!uvVwx&JmKK9VZDT!15U`TRi^u=iuJJQKV!Ln7V)ztI-eO;dvl%#dp_G%;Na_ zA!Cr@I|-$Z)A1xKPiBOKs`a_)h;8yN$9e6Hx`dYR<`}a{UuN%VabL-?Ly&=;wApA5 zT1lq+tJA4TkpaIfhdP~N3x}htGR2_pkY)K(-b(v?gE<| z;%Vl#^Q8JUn_ku$t!gGSUb}EBvFhTFcH!U?ZdY0vng&WJNtYFYu4Ers`PeCd(x*So zc(~5Cm0I>K*JHA4ErHONav9c8i??Vtn&G@B@K8(l0`^jQjUu~@ubL{f-U#9LaqBM{ z!}0KG?eObjcqbgnY7gv~&T}y0+{sOBxa1GNYrrbwiYF#0-nJmsyw zUYxkGhtkfBeSdWl$HVErpD5=SJ=+01zGo3aA1!lJV>EJ!qE>>5spCGYZV$}MPlZn9_habovOXK+1MBqQ7)f9VY0G(D24J0tG7h_>x5mC<8+>vC4XtLlNZ%7?XnpA!R5uI z5hGgpX3geh_ImuFG~{>!HPz{ke#?|>%dj(lPx(~`G^T5zWaO|6Sh5GhQ7}GEp;QsB z5n4gcyo+E^fd<+b14Trm=dIw6&ENZml}EdRHXk`DjC6sL$wC#k`%#d7fT9?W&}5<437a3i^7&}sILl?Uu@wDP6jq*o9G zgr^6$R6=Ej$)jFP-G@!+3+&Ws%Nt|9C)#su^fAc+@aacWa7+2QS(2AF2W^o$V7EQ&)ZCJfOT~e zK2PQOHKM9*&Ftx4(gm0}e;*{My&P0IyDyu2dJ)1sGCe*v2Wr#k=9F8`?Aq(i&m)3} z#F$j0ODvW;*YJ{2HRE()^aSt5M9}z&DQ229C3Y>p0gn~~H3tcu=Kz$*V zz!d`l{wcgV)XRl@&)uwoHZAs2v5<$nhJft30&_!gt1$d^@L6T8;w?N2pkW(gw~*?H_U&)vZBoz}v02oBNStJmJ!Sf@#(!eL}pmM4{(1P2^_Pjk<(h=-g4hLUmBjz5lXYnx1G^`7t&e61RQvP8+{cm7<}lc&K1 z{7;h@li|#5=c+eFq@e!Lqt!YQL?HE*s57PRdijX`>xr`X7l;c=)JLP#h zs?!T1wyUhOG_M(>EjDP;6oyQ6h)sK*EWV=$>S}IVF4LFe^dC z(cGABpUk5E-M@GDQ;YCnBu-_|a#6j+S&#Ov0yB*e9wT6_QniXW<#;QTRi1(aML*r}a_3>8uI$^t#14 zZQzn@Lb-R=TteRC??vh2NuT>OAO?v0woLbeEQvX$z|%g`zg zk7Lbf@k5C<%yC1Yp{bHOC?Z43B4RHdo5G=(b}^(zF{zj`l}0px#x%=Mni+7Pmui2m zBtqeYADM`nQ{AZ)0B{*u!+PRgxg53Eu+*~X_jZ!GP9E}$&Mh}-CrZ312$Xwv&E-V; zW!%%V%xA?#qGzR>jQkPgIk~Fn zXzAhFahfM@vR&2NrN$&Kw(_%vv%2C3rV21J?ev3Kphastl@ZgfO{2=#f`Tfy0a`TX zHL9YI#F!~X*9#I_`W~H09tAaJzOf0Vsj+GNiiD^+JF=(B0dL{cx+Ec)=wg`1x;o)% zU;esa3o9!5%dqB*{<##o)m6dXcpD?m>af9ymQLY#Y(Mcyf5(oy(nw$LH1V9vRR2T# z)beiZUXq}BozzU@q;}ITQB&HohMVboo>$kAsVxdk7G=c*`wB8{@Icg}a6+a6%!U6t zE7UPPGU|*YSdSHxhiq(+c$Q$$3%35md+M0vTO6_El+ zuH#jX4Ahp_Tn60i3q-dpukM8k3_lrp)9wM_{oMSXM{vtBsv|n|G#j~(0 z4Vr=dC2-ER@V4{v=G5e7J~L;cfrYdK1!QdtbP${%vO2aHult2&DdoVP)% zxzJq%|5ewcyy|*_|8)JPan8RZxi2bs;$HqPjdQs@`f0kfnKKK6X0hvA1ee+4b>u}| z;4T8at-}%IP=4#tyyAFb^d!vi1WvH(G}tea-NeL5bBc&9MLy~ck6i7Kk@qq%>#jWO zwz>yq`^H3!RH@vz^)VGJt^+xvq<(fo-4fCV33n8t@`lO*yebiAUY}cw*Pazt zs2ywLYgq6r^92(neolciR^MXyl~V@(gdxfsp=)t3CXEQ=@`1*JkUWWUZ9tR`C|8{+RK{3M^#1+Q&v)^ z99;A=)7XC}#ax|hmBkYoU`ux^J<5`i8pslQ295zEb%Tf2=D4l0LK>e{B#L4U znL}7sGLfeCZmL_adLOCQuVL?rn5Fh^E4);iH&zxwY-v&aEPMEbZ#ul{S;or~62<@J zAt_Od^mMn0wu?gWtFZsPi;r)${R7raG-Oeug-`(Z+ zJZ&#zA^P978F4u5@S=8IzVPO2kpJl??(MgZEGS(ejfXzFk(Ng!2{C zm8hno%60=AJijpDeSt2|i&ohMeykLxA>3A?!$<7HbJ^r6a&M0IxjALz9Jd4AB2Q4y z%{)Mu22II7eVo-ZqXe~S@_7OJ5}!9audh33A*WD(P1r&*eb^~C>fW)6)K|7Jdy|Pr zg{@`UOtt|{#TY4^RgfJ^mNcK0b8-eB%*^$&az`!KG(kYxr`myR1}X>0SVp+yZAiMm z5E1_e!SjRq!;qKKc#!Y~XyPPSgFKKQa1Lc@I7`Ta8v(Y+$F6xuGg&wz^!WS*agrSX zcjN~~^kV#9#B?MDHpKG@>U!z|ptF!|bayO8`X=u^Ic5&##2Jt750bg;6$$D#SdDKK zu`rS5n=H=Io3V~%D-6{s3fl^JLD{+iVGk&KZ^p1#>9vTpeb2EDL~#7wvY3Nul$K2MOx>>LfSz|4}fRF{@dBr7(z6 z>TsC7d1YqtZa#5lktgw$9O>)9gh7>J&6I@!Qf6USA**j#|Dyq9##QHfrA$OTpQzqZ zAPU+D8GGHq!khMivFta;?}%iXIV&g&8w(u6jzhLN1bKv<2#W(@*`7R=x%p~bv2<-W zjNH)~r}i#3tlTmCM0^(EV`dkitC~4C7=6;JcKyBSnX#0jG&lB!Z@SqZe%q{@)2@&b zhcQnck$1T0=b}MSj&El}YuG+GnG#$DJNatg0q@SABWJqHI7`POMu{m z<`{a#n&0g5%$4}X<8f&zpO!XC?;gpBzxZEVB^p4t&k5C)0EiL@|G$_R46ji!{+nq2 uC;fjSWMKa9N&o^0LKuptm~F-?;D2lV-%MQz1@*rI;B_{B%?T|2EBznH(dhyJ literal 0 HcmV?d00001 diff --git a/src/ui/SubtitleEdit.csproj b/src/ui/SubtitleEdit.csproj index d8eb752d0..b537a0b4d 100644 --- a/src/ui/SubtitleEdit.csproj +++ b/src/ui/SubtitleEdit.csproj @@ -2454,7 +2454,9 @@ + + Designer From 1500ab3bb07f0e275937b7fb267ca1cdaeb1f585 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sat, 27 Apr 2024 19:28:12 +0200 Subject: [PATCH 07/28] Fix crash in traslate - thx lambdacore12 :) Fix #8263 --- src/ui/Forms/Translate/AutoTranslate.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ui/Forms/Translate/AutoTranslate.cs b/src/ui/Forms/Translate/AutoTranslate.cs index dc8a903d7..eec68abb2 100644 --- a/src/ui/Forms/Translate/AutoTranslate.cs +++ b/src/ui/Forms/Translate/AutoTranslate.cs @@ -704,6 +704,20 @@ namespace Nikse.SubtitleEdit.Forms.Translate _autoTranslator = GetCurrentEngine(); var engineType = _autoTranslator.GetType(); + if (_autoTranslator.Name == DeepLTranslate.StaticName && string.IsNullOrWhiteSpace(nikseTextBoxApiKey.Text)) + { + MessageBox.Show(this, string.Format(LanguageSettings.Current.GoogleTranslate.XRequiresAnApiKey, _autoTranslator.Name), Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + _translationInProgress = false; + return; + } + + if (_autoTranslator.Name == DeepLTranslate.StaticName && string.IsNullOrWhiteSpace(nikseComboBoxUrl.Text)) + { + MessageBox.Show(this, string.Format("{0} require an url", _autoTranslator.Name), Text, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + _translationInProgress = false; + return; + } + SaveSettings(engineType); buttonOK.Enabled = false; From 1946176eef8f38195c9304da3ab24c53d0d0d444 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sat, 27 Apr 2024 19:48:20 +0200 Subject: [PATCH 08/28] Add new subtitle format - thx lererledd :) Fix #8247 --- src/libse/SubtitleFormats/SubtitleFormat.cs | 1 + src/libse/SubtitleFormats/WhisperRaw2.cs | 71 +++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/libse/SubtitleFormats/WhisperRaw2.cs diff --git a/src/libse/SubtitleFormats/SubtitleFormat.cs b/src/libse/SubtitleFormats/SubtitleFormat.cs index b3f90c5aa..ba1cc59f5 100644 --- a/src/libse/SubtitleFormats/SubtitleFormat.cs +++ b/src/libse/SubtitleFormats/SubtitleFormat.cs @@ -248,6 +248,7 @@ namespace Nikse.SubtitleEdit.Core.SubtitleFormats new WebVTT(), new WebVTTFileWithLineNumber(), new WhisperRaw(), + new WhisperRaw2(), new Xif(), new Xmp(), new YouTubeAnnotations(), diff --git a/src/libse/SubtitleFormats/WhisperRaw2.cs b/src/libse/SubtitleFormats/WhisperRaw2.cs new file mode 100644 index 000000000..044de86ab --- /dev/null +++ b/src/libse/SubtitleFormats/WhisperRaw2.cs @@ -0,0 +1,71 @@ +using Nikse.SubtitleEdit.Core.Common; +using Nikse.SubtitleEdit.Core.Enums; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Nikse.SubtitleEdit.Core.SubtitleFormats +{ + public class WhisperRaw2 : SubtitleFormat + { + private readonly Regex _timeRegex = new Regex(@"^\[\d+.\d+s -> \d+.\d+s\]", RegexOptions.Compiled); + public override string Extension => ".txt"; + public override string Name => "Whisper Raw 2"; + + public override string ToText(Subtitle subtitle, string title) + { + var sb = new StringBuilder(); + const string writeFormat = "[{0} -> {1}] {2}"; + foreach (var p in subtitle.Paragraphs) + { + sb.AppendLine(string.Format(writeFormat, EncodeEndTimeCode(p.StartTime), EncodeEndTimeCode(p.EndTime), HtmlUtil.RemoveHtmlTags(p.Text.Replace(Environment.NewLine, " "), true))); + sb.AppendLine(); + } + + return sb.ToString(); + } + + private static string EncodeEndTimeCode(TimeCode time) + { + return $"{time.TotalSeconds:0.00}s"; + } + + public override void LoadSubtitle(Subtitle subtitle, List lines, string fileName) + { + subtitle.Paragraphs.Clear(); + _errorCount = 0; + foreach (var line in lines) + { + var trimmedLine = line.Trim(); + if (trimmedLine.StartsWith('[')) + { + var match = _timeRegex.Match(trimmedLine); + if (match.Success) + { + var timeString = trimmedLine.Substring(0, match.Length).Trim('[', ']'); + var splitPos = timeString.IndexOf('>'); + if (splitPos > 1 && splitPos < timeString.Length - 3) + { + var start = timeString.Substring(0, splitPos -1).Trim().TrimEnd('s'); + var end = timeString.Substring(splitPos +1).Trim().TrimEnd('s'); + var text = trimmedLine.Remove(0, match.Length).Trim(); + if (!string.IsNullOrEmpty(text)) + { + if (double.TryParse(start, NumberStyles.Any, CultureInfo.InvariantCulture, out var dStart) && + double.TryParse(end, NumberStyles.Any, CultureInfo.InvariantCulture, out var dEnd)) + { + subtitle.Paragraphs.Add(new Paragraph(text, dStart * 1000.0, dEnd * 1000.0)); + } + } + } + } + } + } + + subtitle.Sort(SubtitleSortCriteria.StartTime); + subtitle.Renumber(); + } + } +} From bc3f369e90f6f500970b3150e59a401383d1a4b3 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sat, 27 Apr 2024 20:10:27 +0200 Subject: [PATCH 09/28] Do not auto-set assa style for existing ASSA files Fix #8249 --- src/ui/Forms/Main.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ui/Forms/Main.cs b/src/ui/Forms/Main.cs index 6177b58b1..6d71dfd50 100644 --- a/src/ui/Forms/Main.cs +++ b/src/ui/Forms/Main.cs @@ -23574,7 +23574,9 @@ namespace Nikse.SubtitleEdit.Forms private void SetAssaResolutionWithChecks() { - if (Configuration.Settings.SubtitleSettings.AssaResolutionAutoNew && IsAssa() && _videoInfo?.Height > 0) + if (Configuration.Settings.SubtitleSettings.AssaResolutionAutoNew && + string.IsNullOrEmpty(_subtitle?.Header) && + IsAssa() && _videoInfo?.Height > 0) { if (string.IsNullOrEmpty(_subtitle?.Header)) { From b57d744e8c344252c973ced0aea5ed0eec76c71a Mon Sep 17 00:00:00 2001 From: nkh0472 <67589323+nkh0472@users.noreply.github.com> Date: Sun, 28 Apr 2024 10:07:36 +0800 Subject: [PATCH 10/28] Update zh-Hans.xml for commit ecdcb7fe7e38a3db69c8b0ae00e98f60730a7bae --- src/ui/Languages/zh-Hans.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/Languages/zh-Hans.xml b/src/ui/Languages/zh-Hans.xml index dab37c52d..f34ea786d 100644 --- a/src/ui/Languages/zh-Hans.xml +++ b/src/ui/Languages/zh-Hans.xml @@ -2117,6 +2117,8 @@ Command line: {1} {2} 匹配行 时长小于 时长大于 + CPS 小于 + CP S大于 正好一行 正好两行 超过两行 From ba415101711c440219f5331dc6668e4439564838 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 10:16:25 +0200 Subject: [PATCH 11/28] Update change log --- Changelog.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.txt b/Changelog.txt index 528d11723..72397e323 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -5,6 +5,8 @@ * Add "Text To Speech" - thx darnn/cvrle77/msjsc001 * Add burn-in batch mode - thx Leon/David * Add new LRC with milliseconds format - thx eadmaster + * Add new subtitle format (Whisper output) - thx lererledd + * Add CPS option to modify selection - thx uckthis * IMPROVED: * Update Portuguese translation - thx hugok79 * Update Bulgarian translation - thx Калин @@ -22,6 +24,7 @@ * Fix unwanted text boxes background color change - thx Leon * Fix Whisper on selection in waveform in "translation mode" - thx rRobis * Fix image export shadow when border is zero - thx pixelhunterX + * Fix crash in traslate with empty API key - thx lambdacore12 4.0.5 (13th April 2024) From 156513f453cdb1e38668fe1633e2c4a101b406c7 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 10:27:25 +0200 Subject: [PATCH 12/28] Add delete key action for "unknown words" in OCR - thx RedSoxFan04 :) Fix #8227 --- src/ui/Forms/Ocr/VobSubOcr.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ui/Forms/Ocr/VobSubOcr.cs b/src/ui/Forms/Ocr/VobSubOcr.cs index 9823c69f5..4427632f7 100644 --- a/src/ui/Forms/Ocr/VobSubOcr.cs +++ b/src/ui/Forms/Ocr/VobSubOcr.cs @@ -8600,6 +8600,13 @@ namespace Nikse.SubtitleEdit.Forms.Ocr // ignored } } + + e.Handled = true; + } + else if (e.KeyCode == Keys.Delete) + { + removeAllXToolStripMenuItem_Click(null, null); + e.Handled = true; } } From 2122a8dc30241841affe1aedc792cb3d135be657 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 17:25:29 +0200 Subject: [PATCH 13/28] Add some missing translations for tts --- LanguageBaseEnglish.xml | 5 +++++ src/libse/Common/Settings.cs | 17 +++++++++++++++++ .../Forms/Tts/RegenerateAudioClip.Designer.cs | 2 +- src/ui/Forms/Tts/RegenerateAudioClip.cs | 6 +++++- src/ui/Forms/Tts/ReviewAudioClips.cs | 8 ++++++++ src/ui/Forms/Tts/TextToSpeech.cs | 6 ++++++ src/ui/Logic/Language.cs | 5 +++++ src/ui/Logic/LanguageDeserializer.cs | 15 +++++++++++++++ src/ui/Logic/LanguageStructure.cs | 5 +++++ 9 files changed, 67 insertions(+), 2 deletions(-) diff --git a/LanguageBaseEnglish.xml b/LanguageBaseEnglish.xml index ee2497507..963f2d70a 100644 --- a/LanguageBaseEnglish.xml +++ b/LanguageBaseEnglish.xml @@ -3168,6 +3168,11 @@ Continue? Adjusting speed: {0} / {1}... Merging audio track: {0} / {1}... Generating speech from text: {0} / {1}... + Review audio clips + Review and edit/remove audio clips + Play + Auto-continue + Regenerate SMPTE timing diff --git a/src/libse/Common/Settings.cs b/src/libse/Common/Settings.cs index 4c7804703..22041a59a 100644 --- a/src/libse/Common/Settings.cs +++ b/src/libse/Common/Settings.cs @@ -195,6 +195,8 @@ namespace Nikse.SubtitleEdit.Core.Common public string TextToSpeechElevenLabsApiKey { get; set; } public string TextToSpeechAzureApiKey { get; set; } public string TextToSpeechAzureRegion { get; set; } + public bool TextToSpeechPreview { get; set; } + public bool TextToSpeechAddToVideoFile { get; set; } public bool DisableVidoInfoViaLabel { get; set; } public bool ListViewSyntaxColorDurationSmall { get; set; } public bool ListViewSyntaxColorDurationBig { get; set; } @@ -560,6 +562,7 @@ namespace Nikse.SubtitleEdit.Core.Common AnthropicPrompt = "Translate from {0} to {1}, keep sentences in {1} as they are, do not censor the translation, give only the output without commenting on what you read:"; AnthropicApiModel = "claude-3-opus-20240229"; TextToSpeechAzureRegion = "westeurope"; + TextToSpeechAddToVideoFile = true; TranslateAllowSplit = true; TranslateViaCopyPasteAutoCopyToClipboard = true; TranslateViaCopyPasteMaxSize = 5000; @@ -5502,6 +5505,18 @@ $HorzAlign = Center settings.Tools.TranslateViaCopyPasteAutoCopyToClipboard = Convert.ToBoolean(subNode.InnerText, CultureInfo.InvariantCulture); } + subNode = node.SelectSingleNode("TextToSpeechPreview"); + if (subNode != null) + { + settings.Tools.TextToSpeechPreview = Convert.ToBoolean(subNode.InnerText, CultureInfo.InvariantCulture); + } + + subNode = node.SelectSingleNode("TextToSpeechAddToVideoFile"); + if (subNode != null) + { + settings.Tools.TextToSpeechAddToVideoFile = Convert.ToBoolean(subNode.InnerText, CultureInfo.InvariantCulture); + } + subNode = node.SelectSingleNode("DisableVidoInfoViaLabel"); if (subNode != null) { @@ -12040,6 +12055,8 @@ $HorzAlign = Center textWriter.WriteElementString("TextToSpeechElevenLabsApiKey", settings.Tools.TextToSpeechElevenLabsApiKey); textWriter.WriteElementString("TextToSpeechAzureApiKey", settings.Tools.TextToSpeechAzureApiKey); textWriter.WriteElementString("TextToSpeechAzureRegion", settings.Tools.TextToSpeechAzureRegion); + textWriter.WriteElementString("TextToSpeechPreview", settings.Tools.TextToSpeechPreview.ToString(CultureInfo.InvariantCulture)); + textWriter.WriteElementString("TextToSpeechAddToVideoFile", settings.Tools.TextToSpeechAddToVideoFile.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("DisableVidoInfoViaLabel", settings.Tools.DisableVidoInfoViaLabel.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationSmall", settings.Tools.ListViewSyntaxColorDurationSmall.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationBig", settings.Tools.ListViewSyntaxColorDurationBig.ToString(CultureInfo.InvariantCulture)); diff --git a/src/ui/Forms/Tts/RegenerateAudioClip.Designer.cs b/src/ui/Forms/Tts/RegenerateAudioClip.Designer.cs index f08f44cba..aedece8f0 100644 --- a/src/ui/Forms/Tts/RegenerateAudioClip.Designer.cs +++ b/src/ui/Forms/Tts/RegenerateAudioClip.Designer.cs @@ -1,6 +1,6 @@ namespace Nikse.SubtitleEdit.Forms.Tts { - partial class RegenerateAudioClip + sealed partial class RegenerateAudioClip { ///

/// Required designer variable. diff --git a/src/ui/Forms/Tts/RegenerateAudioClip.cs b/src/ui/Forms/Tts/RegenerateAudioClip.cs index 4dd960de3..9820fea43 100644 --- a/src/ui/Forms/Tts/RegenerateAudioClip.cs +++ b/src/ui/Forms/Tts/RegenerateAudioClip.cs @@ -5,7 +5,7 @@ using System.Windows.Forms; namespace Nikse.SubtitleEdit.Forms.Tts { - public partial class RegenerateAudioClip : Form + public sealed partial class RegenerateAudioClip : Form { public TextToSpeech.FileNameAndSpeedFactor FileNameAndSpeedFactor { get; set; } @@ -23,6 +23,10 @@ namespace Nikse.SubtitleEdit.Forms.Tts _textToSpeech = textToSpeech; _index = idx; + Text = LanguageSettings.Current.ExportCustomText.Edit; + labelText.Text = LanguageSettings.Current.General.Text; + labelVoice.Text = LanguageSettings.Current.TextToSpeech.Voice; + buttonReGenerate.Text = LanguageSettings.Current.TextToSpeech.Regenerate; buttonCancel.Text = LanguageSettings.Current.General.Cancel; UiUtil.FixLargeFonts(this, buttonCancel); diff --git a/src/ui/Forms/Tts/ReviewAudioClips.cs b/src/ui/Forms/Tts/ReviewAudioClips.cs index 763a2eb3d..0e018451e 100644 --- a/src/ui/Forms/Tts/ReviewAudioClips.cs +++ b/src/ui/Forms/Tts/ReviewAudioClips.cs @@ -24,6 +24,14 @@ namespace Nikse.SubtitleEdit.Forms.Tts UiUtil.PreInitialize(this); InitializeComponent(); UiUtil.FixFonts(this); + Text = LanguageSettings.Current.TextToSpeech.ReviewAudioClips; + buttonEdit.Text = LanguageSettings.Current.ExportCustomText.Edit; + buttonPlay.Text = LanguageSettings.Current.TextToSpeech.Play; + checkBoxContinuePlay.Text = LanguageSettings.Current.TextToSpeech.AutoContinue; + columnHeaderCps.Text = LanguageSettings.Current.General.CharsPerSec; + columnHeaderInclude.Text = string.Empty; // include + columnHeaderText.Text = LanguageSettings.Current.General.Text; + columnHeaderVoice.Text = LanguageSettings.Current.TextToSpeech.Voice; buttonOK.Text = LanguageSettings.Current.General.Ok; UiUtil.FixLargeFonts(this, buttonOK); diff --git a/src/ui/Forms/Tts/TextToSpeech.cs b/src/ui/Forms/Tts/TextToSpeech.cs index 5b1e0eecc..76a099807 100644 --- a/src/ui/Forms/Tts/TextToSpeech.cs +++ b/src/ui/Forms/Tts/TextToSpeech.cs @@ -117,6 +117,8 @@ namespace Nikse.SubtitleEdit.Forms.Tts labelActors.Text = LanguageSettings.Current.TextToSpeech.ActorInfo; checkBoxAddToVideoFile.Text = LanguageSettings.Current.TextToSpeech.AddAudioToVideo; buttonGenerateTTS.Text = LanguageSettings.Current.TextToSpeech.GenerateSpeech; + labelRegion.Text = LanguageSettings.Current.General.Region; + checkBoxShowPreview.Text = LanguageSettings.Current.TextToSpeech.ReviewAudioClips; buttonOK.Text = LanguageSettings.Current.General.Ok; buttonCancel.Text = LanguageSettings.Current.General.Cancel; UiUtil.FixLargeFonts(this, buttonOK); @@ -172,6 +174,8 @@ namespace Nikse.SubtitleEdit.Forms.Tts nikseComboBoxEngine_SelectedIndexChanged(null, null); nikseComboBoxEngine.SelectedIndexChanged += nikseComboBoxEngine_SelectedIndexChanged; nikseComboBoxVoice.Text = Configuration.Settings.Tools.TextToSpeechLastVoice; + checkBoxAddToVideoFile.Checked = Configuration.Settings.Tools.TextToSpeechAddToVideoFile; + checkBoxShowPreview.Checked = Configuration.Settings.Tools.TextToSpeechPreview; } private void SetActor(ActorAndVoice actor) @@ -1775,6 +1779,8 @@ namespace Nikse.SubtitleEdit.Forms.Tts Configuration.Settings.Tools.TextToSpeechEngine = engine.Id.ToString(); Configuration.Settings.Tools.TextToSpeechLastVoice = nikseComboBoxVoice.Text; + Configuration.Settings.Tools.TextToSpeechAddToVideoFile = checkBoxAddToVideoFile.Checked; + Configuration.Settings.Tools.TextToSpeechPreview = checkBoxShowPreview.Checked; } private void TextToSpeech_KeyDown(object sender, KeyEventArgs e) diff --git a/src/ui/Logic/Language.cs b/src/ui/Logic/Language.cs index 3c387d048..eaad168dd 100644 --- a/src/ui/Logic/Language.cs +++ b/src/ui/Logic/Language.cs @@ -3565,6 +3565,11 @@ can edit in same subtitle file (collaboration)", AdjustingSpeedXOfY = "Adjusting speed: {0} / {1}...", MergingAudioTrackXOfY = "Merging audio track: {0} / {1}...", GeneratingSpeechFromTextXOfY = "Generating speech from text: {0} / {1}...", + ReviewAudioClips = "Review audio clips", + ReviewInfo = "Review and edit/remove audio clips", + AutoContinue = "Auto-continue", + Play = "Play", + Regenerate = "Regenerate", }; TimedTextSmpteTiming = new LanguageStructure.TimedTextSmpteTiming diff --git a/src/ui/Logic/LanguageDeserializer.cs b/src/ui/Logic/LanguageDeserializer.cs index 0314a86ec..eebd8ade2 100644 --- a/src/ui/Logic/LanguageDeserializer.cs +++ b/src/ui/Logic/LanguageDeserializer.cs @@ -8677,6 +8677,21 @@ namespace Nikse.SubtitleEdit.Logic case "TextToSpeech/GeneratingSpeechFromTextXOfY": language.TextToSpeech.GeneratingSpeechFromTextXOfY = reader.Value; break; + case "TextToSpeech/ReviewAudioClips": + language.TextToSpeech.ReviewAudioClips = reader.Value; + break; + case "TextToSpeech/ReviewInfo": + language.TextToSpeech.ReviewInfo = reader.Value; + break; + case "TextToSpeech/Play": + language.TextToSpeech.Play = reader.Value; + break; + case "TextToSpeech/AutoContinue": + language.TextToSpeech.AutoContinue = reader.Value; + break; + case "TextToSpeech/Regenerate": + language.TextToSpeech.Regenerate = reader.Value; + break; case "TimedTextSmpteTiming/Title": language.TimedTextSmpteTiming.Title = reader.Value; break; diff --git a/src/ui/Logic/LanguageStructure.cs b/src/ui/Logic/LanguageStructure.cs index a90171f46..97de53031 100644 --- a/src/ui/Logic/LanguageStructure.cs +++ b/src/ui/Logic/LanguageStructure.cs @@ -3374,6 +3374,11 @@ public string AdjustingSpeedXOfY { get; set; } public string MergingAudioTrackXOfY { get; set; } public string GeneratingSpeechFromTextXOfY { get; set; } + public string ReviewAudioClips { get; set; } + public string ReviewInfo { get; set; } + public string Play { get; set; } + public string AutoContinue { get; set; } + public string Regenerate { get; set; } } public class TimedTextSmpteTiming From 443d88d1959c1d100392c28c3af9d3d4af09aceb Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 18:33:49 +0200 Subject: [PATCH 14/28] Allow sort/move for "Add end time of prev" in "Join subtitles" - thx Leon :) Work on #8257 --- src/ui/Forms/JoinSubtitles.Designer.cs | 58 +++++++ src/ui/Forms/JoinSubtitles.cs | 204 +++++++++++++++++++++++++ src/ui/Forms/JoinSubtitles.resx | 3 + src/ui/Logic/ListViewSorter.cs | 2 +- 4 files changed, 266 insertions(+), 1 deletion(-) diff --git a/src/ui/Forms/JoinSubtitles.Designer.cs b/src/ui/Forms/JoinSubtitles.Designer.cs index 69208c17e..b04b216ac 100644 --- a/src/ui/Forms/JoinSubtitles.Designer.cs +++ b/src/ui/Forms/JoinSubtitles.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); this.buttonCancel = new System.Windows.Forms.Button(); this.buttonJoin = new System.Windows.Forms.Button(); this.listViewParts = new System.Windows.Forms.ListView(); @@ -45,7 +46,13 @@ this.numericUpDownAddMs = new Nikse.SubtitleEdit.Controls.NikseUpDown(); this.radioButtonJoinAddTime = new System.Windows.Forms.RadioButton(); this.labelAddTime = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.contextMenuStripParts = new System.Windows.Forms.ContextMenuStrip(this.components); + this.moveUpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.moveDownToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.moveTopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.moveBottomToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.groupBoxPreview.SuspendLayout(); + this.contextMenuStripParts.SuspendLayout(); this.SuspendLayout(); // // buttonCancel @@ -83,6 +90,7 @@ this.columnHeaderStartTime, this.columnHeaderEndTime, this.columnHeaderFileName}); + this.listViewParts.ContextMenuStrip = this.contextMenuStripParts; this.listViewParts.FullRowSelect = true; this.listViewParts.HideSelection = false; this.listViewParts.Location = new System.Drawing.Point(6, 19); @@ -91,6 +99,7 @@ this.listViewParts.TabIndex = 101; this.listViewParts.UseCompatibleStateImageBehavior = false; this.listViewParts.View = System.Windows.Forms.View.Details; + this.listViewParts.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listViewParts_ColumnClick); this.listViewParts.DragDrop += new System.Windows.Forms.DragEventHandler(this.listViewParts_DragDrop); this.listViewParts.DragEnter += new System.Windows.Forms.DragEventHandler(this.listViewParts_DragEnter); // @@ -252,6 +261,49 @@ this.labelAddTime.TabIndex = 34; this.labelAddTime.Text = "Add milliseconds after each file"; // + // contextMenuStripParts + // + this.contextMenuStripParts.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.moveUpToolStripMenuItem, + this.moveDownToolStripMenuItem, + this.moveTopToolStripMenuItem, + this.moveBottomToolStripMenuItem}); + this.contextMenuStripParts.Name = "contextMenuStrip1"; + this.contextMenuStripParts.Size = new System.Drawing.Size(216, 92); + this.contextMenuStripParts.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripParts_Opening); + // + // moveUpToolStripMenuItem + // + this.moveUpToolStripMenuItem.Name = "moveUpToolStripMenuItem"; + this.moveUpToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Up))); + this.moveUpToolStripMenuItem.Size = new System.Drawing.Size(215, 22); + this.moveUpToolStripMenuItem.Text = "Move up"; + this.moveUpToolStripMenuItem.Click += new System.EventHandler(this.moveUpToolStripMenuItem_Click); + // + // moveDownToolStripMenuItem + // + this.moveDownToolStripMenuItem.Name = "moveDownToolStripMenuItem"; + this.moveDownToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Down))); + this.moveDownToolStripMenuItem.Size = new System.Drawing.Size(215, 22); + this.moveDownToolStripMenuItem.Text = "Move down"; + this.moveDownToolStripMenuItem.Click += new System.EventHandler(this.moveDownToolStripMenuItem_Click); + // + // moveTopToolStripMenuItem + // + this.moveTopToolStripMenuItem.Name = "moveTopToolStripMenuItem"; + this.moveTopToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Home))); + this.moveTopToolStripMenuItem.Size = new System.Drawing.Size(215, 22); + this.moveTopToolStripMenuItem.Text = "Move to top"; + this.moveTopToolStripMenuItem.Click += new System.EventHandler(this.moveTopToolStripMenuItem_Click); + // + // moveBottomToolStripMenuItem + // + this.moveBottomToolStripMenuItem.Name = "moveBottomToolStripMenuItem"; + this.moveBottomToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.End))); + this.moveBottomToolStripMenuItem.Size = new System.Drawing.Size(215, 22); + this.moveBottomToolStripMenuItem.Text = "Move to bottom"; + this.moveBottomToolStripMenuItem.Click += new System.EventHandler(this.moveBottomToolStripMenuItem_Click); + // // JoinSubtitles // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -279,6 +331,7 @@ this.Resize += new System.EventHandler(this.JoinSubtitles_Resize); this.groupBoxPreview.ResumeLayout(false); this.groupBoxPreview.PerformLayout(); + this.contextMenuStripParts.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -303,5 +356,10 @@ private Nikse.SubtitleEdit.Controls.NikseUpDown numericUpDownAddMs; private System.Windows.Forms.RadioButton radioButtonJoinAddTime; private Nikse.SubtitleEdit.Controls.NikseLabel labelAddTime; + private System.Windows.Forms.ContextMenuStrip contextMenuStripParts; + private System.Windows.Forms.ToolStripMenuItem moveUpToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem moveDownToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem moveTopToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem moveBottomToolStripMenuItem; } } \ No newline at end of file diff --git a/src/ui/Forms/JoinSubtitles.cs b/src/ui/Forms/JoinSubtitles.cs index 823bf5d55..79e3b69ae 100644 --- a/src/ui/Forms/JoinSubtitles.cs +++ b/src/ui/Forms/JoinSubtitles.cs @@ -30,6 +30,11 @@ namespace Nikse.SubtitleEdit.Forms listViewParts.Columns[2].Text = LanguageSettings.Current.JoinSubtitles.EndTime; listViewParts.Columns[3].Text = LanguageSettings.Current.JoinSubtitles.FileName; + moveUpToolStripMenuItem.Text = LanguageSettings.Current.DvdSubRip.MoveUp; + moveDownToolStripMenuItem.Text = LanguageSettings.Current.DvdSubRip.MoveDown; + moveTopToolStripMenuItem.Text = LanguageSettings.Current.MultipleReplace.MoveToTop; + moveBottomToolStripMenuItem.Text = LanguageSettings.Current.MultipleReplace.MoveToBottom; + buttonAddFile.Text = LanguageSettings.Current.DvdSubRip.Add; buttonRemoveFile.Text = LanguageSettings.Current.DvdSubRip.Remove; buttonClear.Text = LanguageSettings.Current.DvdSubRip.Clear; @@ -388,6 +393,7 @@ namespace Nikse.SubtitleEdit.Forms { MessageBox.Show(sb.ToString()); } + JoinSubtitles_Resize(sender, e); } } @@ -438,5 +444,203 @@ namespace Nikse.SubtitleEdit.Forms labelAddTime.Enabled = radioButtonJoinAddTime.Checked; SortAndLoad(); } + + private void contextMenuStripParts_Opening(object sender, System.ComponentModel.CancelEventArgs e) + { + if (radioButtonJoinPlain.Checked) + { + e.Cancel = true; + } + } + + private void MoveUp(ListView listView) + { + if (listView.SelectedItems.Count != 1) + { + return; + } + + var idx = listView.SelectedItems[0].Index; + if (idx == 0) + { + return; + } + + var item = listView.SelectedItems[0]; + listView.Items.RemoveAt(idx); + var style = _fileNamesToJoin[idx]; + _fileNamesToJoin.RemoveAt(idx); + _fileNamesToJoin.Insert(idx - 1, style); + + idx--; + listView.Items.Insert(idx, item); + UpdateSelectedIndices(listView, idx); + } + + private void MoveDown(ListView listView) + { + if (listView.SelectedItems.Count != 1) + { + return; + } + + var idx = listView.SelectedItems[0].Index; + if (idx >= listView.Items.Count - 1) + { + return; + } + + var item = listView.SelectedItems[0]; + listView.Items.RemoveAt(idx); + var style = _fileNamesToJoin[idx]; + _fileNamesToJoin.RemoveAt(idx); + _fileNamesToJoin.Insert(idx + 1, style); + + idx++; + listView.Items.Insert(idx, item); + UpdateSelectedIndices(listView, idx); + } + + private void MoveToTop(ListView listView) + { + if (listView.SelectedItems.Count != 1) + { + return; + } + + var idx = listView.SelectedItems[0].Index; + if (idx == 0) + { + return; + } + + var item = listView.SelectedItems[0]; + listView.Items.RemoveAt(idx); + var style = _fileNamesToJoin[idx]; + _fileNamesToJoin.RemoveAt(idx); + _fileNamesToJoin.Insert(0, style); + + idx = 0; + listView.Items.Insert(idx, item); + UpdateSelectedIndices(listView, idx); + } + + private void MoveToBottom(ListView listView) + { + if (listView.SelectedItems.Count != 1) + { + return; + } + + var idx = listView.SelectedItems[0].Index; + if (idx == listView.Items.Count - 1) + { + return; + } + + var item = listView.SelectedItems[0]; + listView.Items.RemoveAt(idx); + var style = _fileNamesToJoin[idx]; + _fileNamesToJoin.RemoveAt(idx); + _fileNamesToJoin.Add(style); + + listView.Items.Add(item); + UpdateSelectedIndices(listView); + } + + private static void UpdateSelectedIndices(ListView listView, int startingIndex = -1, int numberOfSelectedItems = 1) + { + if (numberOfSelectedItems == 0) + { + return; + } + + if (startingIndex == -1 || startingIndex >= listView.Items.Count) + { + startingIndex = listView.Items.Count - 1; + } + + if (startingIndex - numberOfSelectedItems < -1) + { + return; + } + + listView.SelectedItems.Clear(); + for (var i = 0; i < numberOfSelectedItems; i++) + { + listView.Items[startingIndex - i].Selected = true; + listView.Items[startingIndex - i].EnsureVisible(); + listView.Items[startingIndex - i].Focused = true; + } + } + + private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) + { + if (radioButtonJoinPlain.Checked) + { + return; + } + + MoveUp(listViewParts); + } + + private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) + { + if (radioButtonJoinPlain.Checked) + { + return; + } + + MoveDown(listViewParts); + } + + private void moveTopToolStripMenuItem_Click(object sender, EventArgs e) + { + if (radioButtonJoinPlain.Checked) + { + return; + } + + MoveToTop(listViewParts); + } + + private void moveBottomToolStripMenuItem_Click(object sender, EventArgs e) + { + if (radioButtonJoinPlain.Checked) + { + return; + } + + MoveToBottom(listViewParts); + } + + private void listViewParts_ColumnClick(object sender, ColumnClickEventArgs e) + { + if (radioButtonJoinPlain.Checked || e.Column != 3) + { + return; + } + + var lv = (ListView)sender; + if (!(lv.ListViewItemSorter is ListViewSorter sorter)) + { + sorter = new ListViewSorter + { + ColumnNumber = e.Column, + }; + lv.ListViewItemSorter = sorter; + } + + if (e.Column == sorter.ColumnNumber) + { + sorter.Descending = !sorter.Descending; // inverse sort direction + } + else + { + sorter.ColumnNumber = e.Column; + } + + lv.Sort(); + } } } diff --git a/src/ui/Forms/JoinSubtitles.resx b/src/ui/Forms/JoinSubtitles.resx index 9bad2f599..55f72738c 100644 --- a/src/ui/Forms/JoinSubtitles.resx +++ b/src/ui/Forms/JoinSubtitles.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 159, 22 + 17, 17 diff --git a/src/ui/Logic/ListViewSorter.cs b/src/ui/Logic/ListViewSorter.cs index f9c2a8a92..c56b210f5 100644 --- a/src/ui/Logic/ListViewSorter.cs +++ b/src/ui/Logic/ListViewSorter.cs @@ -13,7 +13,7 @@ namespace Nikse.SubtitleEdit.Logic public bool IsDisplayFileSize { get; set; } public bool Descending { get; set; } - private Regex _invariantNumber = new Regex(@"\d+\.{1,2}", RegexOptions.Compiled); + private readonly Regex _invariantNumber = new Regex(@"\d+\.{1,2}", RegexOptions.Compiled); public int Compare(object o1, object o2) { From 0c28e8a0ec0014d8c2b57cccccd2c9a893b53aa5 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 19:09:03 +0200 Subject: [PATCH 15/28] Add sort mark to join subs - thx Leon :) Work on #8257 --- src/ui/Forms/GenerateVideoWithHardSubs.cs | 38 ++++++++++++++++------- src/ui/Forms/JoinSubtitles.cs | 25 +++++++++++++++ 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/ui/Forms/GenerateVideoWithHardSubs.cs b/src/ui/Forms/GenerateVideoWithHardSubs.cs index 9bfe80d3a..4458db121 100644 --- a/src/ui/Forms/GenerateVideoWithHardSubs.cs +++ b/src/ui/Forms/GenerateVideoWithHardSubs.cs @@ -29,7 +29,7 @@ namespace Nikse.SubtitleEdit.Forms private long _totalFrames; private StringBuilder _log; private readonly bool _isAssa; - private readonly FfmpegMediaInfo _mediaInfo; + private FfmpegMediaInfo _mediaInfo; private bool _promptFFmpegParameters; private readonly bool _mpvOn; private readonly string _mpvSubtitleFileName; @@ -248,7 +248,6 @@ namespace Nikse.SubtitleEdit.Forms UiUtil.FixLargeFonts(this, buttonGenerate); UiUtil.FixFonts(this, 2000); - _mediaInfo = FfmpegMediaInfo.Parse(inputVideoFileName); if (_videoInfo != null && _videoInfo.TotalSeconds > 0) @@ -459,6 +458,8 @@ namespace Nikse.SubtitleEdit.Forms labelPleaseWait.Text = $"{index + 1}/{_batchVideoAndSubList.Count} - {LanguageSettings.Current.General.PleaseWait}"; var videoAndSub = _batchVideoAndSubList[index]; + + _mediaInfo = FfmpegMediaInfo.Parse(videoAndSub.VideoFileName); _videoInfo = UiUtil.GetVideoInfo(videoAndSub.VideoFileName); if (useSourceResolution) { @@ -2162,25 +2163,38 @@ namespace Nikse.SubtitleEdit.Forms item.SubtitleFileFileSizeInBytes = new FileInfo(subFileName).Length; } - - var vInfo = new VideoInfo { Success = false }; - if (fileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)) + var width = 0; + var height = 0; + var mediaInfo = FfmpegMediaInfo.Parse(fileName); + if (mediaInfo.VideoWidth > 0 && mediaInfo.VideoHeight > 0) { - vInfo = QuartsPlayer.GetVideoInfo(fileName); + width = mediaInfo.VideoWidth; + height = mediaInfo.VideoHeight; + } + else + { + var vInfo = new VideoInfo { Success = false }; + if (fileName.EndsWith(".mp4", StringComparison.OrdinalIgnoreCase)) + { + vInfo = QuartsPlayer.GetVideoInfo(fileName); + if (!vInfo.Success) + { + vInfo = LibMpvDynamic.GetVideoInfo(fileName); + } + } + if (!vInfo.Success) { - vInfo = LibMpvDynamic.GetVideoInfo(fileName); + vInfo = UiUtil.GetVideoInfo(fileName); } - } - if (!vInfo.Success) - { - vInfo = UiUtil.GetVideoInfo(fileName); + width = vInfo.Width; + height = vInfo.Height; } var listViewItem = new ListViewItem(fileName); listViewItem.Tag = item; - listViewItem.SubItems.Add($"{vInfo.Width}x{vInfo.Height}"); + listViewItem.SubItems.Add($"{width}x{height}"); var s = Utilities.FormatBytesToDisplayFileSize(item.VideoFileSizeInBytes); listViewItem.SubItems.Add(s); listViewItem.SubItems.Add(Path.GetFileName(item.SubtitleFileName)); diff --git a/src/ui/Forms/JoinSubtitles.cs b/src/ui/Forms/JoinSubtitles.cs index 79e3b69ae..9035c0988 100644 --- a/src/ui/Forms/JoinSubtitles.cs +++ b/src/ui/Forms/JoinSubtitles.cs @@ -3,6 +3,7 @@ using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Logic; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -443,6 +444,7 @@ namespace Nikse.SubtitleEdit.Forms numericUpDownAddMs.Enabled = radioButtonJoinAddTime.Checked; labelAddTime.Enabled = radioButtonJoinAddTime.Checked; SortAndLoad(); + SetSortArrow(listViewParts.Columns[3], SortOrder.None); } private void contextMenuStripParts_Opening(object sender, System.ComponentModel.CancelEventArgs e) @@ -581,6 +583,7 @@ namespace Nikse.SubtitleEdit.Forms return; } + SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveUp(listViewParts); } @@ -591,6 +594,7 @@ namespace Nikse.SubtitleEdit.Forms return; } + SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveDown(listViewParts); } @@ -601,6 +605,7 @@ namespace Nikse.SubtitleEdit.Forms return; } + SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveToTop(listViewParts); } @@ -611,6 +616,7 @@ namespace Nikse.SubtitleEdit.Forms return; } + SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveToBottom(listViewParts); } @@ -641,6 +647,25 @@ namespace Nikse.SubtitleEdit.Forms } lv.Sort(); + + SetSortArrow(listViewParts.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); + } + + private static void SetSortArrow(ColumnHeader head, SortOrder order) + { + const string ascArrow = " ▲"; + const string descArrow = " ▼"; + + // remove arrow + if (head.Text.EndsWith(ascArrow) || head.Text.EndsWith(descArrow)) + head.Text = head.Text.Substring(0, head.Text.Length - 2); + + // add arrow + switch (order) + { + case SortOrder.Ascending: head.Text += ascArrow; break; + case SortOrder.Descending: head.Text += descArrow; break; + } } } } From 6a3ddce7dd158fdd37dad0e73289dbb14802cd03 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 19:09:20 +0200 Subject: [PATCH 16/28] Add video res to ffmpeg info --- src/libse/Common/FfmpegMediaInfo.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/libse/Common/FfmpegMediaInfo.cs b/src/libse/Common/FfmpegMediaInfo.cs index 1ded83ab0..c6bfe1050 100644 --- a/src/libse/Common/FfmpegMediaInfo.cs +++ b/src/libse/Common/FfmpegMediaInfo.cs @@ -4,12 +4,17 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Text; +using System.Text.RegularExpressions; namespace Nikse.SubtitleEdit.Core.Common { public class FfmpegMediaInfo { public List Tracks { get; set; } + public int VideoWidth { get; set; } + public int VideoHeight { get; set; } + + private static readonly Regex ResolutionRegex = new Regex(@"\d\d+x\d\d+", RegexOptions.Compiled); private FfmpegMediaInfo() { @@ -19,7 +24,7 @@ namespace Nikse.SubtitleEdit.Core.Common public static FfmpegMediaInfo Parse(string videoFileName) { if (string.IsNullOrEmpty(Configuration.Settings.General.FFmpegLocation) || - !File.Exists(Configuration.Settings.General.FFmpegLocation)) + !File.Exists(Configuration.Settings.General.FFmpegLocation)) { return new FfmpegMediaInfo(); } @@ -56,6 +61,20 @@ namespace Nikse.SubtitleEdit.Core.Common var s = line.Trim(); if (s.StartsWith("Stream #", StringComparison.Ordinal)) { + var resolutionMatch = ResolutionRegex.Match(s); + if (resolutionMatch.Success) + { + var parts = resolutionMatch.Value.Split('x'); + if (info.VideoWidth == 0 && + parts.Length == 2 && + int.TryParse(parts[0], out var w) && + int.TryParse(parts[0], out var h)) + { + info.VideoWidth = w; + info.VideoHeight = h; + } + } + var arr = s.Replace(": ", "¤").Split('¤'); if (arr.Length == 3) { @@ -123,7 +142,7 @@ namespace Nikse.SubtitleEdit.Core.Common StartInfo = { FileName = ffmpegLocation, - Arguments = $"-i \"{inputFileName}\" - hide_banner", + Arguments = $"-i \"{inputFileName}\" -hide_banner", UseShellExecute = false, CreateNoWindow = true } From 59ab0fca5b74368bafe69c46c47529b675baadd8 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 20:36:47 +0200 Subject: [PATCH 17/28] More work on #8257 --- src/ui/Forms/JoinSubtitles.cs | 23 ++++++++++++----------- src/ui/Logic/ListViewSorter.cs | 10 ++++++++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/ui/Forms/JoinSubtitles.cs b/src/ui/Forms/JoinSubtitles.cs index 9035c0988..f78641c14 100644 --- a/src/ui/Forms/JoinSubtitles.cs +++ b/src/ui/Forms/JoinSubtitles.cs @@ -3,11 +3,11 @@ using Nikse.SubtitleEdit.Core.SubtitleFormats; using Nikse.SubtitleEdit.Logic; using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; +using Nikse.SubtitleEdit.Core.Enums; using MessageBox = Nikse.SubtitleEdit.Forms.SeMsgBox.MessageBox; namespace Nikse.SubtitleEdit.Forms @@ -94,13 +94,14 @@ namespace Nikse.SubtitleEdit.Forms private void listViewParts_DragDrop(object sender, DragEventArgs e) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); - foreach (string fileName in files) + foreach (var fileName in files) { if (!_fileNamesToJoin.Any(file => file.Equals(fileName, StringComparison.OrdinalIgnoreCase))) { _fileNamesToJoin.Add(fileName); } } + SortAndLoad(); } @@ -266,21 +267,16 @@ namespace Nikse.SubtitleEdit.Forms if (!radioButtonJoinAddTime.Checked) { - for (int outer = 0; outer < subtitles.Count; outer++) + for (var outer = 0; outer < subtitles.Count; outer++) { - for (int inner = 1; inner < subtitles.Count; inner++) + for (var inner = 1; inner < subtitles.Count; inner++) { var a = subtitles[inner - 1]; var b = subtitles[inner]; if (a.Paragraphs.Count > 0 && b.Paragraphs.Count > 0 && a.Paragraphs[0].StartTime.TotalMilliseconds > b.Paragraphs[0].StartTime.TotalMilliseconds) { - var t1 = _fileNamesToJoin[inner - 1]; - _fileNamesToJoin[inner - 1] = _fileNamesToJoin[inner]; - _fileNamesToJoin[inner] = t1; - - var t2 = subtitles[inner - 1]; - subtitles[inner - 1] = subtitles[inner]; - subtitles[inner] = t2; + (_fileNamesToJoin[inner - 1], _fileNamesToJoin[inner]) = (_fileNamesToJoin[inner], _fileNamesToJoin[inner - 1]); + (subtitles[inner - 1], subtitles[inner]) = (subtitles[inner], subtitles[inner - 1]); } } } @@ -331,6 +327,11 @@ namespace Nikse.SubtitleEdit.Forms } } + if (radioButtonJoinPlain.Checked) + { + JoinedSubtitle.Sort(SubtitleSortCriteria.StartTime); + } + JoinedSubtitle.Renumber(); labelTotalLines.Text = string.Format(LanguageSettings.Current.JoinSubtitles.TotalNumberOfLinesX, JoinedSubtitle.Paragraphs.Count); } diff --git a/src/ui/Logic/ListViewSorter.cs b/src/ui/Logic/ListViewSorter.cs index c56b210f5..88c6de482 100644 --- a/src/ui/Logic/ListViewSorter.cs +++ b/src/ui/Logic/ListViewSorter.cs @@ -1,6 +1,8 @@ using Nikse.SubtitleEdit.Core.Common; using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; @@ -13,6 +15,7 @@ namespace Nikse.SubtitleEdit.Logic public bool IsDisplayFileSize { get; set; } public bool Descending { get; set; } + private readonly Regex _numbers = new Regex(@"\d+", RegexOptions.Compiled); private readonly Regex _invariantNumber = new Regex(@"\d+\.{1,2}", RegexOptions.Compiled); public int Compare(object o1, object o2) @@ -32,7 +35,7 @@ namespace Nikse.SubtitleEdit.Logic var s1 = lvi1.SubItems[ColumnNumber].Text; var s2 = lvi2.SubItems[ColumnNumber].Text; - if (_invariantNumber.IsMatch(s1) && _invariantNumber.IsMatch(s2) && + if (_invariantNumber.IsMatch(s1) && _invariantNumber.IsMatch(s2) && int.TryParse(s1, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var i1) && int.TryParse(s2, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var i2)) { @@ -53,7 +56,10 @@ namespace Nikse.SubtitleEdit.Logic return i1 > i2 ? 1 : i1 == i2 ? 0 : -1; } - return string.Compare(lvi2.SubItems[ColumnNumber].Text, lvi1.SubItems[ColumnNumber].Text, StringComparison.Ordinal); + // use natural sort order + var str2 = _numbers.Replace(lvi2.SubItems[ColumnNumber].Text, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); + var str1 = _numbers.Replace(lvi1.SubItems[ColumnNumber].Text, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); + return string.Compare(str2, str1, StringComparison.Ordinal); } } } From 965dc3554e7fc0a73b0372b02a212abf97f3bbe2 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 20:40:49 +0200 Subject: [PATCH 18/28] Minor refact --- src/ui/Forms/JoinSubtitles.cs | 29 ++++++----------------------- src/ui/Logic/ListViewSorter.cs | 23 +++++++++++++++++++++-- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/ui/Forms/JoinSubtitles.cs b/src/ui/Forms/JoinSubtitles.cs index f78641c14..ccaec13ce 100644 --- a/src/ui/Forms/JoinSubtitles.cs +++ b/src/ui/Forms/JoinSubtitles.cs @@ -445,7 +445,7 @@ namespace Nikse.SubtitleEdit.Forms numericUpDownAddMs.Enabled = radioButtonJoinAddTime.Checked; labelAddTime.Enabled = radioButtonJoinAddTime.Checked; SortAndLoad(); - SetSortArrow(listViewParts.Columns[3], SortOrder.None); + ListViewSorter.SetSortArrow(listViewParts.Columns[3], SortOrder.None); } private void contextMenuStripParts_Opening(object sender, System.ComponentModel.CancelEventArgs e) @@ -584,7 +584,7 @@ namespace Nikse.SubtitleEdit.Forms return; } - SetSortArrow(listViewParts.Columns[3], SortOrder.None); + ListViewSorter.SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveUp(listViewParts); } @@ -595,7 +595,7 @@ namespace Nikse.SubtitleEdit.Forms return; } - SetSortArrow(listViewParts.Columns[3], SortOrder.None); + ListViewSorter.SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveDown(listViewParts); } @@ -606,7 +606,7 @@ namespace Nikse.SubtitleEdit.Forms return; } - SetSortArrow(listViewParts.Columns[3], SortOrder.None); + ListViewSorter.SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveToTop(listViewParts); } @@ -617,7 +617,7 @@ namespace Nikse.SubtitleEdit.Forms return; } - SetSortArrow(listViewParts.Columns[3], SortOrder.None); + ListViewSorter.SetSortArrow(listViewParts.Columns[3], SortOrder.None); MoveToBottom(listViewParts); } @@ -649,24 +649,7 @@ namespace Nikse.SubtitleEdit.Forms lv.Sort(); - SetSortArrow(listViewParts.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); - } - - private static void SetSortArrow(ColumnHeader head, SortOrder order) - { - const string ascArrow = " ▲"; - const string descArrow = " ▼"; - - // remove arrow - if (head.Text.EndsWith(ascArrow) || head.Text.EndsWith(descArrow)) - head.Text = head.Text.Substring(0, head.Text.Length - 2); - - // add arrow - switch (order) - { - case SortOrder.Ascending: head.Text += ascArrow; break; - case SortOrder.Descending: head.Text += descArrow; break; - } + ListViewSorter.SetSortArrow(listViewParts.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); } } } diff --git a/src/ui/Logic/ListViewSorter.cs b/src/ui/Logic/ListViewSorter.cs index 88c6de482..11de406db 100644 --- a/src/ui/Logic/ListViewSorter.cs +++ b/src/ui/Logic/ListViewSorter.cs @@ -1,8 +1,6 @@ using Nikse.SubtitleEdit.Core.Common; using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; using System.Text.RegularExpressions; using System.Windows.Forms; @@ -61,5 +59,26 @@ namespace Nikse.SubtitleEdit.Logic var str1 = _numbers.Replace(lvi1.SubItems[ColumnNumber].Text, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); return string.Compare(str2, str1, StringComparison.Ordinal); } + + public static void SetSortArrow(ColumnHeader columnHeader, SortOrder sortOrder) + { + const string ascArrow = " ▲"; + const string descArrow = " ▼"; + + if (columnHeader.Text.EndsWith(ascArrow) || columnHeader.Text.EndsWith(descArrow)) + { + columnHeader.Text = columnHeader.Text.Substring(0, columnHeader.Text.Length - 2); + } + + switch (sortOrder) + { + case SortOrder.Ascending: + columnHeader.Text += ascArrow; + break; + case SortOrder.Descending: + columnHeader.Text += descArrow; + break; + } + } } } From fd03b2e447a8c1c535bbe9709e5a14ead49cab35 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 21:10:46 +0200 Subject: [PATCH 19/28] Update dk/sv translations --- src/ui/Languages/da-DK.xml | 20 ++++++++++++++++++++ src/ui/Languages/sv-SE.xml | 20 ++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/ui/Languages/da-DK.xml b/src/ui/Languages/da-DK.xml index c6dc45b86..7f0086787 100644 --- a/src/ui/Languages/da-DK.xml +++ b/src/ui/Languages/da-DK.xml @@ -1447,6 +1447,7 @@ Go to "Indstillinger -> Indstillinger -> Værktøj" for at indtaste din Bi Fjern/eksporter sceneændringer... Batch generer bølgeformer... Vis/skjul bølgeform og spektrogram + Tekst til tale og føj til video... Pop video vinduer ud Saml video vinduer @@ -2120,6 +2121,8 @@ Hvis du har redigeret denne fil med Subtitle Edit du måske finde en backup via Lige linjer Varighed mindre end Varighed større end + CPS mindre end + CPS større end Præcis én linje Præcis to linjer Mere end to linjer @@ -3146,6 +3149,23 @@ Fortsæt? Et synkronisering punkt vil justere positioner, to eller flere synkroniserings punkter vil justere position og hastighed Anvend + + Tekst til tale + Stemme + Test stemme + Standardstemme + Tilføj lyd til videofil (ny fil) + Generer tale fra tekst + Højreklik for at tildele skuespiller til stemme + Justerer hastighed: {0} / {1}... + Samler lydspor: {0} / {1}... + Genererer tale fra tekst: {0} / {1}... + Gennemgå lydklip + Gennemgå og rediger/fjern lydklip + Spil + Auto-fortsæt + Regenerer + SMPTE timing Vil du bruge SMPTE-timing til aktuelle undertekster? diff --git a/src/ui/Languages/sv-SE.xml b/src/ui/Languages/sv-SE.xml index cbecb4dbb..1eb18102c 100644 --- a/src/ui/Languages/sv-SE.xml +++ b/src/ui/Languages/sv-SE.xml @@ -1453,6 +1453,7 @@ Gå till "Alternativ -> Inställningar -> Verktyg" för att ange din nycke Ta bort/exportera bildändringar... Batch genererar vågformdata... Visa/dölj ljudformat och spektrogram + Text till tal och lägg till video... Avdocka videokontrollerna Docka videokontrollerna @@ -2129,6 +2130,8 @@ Ladda ner och fortsätta? Jämna radnummer Varaktighet kortare än Varaktighet längre än + CPS mindre än + CPS större än Exakt en rad Exakt två rader Mer än två rader @@ -3156,6 +3159,23 @@ och N bryter En synkpunkt kommer att justera positionen. två eller flera synkroniseringspunkter kommer att justera position och hastighet Verkställ + + Text till tal + Röst + Testa röst + Standardröst + Lägg till ljud till videofil (ny fil) + Generera tal från text + Högerklicka för att tilldela skådespelare till röst + Justerar hastighet: {0} / {1}... + Slår ihop ljudspår: {0} / {1}... + Genererar tal från text: {0} / {1}... + Granska ljudklipp + Granska och redigera/ta bort ljudklipp + Spela + Auto-fortsätt + Regenerera + SMPTE-timing Använd SMPTE-timing för aktuell undertext? From d6add133341dafc325a783a5e95bbbc2770d1784 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Sun, 28 Apr 2024 21:14:50 +0200 Subject: [PATCH 20/28] A few language fixes --- src/ui/Forms/Tts/ReviewAudioClips.cs | 1 + src/ui/Forms/Tts/TextToSpeech.Designer.cs | 54 +++++++++++------------ src/ui/Forms/Tts/TextToSpeech.cs | 9 ++-- 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/ui/Forms/Tts/ReviewAudioClips.cs b/src/ui/Forms/Tts/ReviewAudioClips.cs index 0e018451e..ef276e7b5 100644 --- a/src/ui/Forms/Tts/ReviewAudioClips.cs +++ b/src/ui/Forms/Tts/ReviewAudioClips.cs @@ -25,6 +25,7 @@ namespace Nikse.SubtitleEdit.Forms.Tts InitializeComponent(); UiUtil.FixFonts(this); Text = LanguageSettings.Current.TextToSpeech.ReviewAudioClips; + labelInfo.Text = LanguageSettings.Current.TextToSpeech.ReviewInfo; buttonEdit.Text = LanguageSettings.Current.ExportCustomText.Edit; buttonPlay.Text = LanguageSettings.Current.TextToSpeech.Play; checkBoxContinuePlay.Text = LanguageSettings.Current.TextToSpeech.AutoContinue; diff --git a/src/ui/Forms/Tts/TextToSpeech.Designer.cs b/src/ui/Forms/Tts/TextToSpeech.Designer.cs index 9358a4f58..b0f4300f5 100644 --- a/src/ui/Forms/Tts/TextToSpeech.Designer.cs +++ b/src/ui/Forms/Tts/TextToSpeech.Designer.cs @@ -34,7 +34,7 @@ this.buttonGenerateTTS = new System.Windows.Forms.Button(); this.progressBar1 = new System.Windows.Forms.ProgressBar(); this.labelEngine = new System.Windows.Forms.Label(); - this.groupBoxMsSettings = new System.Windows.Forms.GroupBox(); + this.groupBoxSettings = new System.Windows.Forms.GroupBox(); this.labelRegion = new System.Windows.Forms.Label(); this.nikseComboBoxRegion = new Nikse.SubtitleEdit.Controls.NikseComboBox(); this.labelVoiceCount = new System.Windows.Forms.Label(); @@ -55,7 +55,7 @@ this.contextMenuStripActors = new System.Windows.Forms.ContextMenuStrip(this.components); this.labelActors = new System.Windows.Forms.Label(); this.buttonCancel = new System.Windows.Forms.Button(); - this.groupBoxMsSettings.SuspendLayout(); + this.groupBoxSettings.SuspendLayout(); this.contextMenuStripVoices.SuspendLayout(); this.SuspendLayout(); // @@ -113,29 +113,29 @@ this.labelEngine.TabIndex = 14; this.labelEngine.Text = "Engine"; // - // groupBoxMsSettings + // groupBoxSettings // - this.groupBoxMsSettings.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.groupBoxSettings.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); - this.groupBoxMsSettings.Controls.Add(this.labelRegion); - this.groupBoxMsSettings.Controls.Add(this.nikseComboBoxRegion); - this.groupBoxMsSettings.Controls.Add(this.labelVoiceCount); - this.groupBoxMsSettings.Controls.Add(this.checkBoxShowPreview); - this.groupBoxMsSettings.Controls.Add(this.labelApiKey); - this.groupBoxMsSettings.Controls.Add(this.nikseTextBoxApiKey); - this.groupBoxMsSettings.Controls.Add(this.TextBoxTest); - this.groupBoxMsSettings.Controls.Add(this.buttonTestVoice); - this.groupBoxMsSettings.Controls.Add(this.checkBoxAddToVideoFile); - this.groupBoxMsSettings.Controls.Add(this.labelVoice); - this.groupBoxMsSettings.Controls.Add(this.nikseComboBoxVoice); - this.groupBoxMsSettings.Controls.Add(this.labelEngine); - this.groupBoxMsSettings.Controls.Add(this.nikseComboBoxEngine); - this.groupBoxMsSettings.Location = new System.Drawing.Point(15, 12); - this.groupBoxMsSettings.Name = "groupBoxMsSettings"; - this.groupBoxMsSettings.Size = new System.Drawing.Size(391, 405); - this.groupBoxMsSettings.TabIndex = 1; - this.groupBoxMsSettings.TabStop = false; - this.groupBoxMsSettings.Text = "Settings"; + this.groupBoxSettings.Controls.Add(this.labelRegion); + this.groupBoxSettings.Controls.Add(this.nikseComboBoxRegion); + this.groupBoxSettings.Controls.Add(this.labelVoiceCount); + this.groupBoxSettings.Controls.Add(this.checkBoxShowPreview); + this.groupBoxSettings.Controls.Add(this.labelApiKey); + this.groupBoxSettings.Controls.Add(this.nikseTextBoxApiKey); + this.groupBoxSettings.Controls.Add(this.TextBoxTest); + this.groupBoxSettings.Controls.Add(this.buttonTestVoice); + this.groupBoxSettings.Controls.Add(this.checkBoxAddToVideoFile); + this.groupBoxSettings.Controls.Add(this.labelVoice); + this.groupBoxSettings.Controls.Add(this.nikseComboBoxVoice); + this.groupBoxSettings.Controls.Add(this.labelEngine); + this.groupBoxSettings.Controls.Add(this.nikseComboBoxEngine); + this.groupBoxSettings.Location = new System.Drawing.Point(15, 12); + this.groupBoxSettings.Name = "groupBoxSettings"; + this.groupBoxSettings.Size = new System.Drawing.Size(391, 405); + this.groupBoxSettings.TabIndex = 1; + this.groupBoxSettings.TabStop = false; + this.groupBoxSettings.Text = "Settings"; // // labelRegion // @@ -425,7 +425,7 @@ this.Controls.Add(this.buttonCancel); this.Controls.Add(this.labelActors); this.Controls.Add(this.listViewActors); - this.Controls.Add(this.groupBoxMsSettings); + this.Controls.Add(this.groupBoxSettings); this.Controls.Add(this.progressBar1); this.Controls.Add(this.buttonGenerateTTS); this.Controls.Add(this.labelProgress); @@ -443,8 +443,8 @@ this.ResizeEnd += new System.EventHandler(this.TextToSpeech_ResizeEnd); this.SizeChanged += new System.EventHandler(this.TextToSpeech_SizeChanged); this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextToSpeech_KeyDown); - this.groupBoxMsSettings.ResumeLayout(false); - this.groupBoxMsSettings.PerformLayout(); + this.groupBoxSettings.ResumeLayout(false); + this.groupBoxSettings.PerformLayout(); this.contextMenuStripVoices.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -458,7 +458,7 @@ private System.Windows.Forms.ProgressBar progressBar1; private Controls.NikseComboBox nikseComboBoxEngine; private System.Windows.Forms.Label labelEngine; - private System.Windows.Forms.GroupBox groupBoxMsSettings; + private System.Windows.Forms.GroupBox groupBoxSettings; private System.Windows.Forms.Label labelVoice; private Controls.NikseComboBox nikseComboBoxVoice; private System.Windows.Forms.CheckBox checkBoxAddToVideoFile; diff --git a/src/ui/Forms/Tts/TextToSpeech.cs b/src/ui/Forms/Tts/TextToSpeech.cs index 76a099807..8ddebe2cd 100644 --- a/src/ui/Forms/Tts/TextToSpeech.cs +++ b/src/ui/Forms/Tts/TextToSpeech.cs @@ -111,6 +111,7 @@ namespace Nikse.SubtitleEdit.Forms.Tts .ToList(); Text = LanguageSettings.Current.TextToSpeech.Title; + groupBoxSettings.Text = LanguageSettings.Current.Settings.Title; labelVoice.Text = LanguageSettings.Current.TextToSpeech.Voice; labelApiKey.Text = LanguageSettings.Current.VobSubOcr.ApiKey; buttonTestVoice.Text = LanguageSettings.Current.TextToSpeech.TestVoice; @@ -160,11 +161,11 @@ namespace Nikse.SubtitleEdit.Forms.Tts if (!SubtitleFormatHasActors() || !_actors.Any()) { - var w = groupBoxMsSettings.Width + 100; + var w = groupBoxSettings.Width + 100; var right = buttonCancel.Right; - groupBoxMsSettings.Left = progressBar1.Left; - groupBoxMsSettings.Width = right - progressBar1.Left; - groupBoxMsSettings.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; + groupBoxSettings.Left = progressBar1.Left; + groupBoxSettings.Width = right - progressBar1.Left; + groupBoxSettings.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom; Width = w; MinimumSize = new Size(w, MinimumSize.Height); From 8b8db9001df5cf519836f6324d75e12183cec4f5 Mon Sep 17 00:00:00 2001 From: Lucas Date: Sun, 28 Apr 2024 20:19:12 -0300 Subject: [PATCH 21/28] Correct a detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently you should use the call “bash build.sh” with this you can just use “./build.sh” --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 1b7640037..720e379b8 100644 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -#!bin/bash +#!/bin/bash function ShowHelp() { From f6399cf63e2fffe777eca72eb9f7630e95dec8ff Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 06:02:52 +0200 Subject: [PATCH 22/28] Work on #8257 --- src/libse/Common/FfmpegMediaInfo.cs | 2 +- src/ui/Forms/JoinSubtitles.cs | 35 +++++++++++++++++++++-------- src/ui/Logic/ListViewSorter.cs | 18 +++++++++------ 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/libse/Common/FfmpegMediaInfo.cs b/src/libse/Common/FfmpegMediaInfo.cs index c6bfe1050..d4ff98e53 100644 --- a/src/libse/Common/FfmpegMediaInfo.cs +++ b/src/libse/Common/FfmpegMediaInfo.cs @@ -68,7 +68,7 @@ namespace Nikse.SubtitleEdit.Core.Common if (info.VideoWidth == 0 && parts.Length == 2 && int.TryParse(parts[0], out var w) && - int.TryParse(parts[0], out var h)) + int.TryParse(parts[1], out var h)) { info.VideoWidth = w; info.VideoHeight = h; diff --git a/src/ui/Forms/JoinSubtitles.cs b/src/ui/Forms/JoinSubtitles.cs index ccaec13ce..7668c57ef 100644 --- a/src/ui/Forms/JoinSubtitles.cs +++ b/src/ui/Forms/JoinSubtitles.cs @@ -94,15 +94,21 @@ namespace Nikse.SubtitleEdit.Forms private void listViewParts_DragDrop(object sender, DragEventArgs e) { var files = (string[])e.Data.GetData(DataFormats.FileDrop); - foreach (var fileName in files) - { - if (!_fileNamesToJoin.Any(file => file.Equals(fileName, StringComparison.OrdinalIgnoreCase))) - { - _fileNamesToJoin.Add(fileName); - } - } - SortAndLoad(); + TaskDelayHelper.RunDelayed(TimeSpan.FromMilliseconds(1), () => + { + var fileNames = files.ToList(); + fileNames.Sort(ListViewSorter.NaturalComparer); + foreach (var fileName in fileNames) + { + if (!_fileNamesToJoin.Any(file => file.Equals(fileName, StringComparison.OrdinalIgnoreCase))) + { + _fileNamesToJoin.Add(fileName); + } + } + + SortAndLoad(); + }); } private void SortAndLoad() @@ -289,6 +295,7 @@ namespace Nikse.SubtitleEdit.Forms { var sub = subtitles[i]; var lvi = new ListViewItem($"{sub.Paragraphs.Count:#,###,###}"); + lvi.Tag = fileName; if (sub.Paragraphs.Count > 0) { lvi.SubItems.Add(sub.Paragraphs[0].StartTime.ToString()); @@ -371,7 +378,9 @@ namespace Nikse.SubtitleEdit.Forms if (openFileDialog1.ShowDialog(this) == DialogResult.OK) { var sb = new StringBuilder(); - foreach (string fileName in openFileDialog1.FileNames) + var fileNames = openFileDialog1.FileNames.ToList(); + fileNames.Sort(ListViewSorter.NaturalComparer); + foreach (var fileName in fileNames) { Application.DoEvents(); if (File.Exists(fileName)) @@ -390,6 +399,7 @@ namespace Nikse.SubtitleEdit.Forms } } } + SortAndLoad(); if (sb.Length > 0) { @@ -650,6 +660,13 @@ namespace Nikse.SubtitleEdit.Forms lv.Sort(); ListViewSorter.SetSortArrow(listViewParts.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); + + _fileNamesToJoin.Clear(); + foreach (ListViewItem item in listViewParts.Items) + { + _fileNamesToJoin.Add((string)item.Tag); + } + SortAndLoad(); } } } diff --git a/src/ui/Logic/ListViewSorter.cs b/src/ui/Logic/ListViewSorter.cs index 11de406db..21916d47a 100644 --- a/src/ui/Logic/ListViewSorter.cs +++ b/src/ui/Logic/ListViewSorter.cs @@ -13,8 +13,8 @@ namespace Nikse.SubtitleEdit.Logic public bool IsDisplayFileSize { get; set; } public bool Descending { get; set; } - private readonly Regex _numbers = new Regex(@"\d+", RegexOptions.Compiled); - private readonly Regex _invariantNumber = new Regex(@"\d+\.{1,2}", RegexOptions.Compiled); + private static readonly Regex Numbers = new Regex(@"\d+", RegexOptions.Compiled); + private static readonly Regex InvariantNumber = new Regex(@"\d+\.{1,2}", RegexOptions.Compiled); public int Compare(object o1, object o2) { @@ -33,7 +33,7 @@ namespace Nikse.SubtitleEdit.Logic var s1 = lvi1.SubItems[ColumnNumber].Text; var s2 = lvi2.SubItems[ColumnNumber].Text; - if (_invariantNumber.IsMatch(s1) && _invariantNumber.IsMatch(s2) && + if (InvariantNumber.IsMatch(s1) && InvariantNumber.IsMatch(s2) && int.TryParse(s1, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var i1) && int.TryParse(s2, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var i2)) { @@ -54,10 +54,7 @@ namespace Nikse.SubtitleEdit.Logic return i1 > i2 ? 1 : i1 == i2 ? 0 : -1; } - // use natural sort order - var str2 = _numbers.Replace(lvi2.SubItems[ColumnNumber].Text, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); - var str1 = _numbers.Replace(lvi1.SubItems[ColumnNumber].Text, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); - return string.Compare(str2, str1, StringComparison.Ordinal); + return NaturalComparer(lvi2.SubItems[ColumnNumber].Text, lvi1.SubItems[ColumnNumber].Text); } public static void SetSortArrow(ColumnHeader columnHeader, SortOrder sortOrder) @@ -80,5 +77,12 @@ namespace Nikse.SubtitleEdit.Logic break; } } + + public static int NaturalComparer(string x, string y) + { + var str2 = Numbers.Replace(x, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); + var str1 = Numbers.Replace(y, m => m.Value.PadLeft(10, '0')).RemoveChar(' '); + return string.Compare(str2, str1, StringComparison.OrdinalIgnoreCase); + } } } From 2ffed9b2b999320fb273ac0fa5769947bc593822 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 06:17:12 +0200 Subject: [PATCH 23/28] Move video info to context menu - thx rRobis/Leon :) Work on #8273 --- LanguageBaseEnglish.xml | 1 + src/libse/Common/Settings.cs | 8 -- src/ui/Forms/Main.Designer.cs | 111 +++++++++++++++++---------- src/ui/Forms/Main.cs | 87 +++++++++++++-------- src/ui/Forms/Main.resx | 12 ++- src/ui/Logic/Language.cs | 1 + src/ui/Logic/LanguageDeserializer.cs | 3 + src/ui/Logic/LanguageStructure.cs | 1 + 8 files changed, 139 insertions(+), 85 deletions(-) diff --git a/LanguageBaseEnglish.xml b/LanguageBaseEnglish.xml index 963f2d70a..cd9b66f98 100644 --- a/LanguageBaseEnglish.xml +++ b/LanguageBaseEnglish.xml @@ -1610,6 +1610,7 @@ To use an API key, go to "Options -> Settings -> Auto-translate" to enter Go to source view Go to list view Extract audio... + Media information diff --git a/src/libse/Common/Settings.cs b/src/libse/Common/Settings.cs index 22041a59a..b0fbad13d 100644 --- a/src/libse/Common/Settings.cs +++ b/src/libse/Common/Settings.cs @@ -197,7 +197,6 @@ namespace Nikse.SubtitleEdit.Core.Common public string TextToSpeechAzureRegion { get; set; } public bool TextToSpeechPreview { get; set; } public bool TextToSpeechAddToVideoFile { get; set; } - public bool DisableVidoInfoViaLabel { get; set; } public bool ListViewSyntaxColorDurationSmall { get; set; } public bool ListViewSyntaxColorDurationBig { get; set; } public bool ListViewSyntaxColorOverlap { get; set; } @@ -5517,12 +5516,6 @@ $HorzAlign = Center settings.Tools.TextToSpeechAddToVideoFile = Convert.ToBoolean(subNode.InnerText, CultureInfo.InvariantCulture); } - subNode = node.SelectSingleNode("DisableVidoInfoViaLabel"); - if (subNode != null) - { - settings.Tools.DisableVidoInfoViaLabel = Convert.ToBoolean(subNode.InnerText, CultureInfo.InvariantCulture); - } - subNode = node.SelectSingleNode("ListViewSyntaxColorDurationSmall"); if (subNode != null) { @@ -12057,7 +12050,6 @@ $HorzAlign = Center textWriter.WriteElementString("TextToSpeechAzureRegion", settings.Tools.TextToSpeechAzureRegion); textWriter.WriteElementString("TextToSpeechPreview", settings.Tools.TextToSpeechPreview.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("TextToSpeechAddToVideoFile", settings.Tools.TextToSpeechAddToVideoFile.ToString(CultureInfo.InvariantCulture)); - textWriter.WriteElementString("DisableVidoInfoViaLabel", settings.Tools.DisableVidoInfoViaLabel.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationSmall", settings.Tools.ListViewSyntaxColorDurationSmall.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorDurationBig", settings.Tools.ListViewSyntaxColorDurationBig.ToString(CultureInfo.InvariantCulture)); textWriter.WriteElementString("ListViewSyntaxColorLongLines", settings.Tools.ListViewSyntaxColorLongLines.ToString(CultureInfo.InvariantCulture)); diff --git a/src/ui/Forms/Main.Designer.cs b/src/ui/Forms/Main.Designer.cs index 1e6d87231..02660f86c 100644 --- a/src/ui/Forms/Main.Designer.cs +++ b/src/ui/Forms/Main.Designer.cs @@ -41,9 +41,9 @@ namespace Nikse.SubtitleEdit.Forms { this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main)); - Nikse.SubtitleEdit.Core.Common.TimeCode timeCode1 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); - Nikse.SubtitleEdit.Core.Common.TimeCode timeCode2 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); - Nikse.SubtitleEdit.Core.Common.TimeCode timeCode3 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); + Nikse.SubtitleEdit.Core.Common.TimeCode timeCode13 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); + Nikse.SubtitleEdit.Core.Common.TimeCode timeCode14 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); + Nikse.SubtitleEdit.Core.Common.TimeCode timeCode15 = new Nikse.SubtitleEdit.Core.Common.TimeCode(); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.labelStatus = new System.Windows.Forms.ToolStripStatusLabel(); this.toolStripSelected = new System.Windows.Forms.ToolStripStatusLabel(); @@ -452,7 +452,6 @@ namespace Nikse.SubtitleEdit.Forms this.splitToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.mergeWithPreviousToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.mergeWithNextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.runWhiperOnParagraphToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator11 = new System.Windows.Forms.ToolStripSeparator(); this.extendToPreviousToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.extendToNextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -465,6 +464,7 @@ namespace Nikse.SubtitleEdit.Forms this.toolStripSeparatorGuessTimeCodes = new System.Windows.Forms.ToolStripSeparator(); this.removeShotChangeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addShotChangeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.runWhiperOnParagraphToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.guessTimeCodesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.seekSilenceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.insertSubtitleHereToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -582,6 +582,9 @@ namespace Nikse.SubtitleEdit.Forms this.timerOriginalTextUndo = new System.Windows.Forms.Timer(this.components); this.contextMenuStripShowVideoControls = new System.Windows.Forms.ContextMenuStrip(this.components); this.toolStripMenuItemShowVideoControls = new System.Windows.Forms.ToolStripMenuItem(); + this.contextMenuStripVideoFileName = new System.Windows.Forms.ContextMenuStrip(this.components); + this.videoInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openContainingFolderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.statusStrip1.SuspendLayout(); this.toolStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); @@ -620,6 +623,7 @@ namespace Nikse.SubtitleEdit.Forms this.panelVideoPlayer.SuspendLayout(); this.contextMenuStripEmpty.SuspendLayout(); this.contextMenuStripShowVideoControls.SuspendLayout(); + this.contextMenuStripVideoFileName.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 @@ -1077,7 +1081,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxEncoding.DropDownHeight = 215; this.comboBoxEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxEncoding.DropDownWidth = 0; - this.comboBoxEncoding.Items.AddRange(new object[] { + this.comboBoxEncoding.Items.AddRange(new string[] { "ANSI", "UTF-7", "UTF-8", @@ -3401,13 +3405,13 @@ namespace Nikse.SubtitleEdit.Forms // labelVideoInfo // this.labelVideoInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.labelVideoInfo.ContextMenuStrip = this.contextMenuStripVideoFileName; this.labelVideoInfo.Location = new System.Drawing.Point(603, 12); this.labelVideoInfo.Name = "labelVideoInfo"; this.labelVideoInfo.Size = new System.Drawing.Size(369, 19); this.labelVideoInfo.TabIndex = 12; this.labelVideoInfo.Text = "No video file loaded"; this.labelVideoInfo.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.labelVideoInfo.Click += new System.EventHandler(this.labelVideoInfo_Click); // // trackBarWaveformPosition // @@ -3680,7 +3684,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxAutoContinue.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxAutoContinue.DropDownWidth = 96; this.comboBoxAutoContinue.FormattingEnabled = false; - this.comboBoxAutoContinue.Items.AddRange(new object[] { + this.comboBoxAutoContinue.Items.AddRange(new string[] { "0", "1", "2", @@ -3762,7 +3766,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxAutoRepeat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxAutoRepeat.DropDownWidth = 96; this.comboBoxAutoRepeat.FormattingEnabled = false; - this.comboBoxAutoRepeat.Items.AddRange(new object[] { + this.comboBoxAutoRepeat.Items.AddRange(new string[] { "0", "1", "2", @@ -3886,14 +3890,14 @@ namespace Nikse.SubtitleEdit.Forms this.timeUpDownVideoPosition.Size = new System.Drawing.Size(113, 23); this.timeUpDownVideoPosition.TabIndex = 12; this.timeUpDownVideoPosition.TabStop = false; - timeCode1.Hours = 0; - timeCode1.Milliseconds = 0; - timeCode1.Minutes = 0; - timeCode1.Seconds = 0; - timeCode1.TimeSpan = System.TimeSpan.Parse("00:00:00"); - timeCode1.TotalMilliseconds = 0D; - timeCode1.TotalSeconds = 0D; - this.timeUpDownVideoPosition.TimeCode = timeCode1; + timeCode13.Hours = 0; + timeCode13.Milliseconds = 0; + timeCode13.Minutes = 0; + timeCode13.Seconds = 0; + timeCode13.TimeSpan = System.TimeSpan.Parse("00:00:00"); + timeCode13.TotalMilliseconds = 0D; + timeCode13.TotalSeconds = 0D; + this.timeUpDownVideoPosition.TimeCode = timeCode13; this.timeUpDownVideoPosition.UseVideoOffset = false; // // buttonGotoSub @@ -4166,14 +4170,14 @@ namespace Nikse.SubtitleEdit.Forms this.timeUpDownVideoPositionAdjust.Size = new System.Drawing.Size(113, 23); this.timeUpDownVideoPositionAdjust.TabIndex = 13; this.timeUpDownVideoPositionAdjust.TabStop = false; - timeCode2.Hours = 0; - timeCode2.Milliseconds = 0; - timeCode2.Minutes = 0; - timeCode2.Seconds = 0; - timeCode2.TimeSpan = System.TimeSpan.Parse("00:00:00"); - timeCode2.TotalMilliseconds = 0D; - timeCode2.TotalSeconds = 0D; - this.timeUpDownVideoPositionAdjust.TimeCode = timeCode2; + timeCode14.Hours = 0; + timeCode14.Milliseconds = 0; + timeCode14.Minutes = 0; + timeCode14.Seconds = 0; + timeCode14.TimeSpan = System.TimeSpan.Parse("00:00:00"); + timeCode14.TotalMilliseconds = 0D; + timeCode14.TotalSeconds = 0D; + this.timeUpDownVideoPositionAdjust.TimeCode = timeCode14; this.timeUpDownVideoPositionAdjust.UseVideoOffset = false; // // buttonAdjustSetEndTime @@ -4472,7 +4476,7 @@ namespace Nikse.SubtitleEdit.Forms this.seekSilenceToolStripMenuItem, this.insertSubtitleHereToolStripMenuItem}); this.contextMenuStripWaveform.Name = "contextMenuStripWaveform"; - this.contextMenuStripWaveform.Size = new System.Drawing.Size(275, 534); + this.contextMenuStripWaveform.Size = new System.Drawing.Size(275, 512); this.contextMenuStripWaveform.Closing += new System.Windows.Forms.ToolStripDropDownClosingEventHandler(this.ContextMenuStripWaveformClosing); this.contextMenuStripWaveform.Opening += new System.ComponentModel.CancelEventHandler(this.ContextMenuStripWaveformOpening); // @@ -4546,13 +4550,6 @@ namespace Nikse.SubtitleEdit.Forms this.mergeWithNextToolStripMenuItem.Text = "Merge with next"; this.mergeWithNextToolStripMenuItem.Click += new System.EventHandler(this.MergeWithNextToolStripMenuItemClick); // - // runWhiperOnParagraphToolStripMenuItem - // - this.runWhiperOnParagraphToolStripMenuItem.Name = "runWhiperOnParagraphToolStripMenuItem"; - this.runWhiperOnParagraphToolStripMenuItem.Size = new System.Drawing.Size(274, 22); - this.runWhiperOnParagraphToolStripMenuItem.Text = "Run Whiper on paragraph..."; - this.runWhiperOnParagraphToolStripMenuItem.Click += new System.EventHandler(this.runWhiperOnParagraphToolStripMenuItem_Click); - // // toolStripSeparator11 // this.toolStripSeparator11.Name = "toolStripSeparator11"; @@ -4629,6 +4626,13 @@ namespace Nikse.SubtitleEdit.Forms this.addShotChangeToolStripMenuItem.Text = "Add shot change"; this.addShotChangeToolStripMenuItem.Click += new System.EventHandler(this.AddShotChangeToolStripMenuItemClick); // + // runWhiperOnParagraphToolStripMenuItem + // + this.runWhiperOnParagraphToolStripMenuItem.Name = "runWhiperOnParagraphToolStripMenuItem"; + this.runWhiperOnParagraphToolStripMenuItem.Size = new System.Drawing.Size(274, 22); + this.runWhiperOnParagraphToolStripMenuItem.Text = "Run Whiper on paragraph..."; + this.runWhiperOnParagraphToolStripMenuItem.Click += new System.EventHandler(this.runWhiperOnParagraphToolStripMenuItem_Click); + // // guessTimeCodesToolStripMenuItem // this.guessTimeCodesToolStripMenuItem.Name = "guessTimeCodesToolStripMenuItem"; @@ -5522,14 +5526,14 @@ namespace Nikse.SubtitleEdit.Forms this.timeUpDownStartTime.Size = new System.Drawing.Size(113, 23); this.timeUpDownStartTime.TabIndex = 0; this.timeUpDownStartTime.TabStop = false; - timeCode3.Hours = 0; - timeCode3.Milliseconds = 0; - timeCode3.Minutes = 0; - timeCode3.Seconds = 0; - timeCode3.TimeSpan = System.TimeSpan.Parse("00:00:00"); - timeCode3.TotalMilliseconds = 0D; - timeCode3.TotalSeconds = 0D; - this.timeUpDownStartTime.TimeCode = timeCode3; + timeCode15.Hours = 0; + timeCode15.Milliseconds = 0; + timeCode15.Minutes = 0; + timeCode15.Seconds = 0; + timeCode15.TimeSpan = System.TimeSpan.Parse("00:00:00"); + timeCode15.TotalMilliseconds = 0D; + timeCode15.TotalSeconds = 0D; + this.timeUpDownStartTime.TimeCode = timeCode15; this.timeUpDownStartTime.UseVideoOffset = false; // // numericUpDownDuration @@ -5883,6 +5887,29 @@ namespace Nikse.SubtitleEdit.Forms this.toolStripMenuItemShowVideoControls.Text = "Show video controls"; this.toolStripMenuItemShowVideoControls.Click += new System.EventHandler(this.ToolStripMenuItemShowVideoControlsClick); // + // contextMenuStripVideoFileName + // + this.contextMenuStripVideoFileName.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.videoInfoToolStripMenuItem, + this.openContainingFolderToolStripMenuItem}); + this.contextMenuStripVideoFileName.Name = "contextMenuStripVideoFileName"; + this.contextMenuStripVideoFileName.Size = new System.Drawing.Size(198, 48); + this.contextMenuStripVideoFileName.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripVideoFileName_Opening); + // + // videoInfoToolStripMenuItem + // + this.videoInfoToolStripMenuItem.Name = "videoInfoToolStripMenuItem"; + this.videoInfoToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.videoInfoToolStripMenuItem.Text = "Video info"; + this.videoInfoToolStripMenuItem.Click += new System.EventHandler(this.videoInfoToolStripMenuItem_Click); + // + // openContainingFolderToolStripMenuItem + // + this.openContainingFolderToolStripMenuItem.Name = "openContainingFolderToolStripMenuItem"; + this.openContainingFolderToolStripMenuItem.Size = new System.Drawing.Size(197, 22); + this.openContainingFolderToolStripMenuItem.Text = "Open containing folder"; + this.openContainingFolderToolStripMenuItem.Click += new System.EventHandler(this.openContainingFolderToolStripMenuItem_Click); + // // Main // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -5959,6 +5986,7 @@ namespace Nikse.SubtitleEdit.Forms this.panelVideoPlayer.ResumeLayout(false); this.contextMenuStripEmpty.ResumeLayout(false); this.contextMenuStripShowVideoControls.ResumeLayout(false); + this.contextMenuStripVideoFileName.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -6504,5 +6532,8 @@ namespace Nikse.SubtitleEdit.Forms private System.Windows.Forms.ToolStripButton toolStripButtonVideoOpen; private System.Windows.Forms.ToolStripMenuItem runWhiperOnParagraphToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem textToSpeechAndAddToVideoToolStripMenuItem; + private ContextMenuStrip contextMenuStripVideoFileName; + private ToolStripMenuItem videoInfoToolStripMenuItem; + private ToolStripMenuItem openContainingFolderToolStripMenuItem; } } \ No newline at end of file diff --git a/src/ui/Forms/Main.cs b/src/ui/Forms/Main.cs index 6d71dfd50..6374e4cb7 100644 --- a/src/ui/Forms/Main.cs +++ b/src/ui/Forms/Main.cs @@ -36696,9 +36696,46 @@ namespace Nikse.SubtitleEdit.Forms } } - private void labelVideoInfo_Click(object sender, EventArgs e) + private void ToolStripButtonVideoOpenClick(object sender, EventArgs e) { - if (string.IsNullOrEmpty(_videoFileName) || _videoInfo == null || Configuration.Settings.Tools.DisableVidoInfoViaLabel) + OpenVideoDialog(); + } + + private void runWhiperOnParagraphToolStripMenuItem_Click(object sender, EventArgs e) + { + AudioToTextWhisperSelectedLines(); + } + + private void textToSpeechAndAddToVideoToolStripMenuItem_Click(object sender, EventArgs e) + { + TaskDelayHelper.RunDelayed(TimeSpan.FromMilliseconds(25), () => + { + if (string.IsNullOrEmpty(_videoFileName) || _videoInfo == null) + { + MessageBox.Show(LanguageSettings.Current.General.NoVideoLoaded); + return; + } + + if (RequireFfmpegOk()) + { + using (var form = new TextToSpeech(_subtitle, GetCurrentSubtitleFormat(), _videoFileName, _videoInfo)) + { + if (form.ShowDialog(this) == DialogResult.OK) + { + var idx = FirstSelectedIndex; + _subtitle = form.EditedSubtitle; + SubtitleListview1.Fill(_subtitle, _subtitleOriginal); + _subtitleListViewIndex = -1; + SubtitleListview1.SelectIndexAndEnsureVisibleFaster(idx); + } + } + } + }); + } + + private void videoInfoToolStripMenuItem_Click(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(_videoFileName) || _videoInfo == null) { return; } @@ -36784,41 +36821,23 @@ namespace Nikse.SubtitleEdit.Forms MessageBox.Show(sb.ToString() + sbTrackInfo.ToString()); } - private void ToolStripButtonVideoOpenClick(object sender, EventArgs e) + private void contextMenuStripVideoFileName_Opening(object sender, CancelEventArgs e) { - OpenVideoDialog(); - } - - private void runWhiperOnParagraphToolStripMenuItem_Click(object sender, EventArgs e) - { - AudioToTextWhisperSelectedLines(); - } - - private void textToSpeechAndAddToVideoToolStripMenuItem_Click(object sender, EventArgs e) - { - TaskDelayHelper.RunDelayed(TimeSpan.FromMilliseconds(25), () => + if (string.IsNullOrEmpty(_videoFileName) || _videoInfo == null) { - if (string.IsNullOrEmpty(_videoFileName) || _videoInfo == null) - { - MessageBox.Show(LanguageSettings.Current.General.NoVideoLoaded); - return; - } + e.Cancel = true; + } - if (RequireFfmpegOk()) - { - using (var form = new TextToSpeech(_subtitle, GetCurrentSubtitleFormat(), _videoFileName, _videoInfo)) - { - if (form.ShowDialog(this) == DialogResult.OK) - { - var idx = FirstSelectedIndex; - _subtitle = form.EditedSubtitle; - SubtitleListview1.Fill(_subtitle, _subtitleOriginal); - _subtitleListViewIndex = -1; - SubtitleListview1.SelectIndexAndEnsureVisibleFaster(idx); - } - } - } - }); + openContainingFolderToolStripMenuItem.Text = _language.Menu.File.OpenContainingFolder; + videoInfoToolStripMenuItem.Text = _language.Menu.ContextMenu.MediaInfo; + } + + private void openContainingFolderToolStripMenuItem_Click(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(_videoFileName) && File.Exists(_videoFileName)) + { + UiUtil.OpenFolderFromFileName(_videoFileName); + } } } } \ No newline at end of file diff --git a/src/ui/Forms/Main.resx b/src/ui/Forms/Main.resx index d5979c470..c577e339f 100644 --- a/src/ui/Forms/Main.resx +++ b/src/ui/Forms/Main.resx @@ -684,6 +684,12 @@ ZW1zBV9zaXplCF92ZXJzaW9uBwAABggIAgAAAAkDAAAAAAAAAAAAAAAPAwAAAAAAAAAGCw== + + 929, 92 + + + 652, 56 + 652, 56 @@ -769,12 +775,12 @@ 981, 56 - - 668, 17 - 193, 17 + + 668, 17 + 17, 95 diff --git a/src/ui/Logic/Language.cs b/src/ui/Logic/Language.cs index eaad168dd..c551a1b43 100644 --- a/src/ui/Logic/Language.cs +++ b/src/ui/Logic/Language.cs @@ -2210,6 +2210,7 @@ namespace Nikse.SubtitleEdit.Logic GoToSourceView = "Go to source view", GoToListView = "Go to list view", ExtractAudio = "Extract audio...", + MediaInfo = "Media information", } }, diff --git a/src/ui/Logic/LanguageDeserializer.cs b/src/ui/Logic/LanguageDeserializer.cs index eebd8ade2..3da9a72ab 100644 --- a/src/ui/Logic/LanguageDeserializer.cs +++ b/src/ui/Logic/LanguageDeserializer.cs @@ -5170,6 +5170,9 @@ namespace Nikse.SubtitleEdit.Logic case "Main/Menu/ContextMenu/ExtractAudio": language.Main.Menu.ContextMenu.ExtractAudio = reader.Value; break; + case "Main/Menu/ContextMenu/MediaInfo": + language.Main.Menu.ContextMenu.MediaInfo = reader.Value; + break; case "Main/Controls/SubtitleFormat": language.Main.Controls.SubtitleFormat = reader.Value; break; diff --git a/src/ui/Logic/LanguageStructure.cs b/src/ui/Logic/LanguageStructure.cs index 97de53031..a336bac1b 100644 --- a/src/ui/Logic/LanguageStructure.cs +++ b/src/ui/Logic/LanguageStructure.cs @@ -2015,6 +2015,7 @@ public string GoToSourceView { get; set; } public string GoToListView { get; set; } public string ExtractAudio { get; set; } + public string MediaInfo { get; set; } } public FileMenu File { get; set; } From 6947faeba27b036e9d508b76f373d66376a78782 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 06:35:25 +0200 Subject: [PATCH 24/28] Work on #8272 --- .../GenerateVideoWithHardSubs.Designer.cs | 952 +++++++++--------- src/ui/Forms/GenerateVideoWithHardSubs.cs | 58 +- 2 files changed, 529 insertions(+), 481 deletions(-) diff --git a/src/ui/Forms/GenerateVideoWithHardSubs.Designer.cs b/src/ui/Forms/GenerateVideoWithHardSubs.Designer.cs index 533731909..160dea6aa 100644 --- a/src/ui/Forms/GenerateVideoWithHardSubs.Designer.cs +++ b/src/ui/Forms/GenerateVideoWithHardSubs.Designer.cs @@ -48,17 +48,53 @@ namespace Nikse.SubtitleEdit.Forms this.groupBoxCut = new System.Windows.Forms.GroupBox(); this.buttonCutTo = new System.Windows.Forms.Button(); this.buttonCutFrom = new System.Windows.Forms.Button(); + this.numericUpDownCutToSeconds = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownCutToMinutes = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownCutToHours = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownCutFromSeconds = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownCutFromMinutes = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownCutFromHours = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.labelCutTo = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.labelCutFrom = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.checkBoxCut = new System.Windows.Forms.CheckBox(); + this.labelVideoBitrate = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.checkBoxBox = new System.Windows.Forms.CheckBox(); this.checkBoxAlignRight = new System.Windows.Forms.CheckBox(); + this.labelInfo = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.checkBoxRightToLeft = new System.Windows.Forms.CheckBox(); + this.comboBoxSubtitleFont = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.labelSubtitleFont = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.groupBoxVideo = new System.Windows.Forms.GroupBox(); + this.labelCrfHint = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.buttonVideoChooseStandardRes = new System.Windows.Forms.Button(); + this.labelResolution = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.numericUpDownWidth = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.numericUpDownHeight = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.labelX = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.labelPreset = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.comboBoxTune = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.comboBoxPreset = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.labelTune = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.labelCRF = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.comboBoxVideoEncoding = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.comboBoxCrf = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.labelVideoEncoding = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.groupBoxAudio = new System.Windows.Forms.GroupBox(); this.listViewAudioTracks = new System.Windows.Forms.ListView(); this.columnHeaderAudioTrack = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.labelAudioEnc = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.comboBoxAudioBitRate = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.comboBoxAudioEnc = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.labelAudioBitRate = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.checkBoxMakeStereo = new System.Windows.Forms.CheckBox(); + this.labelAudioSampleRate = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.comboBoxAudioSampleRate = new Nikse.SubtitleEdit.Controls.NikseComboBox(); + this.numericUpDownTargetFileSize = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.labelFileSize = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.checkBoxTargetFileSize = new System.Windows.Forms.CheckBox(); + this.labelFileName = new Nikse.SubtitleEdit.Controls.NikseLabel(); + this.numericUpDownFontSize = new Nikse.SubtitleEdit.Controls.NikseUpDown(); + this.labelFontSize = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.listViewBatch = new System.Windows.Forms.ListView(); this.columnHeaderVideoFile = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); this.columnHeaderResolution = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); @@ -73,6 +109,7 @@ namespace Nikse.SubtitleEdit.Forms this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this.deleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.clearToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.videoPlayerContainer1 = new Nikse.SubtitleEdit.Controls.VideoPlayerContainer(); this.buttonOutputFileSettings = new System.Windows.Forms.Button(); this.buttonPreview = new System.Windows.Forms.Button(); this.linkLabelHelp = new System.Windows.Forms.LinkLabel(); @@ -94,43 +131,6 @@ namespace Nikse.SubtitleEdit.Forms this.x352ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.x272ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.buttonMode = new System.Windows.Forms.Button(); - this.numericUpDownCutToSeconds = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownCutToMinutes = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownCutToHours = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownCutFromSeconds = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownCutFromMinutes = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownCutFromHours = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.labelCutTo = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelCutFrom = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelVideoBitrate = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelInfo = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.comboBoxSubtitleFont = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.labelSubtitleFont = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelCrfHint = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelResolution = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.numericUpDownWidth = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.numericUpDownHeight = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.labelX = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelPreset = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.comboBoxTune = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.comboBoxPreset = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.labelTune = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelCRF = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.comboBoxVideoEncoding = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.comboBoxCrf = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.labelVideoEncoding = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelAudioEnc = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.comboBoxAudioBitRate = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.comboBoxAudioEnc = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.labelAudioBitRate = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelAudioSampleRate = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.comboBoxAudioSampleRate = new Nikse.SubtitleEdit.Controls.NikseComboBox(); - this.numericUpDownTargetFileSize = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.labelFileSize = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.labelFileName = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.numericUpDownFontSize = new Nikse.SubtitleEdit.Controls.NikseUpDown(); - this.labelFontSize = new Nikse.SubtitleEdit.Controls.NikseLabel(); - this.videoPlayerContainer1 = new Nikse.SubtitleEdit.Controls.VideoPlayerContainer(); this.labelPreviewPleaseWait = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.labelPass = new Nikse.SubtitleEdit.Controls.NikseLabel(); this.labelProgress = new Nikse.SubtitleEdit.Controls.NikseLabel(); @@ -268,7 +268,6 @@ namespace Nikse.SubtitleEdit.Forms this.nikseLabelOutputFileFolder.Size = new System.Drawing.Size(90, 13); this.nikseLabelOutputFileFolder.TabIndex = 144; this.nikseLabelOutputFileFolder.Text = "Use source folder"; - this.nikseLabelOutputFileFolder.Click += new System.EventHandler(this.nikseLabelOutputFileFolder_Click); // // buttonRemoveFile // @@ -360,437 +359,6 @@ namespace Nikse.SubtitleEdit.Forms this.buttonCutFrom.UseVisualStyleBackColor = true; this.buttonCutFrom.Click += new System.EventHandler(this.buttonCutFrom_Click); // - // checkBoxCut - // - this.checkBoxCut.AutoSize = true; - this.checkBoxCut.Location = new System.Drawing.Point(12, 36); - this.checkBoxCut.Name = "checkBoxCut"; - this.checkBoxCut.Size = new System.Drawing.Size(42, 17); - this.checkBoxCut.TabIndex = 0; - this.checkBoxCut.Text = "Cut"; - this.checkBoxCut.UseVisualStyleBackColor = true; - this.checkBoxCut.CheckedChanged += new System.EventHandler(this.checkBoxCut_CheckedChanged); - // - // checkBoxBox - // - this.checkBoxBox.AutoSize = true; - this.checkBoxBox.Location = new System.Drawing.Point(226, 27); - this.checkBoxBox.Name = "checkBoxBox"; - this.checkBoxBox.Size = new System.Drawing.Size(44, 17); - this.checkBoxBox.TabIndex = 2; - this.checkBoxBox.Text = "Box"; - this.checkBoxBox.UseVisualStyleBackColor = true; - this.checkBoxBox.CheckedChanged += new System.EventHandler(this.checkBoxBox_CheckedChanged); - // - // checkBoxAlignRight - // - this.checkBoxAlignRight.AutoSize = true; - this.checkBoxAlignRight.Location = new System.Drawing.Point(217, 114); - this.checkBoxAlignRight.Name = "checkBoxAlignRight"; - this.checkBoxAlignRight.Size = new System.Drawing.Size(72, 17); - this.checkBoxAlignRight.TabIndex = 10; - this.checkBoxAlignRight.Text = "Align right"; - this.checkBoxAlignRight.UseVisualStyleBackColor = true; - this.checkBoxAlignRight.CheckedChanged += new System.EventHandler(this.checkBoxAlignRight_CheckedChanged); - // - // checkBoxRightToLeft - // - this.checkBoxRightToLeft.AutoSize = true; - this.checkBoxRightToLeft.Location = new System.Drawing.Point(120, 114); - this.checkBoxRightToLeft.Name = "checkBoxRightToLeft"; - this.checkBoxRightToLeft.Size = new System.Drawing.Size(80, 17); - this.checkBoxRightToLeft.TabIndex = 9; - this.checkBoxRightToLeft.Text = "Right to left"; - this.checkBoxRightToLeft.UseVisualStyleBackColor = true; - this.checkBoxRightToLeft.CheckedChanged += new System.EventHandler(this.checkBoxRightToLeft_CheckedChanged); - // - // groupBoxVideo - // - this.groupBoxVideo.Controls.Add(this.labelCrfHint); - this.groupBoxVideo.Controls.Add(this.buttonVideoChooseStandardRes); - this.groupBoxVideo.Controls.Add(this.labelResolution); - this.groupBoxVideo.Controls.Add(this.numericUpDownWidth); - this.groupBoxVideo.Controls.Add(this.numericUpDownHeight); - this.groupBoxVideo.Controls.Add(this.labelX); - this.groupBoxVideo.Controls.Add(this.labelPreset); - this.groupBoxVideo.Controls.Add(this.comboBoxTune); - this.groupBoxVideo.Controls.Add(this.comboBoxPreset); - this.groupBoxVideo.Controls.Add(this.labelTune); - this.groupBoxVideo.Controls.Add(this.labelCRF); - this.groupBoxVideo.Controls.Add(this.comboBoxVideoEncoding); - this.groupBoxVideo.Controls.Add(this.comboBoxCrf); - this.groupBoxVideo.Controls.Add(this.labelVideoEncoding); - this.groupBoxVideo.Location = new System.Drawing.Point(6, 141); - this.groupBoxVideo.Name = "groupBoxVideo"; - this.groupBoxVideo.Size = new System.Drawing.Size(406, 166); - this.groupBoxVideo.TabIndex = 70; - this.groupBoxVideo.TabStop = false; - this.groupBoxVideo.Text = "Video"; - // - // buttonVideoChooseStandardRes - // - this.buttonVideoChooseStandardRes.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.buttonVideoChooseStandardRes.Location = new System.Drawing.Point(257, 15); - this.buttonVideoChooseStandardRes.Name = "buttonVideoChooseStandardRes"; - this.buttonVideoChooseStandardRes.Size = new System.Drawing.Size(69, 23); - this.buttonVideoChooseStandardRes.TabIndex = 3; - this.buttonVideoChooseStandardRes.Text = "..."; - this.buttonVideoChooseStandardRes.UseVisualStyleBackColor = true; - this.buttonVideoChooseStandardRes.Click += new System.EventHandler(this.buttonVideoChooseStandardRes_Click); - // - // groupBoxAudio - // - this.groupBoxAudio.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.groupBoxAudio.Controls.Add(this.listViewAudioTracks); - this.groupBoxAudio.Controls.Add(this.labelAudioEnc); - this.groupBoxAudio.Controls.Add(this.comboBoxAudioBitRate); - this.groupBoxAudio.Controls.Add(this.comboBoxAudioEnc); - this.groupBoxAudio.Controls.Add(this.labelAudioBitRate); - this.groupBoxAudio.Controls.Add(this.checkBoxMakeStereo); - this.groupBoxAudio.Controls.Add(this.labelAudioSampleRate); - this.groupBoxAudio.Controls.Add(this.comboBoxAudioSampleRate); - this.groupBoxAudio.Location = new System.Drawing.Point(430, 141); - this.groupBoxAudio.Name = "groupBoxAudio"; - this.groupBoxAudio.Size = new System.Drawing.Size(425, 166); - this.groupBoxAudio.TabIndex = 90; - this.groupBoxAudio.TabStop = false; - this.groupBoxAudio.Text = "Audio"; - // - // listViewAudioTracks - // - this.listViewAudioTracks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.listViewAudioTracks.CheckBoxes = true; - this.listViewAudioTracks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnHeaderAudioTrack}); - this.listViewAudioTracks.HideSelection = false; - this.listViewAudioTracks.Location = new System.Drawing.Point(229, 29); - this.listViewAudioTracks.Name = "listViewAudioTracks"; - this.listViewAudioTracks.Size = new System.Drawing.Size(190, 122); - this.listViewAudioTracks.TabIndex = 45; - this.listViewAudioTracks.UseCompatibleStateImageBehavior = false; - this.listViewAudioTracks.View = System.Windows.Forms.View.Details; - // - // columnHeaderAudioTrack - // - this.columnHeaderAudioTrack.Text = "Audio tracks"; - this.columnHeaderAudioTrack.Width = 160; - // - // checkBoxMakeStereo - // - this.checkBoxMakeStereo.AutoSize = true; - this.checkBoxMakeStereo.Checked = true; - this.checkBoxMakeStereo.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBoxMakeStereo.Location = new System.Drawing.Point(91, 58); - this.checkBoxMakeStereo.Name = "checkBoxMakeStereo"; - this.checkBoxMakeStereo.Size = new System.Drawing.Size(57, 17); - this.checkBoxMakeStereo.TabIndex = 2; - this.checkBoxMakeStereo.Text = "Stereo"; - this.checkBoxMakeStereo.UseVisualStyleBackColor = true; - // - // checkBoxTargetFileSize - // - this.checkBoxTargetFileSize.AutoSize = true; - this.checkBoxTargetFileSize.Location = new System.Drawing.Point(22, 327); - this.checkBoxTargetFileSize.Name = "checkBoxTargetFileSize"; - this.checkBoxTargetFileSize.Size = new System.Drawing.Size(192, 17); - this.checkBoxTargetFileSize.TabIndex = 100; - this.checkBoxTargetFileSize.Text = "Target file size (two pass encoding)"; - this.checkBoxTargetFileSize.UseVisualStyleBackColor = true; - this.checkBoxTargetFileSize.CheckedChanged += new System.EventHandler(this.checkBoxTargetFileSize_CheckedChanged); - // - // listViewBatch - // - this.listViewBatch.AllowDrop = true; - this.listViewBatch.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.listViewBatch.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnHeaderVideoFile, - this.columnHeaderResolution, - this.columnHeaderSize, - this.columnHeaderSubtitleFile, - this.columnHeaderStatus}); - this.listViewBatch.ContextMenuStrip = this.contextMenuStripBatch; - this.listViewBatch.FullRowSelect = true; - this.listViewBatch.HideSelection = false; - this.listViewBatch.Location = new System.Drawing.Point(6, 316); - this.listViewBatch.Name = "listViewBatch"; - this.listViewBatch.Size = new System.Drawing.Size(852, 191); - this.listViewBatch.TabIndex = 3; - this.listViewBatch.UseCompatibleStateImageBehavior = false; - this.listViewBatch.View = System.Windows.Forms.View.Details; - this.listViewBatch.DragDrop += new System.Windows.Forms.DragEventHandler(this.listViewBatch_DragDrop); - this.listViewBatch.DragEnter += new System.Windows.Forms.DragEventHandler(this.listViewBatch_DragEnter); - this.listViewBatch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listViewBatch_KeyDown); - // - // columnHeaderVideoFile - // - this.columnHeaderVideoFile.Text = "Video file"; - this.columnHeaderVideoFile.Width = 420; - // - // columnHeaderResolution - // - this.columnHeaderResolution.Text = "Resolution"; - this.columnHeaderResolution.Width = 80; - // - // columnHeaderSize - // - this.columnHeaderSize.Text = "Size"; - this.columnHeaderSize.Width = 80; - // - // columnHeaderSubtitleFile - // - this.columnHeaderSubtitleFile.Text = "Subtitle file"; - this.columnHeaderSubtitleFile.Width = 180; - // - // columnHeaderStatus - // - this.columnHeaderStatus.Text = "Status"; - this.columnHeaderStatus.Width = 80; - // - // contextMenuStripBatch - // - this.contextMenuStripBatch.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.addFilesToolStripMenuItem, - this.toolStripSeparator2, - this.pickSubtitleFileToolStripMenuItem, - this.removeSubtitleFileToolStripMenuItem, - this.toolStripSeparator1, - this.deleteToolStripMenuItem, - this.clearToolStripMenuItem}); - this.contextMenuStripBatch.Name = "contextMenuStripBatch"; - this.contextMenuStripBatch.Size = new System.Drawing.Size(179, 126); - this.contextMenuStripBatch.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripBatch_Opening); - // - // addFilesToolStripMenuItem - // - this.addFilesToolStripMenuItem.Name = "addFilesToolStripMenuItem"; - this.addFilesToolStripMenuItem.Size = new System.Drawing.Size(178, 22); - this.addFilesToolStripMenuItem.Text = "Add video files..."; - this.addFilesToolStripMenuItem.Click += new System.EventHandler(this.addFilesToolStripMenuItem_Click); - // - // toolStripSeparator2 - // - this.toolStripSeparator2.Name = "toolStripSeparator2"; - this.toolStripSeparator2.Size = new System.Drawing.Size(175, 6); - // - // pickSubtitleFileToolStripMenuItem - // - this.pickSubtitleFileToolStripMenuItem.Name = "pickSubtitleFileToolStripMenuItem"; - this.pickSubtitleFileToolStripMenuItem.Size = new System.Drawing.Size(178, 22); - this.pickSubtitleFileToolStripMenuItem.Text = "Pick subtitle file..."; - this.pickSubtitleFileToolStripMenuItem.Click += new System.EventHandler(this.pickSubtitleFileToolStripMenuItem_Click); - // - // removeSubtitleFileToolStripMenuItem - // - this.removeSubtitleFileToolStripMenuItem.Name = "removeSubtitleFileToolStripMenuItem"; - this.removeSubtitleFileToolStripMenuItem.Size = new System.Drawing.Size(178, 22); - this.removeSubtitleFileToolStripMenuItem.Text = "Remove subtitle file"; - this.removeSubtitleFileToolStripMenuItem.Click += new System.EventHandler(this.removeSubtitleFileToolStripMenuItem_Click); - // - // toolStripSeparator1 - // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(175, 6); - // - // deleteToolStripMenuItem - // - this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; - this.deleteToolStripMenuItem.Size = new System.Drawing.Size(178, 22); - this.deleteToolStripMenuItem.Text = "Delete"; - this.deleteToolStripMenuItem.Click += new System.EventHandler(this.deleteToolStripMenuItem_Click); - // - // clearToolStripMenuItem - // - this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; - this.clearToolStripMenuItem.Size = new System.Drawing.Size(178, 22); - this.clearToolStripMenuItem.Text = "Clear"; - this.clearToolStripMenuItem.Click += new System.EventHandler(this.clearToolStripMenuItem_Click); - // - // buttonOutputFileSettings - // - this.buttonOutputFileSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.buttonOutputFileSettings.Location = new System.Drawing.Point(255, 513); - this.buttonOutputFileSettings.Name = "buttonOutputFileSettings"; - this.buttonOutputFileSettings.Size = new System.Drawing.Size(167, 23); - this.buttonOutputFileSettings.TabIndex = 148; - this.buttonOutputFileSettings.Text = "Output file/folder..."; - this.buttonOutputFileSettings.UseVisualStyleBackColor = true; - this.buttonOutputFileSettings.Click += new System.EventHandler(this.buttonOutputFileSettings_Click); - // - // buttonPreview - // - this.buttonPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonPreview.Location = new System.Drawing.Point(621, 586); - this.buttonPreview.Name = "buttonPreview"; - this.buttonPreview.Size = new System.Drawing.Size(121, 23); - this.buttonPreview.TabIndex = 130; - this.buttonPreview.Text = "Preview"; - this.buttonPreview.UseVisualStyleBackColor = true; - this.buttonPreview.Click += new System.EventHandler(this.buttonPreview_Click); - // - // linkLabelHelp - // - this.linkLabelHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.linkLabelHelp.AutoSize = true; - this.linkLabelHelp.Location = new System.Drawing.Point(844, 563); - this.linkLabelHelp.Name = "linkLabelHelp"; - this.linkLabelHelp.Size = new System.Drawing.Size(29, 13); - this.linkLabelHelp.TabIndex = 120; - this.linkLabelHelp.TabStop = true; - this.linkLabelHelp.Text = "Help"; - this.linkLabelHelp.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelHelp_LinkClicked); - // - // contextMenuStripRes - // - this.contextMenuStripRes.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.useSourceResolutionToolStripMenuItem, - this.x2160ToolStripMenuItem, - this.uHD3840x2160ToolStripMenuItem, - this.k2048x1080ToolStripMenuItem, - this.dCI2KScope2048x858ToolStripMenuItem, - this.dCI2KFlat1998x1080ToolStripMenuItem, - this.toolStripMenuItemYouTubeShort, - this.p1920x1080ToolStripMenuItem, - this.x1080ToolStripMenuItem, - this.p1280x720ToolStripMenuItem, - this.x720ToolStripMenuItem, - this.p848x480ToolStripMenuItem, - this.pAL720x576ToolStripMenuItem, - this.nTSC720x480ToolStripMenuItem, - this.x352ToolStripMenuItem, - this.x272ToolStripMenuItem}); - this.contextMenuStripRes.Name = "contextMenuStripRes"; - this.contextMenuStripRes.Size = new System.Drawing.Size(221, 356); - this.contextMenuStripRes.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripRes_Opening); - // - // useSourceResolutionToolStripMenuItem - // - this.useSourceResolutionToolStripMenuItem.Name = "useSourceResolutionToolStripMenuItem"; - this.useSourceResolutionToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.useSourceResolutionToolStripMenuItem.Text = "Use source resoluton"; - this.useSourceResolutionToolStripMenuItem.Click += new System.EventHandler(this.useSourceResolutionToolStripMenuItem_Click); - // - // x2160ToolStripMenuItem - // - this.x2160ToolStripMenuItem.Name = "x2160ToolStripMenuItem"; - this.x2160ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.x2160ToolStripMenuItem.Text = "4K (4096x2160)"; - this.x2160ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // uHD3840x2160ToolStripMenuItem - // - this.uHD3840x2160ToolStripMenuItem.Name = "uHD3840x2160ToolStripMenuItem"; - this.uHD3840x2160ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.uHD3840x2160ToolStripMenuItem.Text = "UHD (3840x2160)"; - this.uHD3840x2160ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // k2048x1080ToolStripMenuItem - // - this.k2048x1080ToolStripMenuItem.Name = "k2048x1080ToolStripMenuItem"; - this.k2048x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.k2048x1080ToolStripMenuItem.Text = "2K (2048x1080)"; - this.k2048x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // dCI2KScope2048x858ToolStripMenuItem - // - this.dCI2KScope2048x858ToolStripMenuItem.Name = "dCI2KScope2048x858ToolStripMenuItem"; - this.dCI2KScope2048x858ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.dCI2KScope2048x858ToolStripMenuItem.Text = "DCI 2K Scope (2048x858)"; - this.dCI2KScope2048x858ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // dCI2KFlat1998x1080ToolStripMenuItem - // - this.dCI2KFlat1998x1080ToolStripMenuItem.Name = "dCI2KFlat1998x1080ToolStripMenuItem"; - this.dCI2KFlat1998x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.dCI2KFlat1998x1080ToolStripMenuItem.Text = "DCI 2K Flat (1998x1080)"; - this.dCI2KFlat1998x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // toolStripMenuItemYouTubeShort - // - this.toolStripMenuItemYouTubeShort.Name = "toolStripMenuItemYouTubeShort"; - this.toolStripMenuItemYouTubeShort.Size = new System.Drawing.Size(220, 22); - this.toolStripMenuItemYouTubeShort.Text = "YouTube shorts (1080x1920)"; - this.toolStripMenuItemYouTubeShort.Click += new System.EventHandler(this.ResolutionPickClick); - // - // p1920x1080ToolStripMenuItem - // - this.p1920x1080ToolStripMenuItem.Name = "p1920x1080ToolStripMenuItem"; - this.p1920x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.p1920x1080ToolStripMenuItem.Text = "1080p (1920x1080)"; - this.p1920x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // x1080ToolStripMenuItem - // - this.x1080ToolStripMenuItem.Name = "x1080ToolStripMenuItem"; - this.x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.x1080ToolStripMenuItem.Text = "1440x1080"; - this.x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // p1280x720ToolStripMenuItem - // - this.p1280x720ToolStripMenuItem.Name = "p1280x720ToolStripMenuItem"; - this.p1280x720ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.p1280x720ToolStripMenuItem.Text = "720p (1280x720)"; - this.p1280x720ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // x720ToolStripMenuItem - // - this.x720ToolStripMenuItem.Name = "x720ToolStripMenuItem"; - this.x720ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.x720ToolStripMenuItem.Text = "960x720"; - this.x720ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // p848x480ToolStripMenuItem - // - this.p848x480ToolStripMenuItem.Name = "p848x480ToolStripMenuItem"; - this.p848x480ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.p848x480ToolStripMenuItem.Text = "480p (848x480)"; - this.p848x480ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // pAL720x576ToolStripMenuItem - // - this.pAL720x576ToolStripMenuItem.Name = "pAL720x576ToolStripMenuItem"; - this.pAL720x576ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.pAL720x576ToolStripMenuItem.Text = "PAL (720x576)"; - this.pAL720x576ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // nTSC720x480ToolStripMenuItem - // - this.nTSC720x480ToolStripMenuItem.Name = "nTSC720x480ToolStripMenuItem"; - this.nTSC720x480ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.nTSC720x480ToolStripMenuItem.Text = "NTSC (720x480)"; - this.nTSC720x480ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // x352ToolStripMenuItem - // - this.x352ToolStripMenuItem.Name = "x352ToolStripMenuItem"; - this.x352ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.x352ToolStripMenuItem.Text = "640x352"; - this.x352ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // x272ToolStripMenuItem - // - this.x272ToolStripMenuItem.Name = "x272ToolStripMenuItem"; - this.x272ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); - this.x272ToolStripMenuItem.Text = "640x272"; - this.x272ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); - // - // buttonMode - // - this.buttonMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.buttonMode.ImeMode = System.Windows.Forms.ImeMode.NoControl; - this.buttonMode.Location = new System.Drawing.Point(748, 586); - this.buttonMode.Name = "buttonMode"; - this.buttonMode.Size = new System.Drawing.Size(125, 23); - this.buttonMode.TabIndex = 143; - this.buttonMode.Text = "Batch mode"; - this.buttonMode.UseVisualStyleBackColor = true; - this.buttonMode.Click += new System.EventHandler(this.buttonMode_Click); - // // numericUpDownCutToSeconds // this.numericUpDownCutToSeconds.BackColor = System.Drawing.SystemColors.Window; @@ -1031,6 +599,17 @@ namespace Nikse.SubtitleEdit.Forms this.labelCutFrom.TabIndex = 14; this.labelCutFrom.Text = "From"; // + // checkBoxCut + // + this.checkBoxCut.AutoSize = true; + this.checkBoxCut.Location = new System.Drawing.Point(12, 36); + this.checkBoxCut.Name = "checkBoxCut"; + this.checkBoxCut.Size = new System.Drawing.Size(42, 17); + this.checkBoxCut.TabIndex = 0; + this.checkBoxCut.Text = "Cut"; + this.checkBoxCut.UseVisualStyleBackColor = true; + this.checkBoxCut.CheckedChanged += new System.EventHandler(this.checkBoxCut_CheckedChanged); + // // labelVideoBitrate // this.labelVideoBitrate.AutoSize = true; @@ -1040,6 +619,28 @@ namespace Nikse.SubtitleEdit.Forms this.labelVideoBitrate.TabIndex = 14; this.labelVideoBitrate.Text = "labelVideoBitrate"; // + // checkBoxBox + // + this.checkBoxBox.AutoSize = true; + this.checkBoxBox.Location = new System.Drawing.Point(226, 27); + this.checkBoxBox.Name = "checkBoxBox"; + this.checkBoxBox.Size = new System.Drawing.Size(44, 17); + this.checkBoxBox.TabIndex = 2; + this.checkBoxBox.Text = "Box"; + this.checkBoxBox.UseVisualStyleBackColor = true; + this.checkBoxBox.CheckedChanged += new System.EventHandler(this.checkBoxBox_CheckedChanged); + // + // checkBoxAlignRight + // + this.checkBoxAlignRight.AutoSize = true; + this.checkBoxAlignRight.Location = new System.Drawing.Point(217, 114); + this.checkBoxAlignRight.Name = "checkBoxAlignRight"; + this.checkBoxAlignRight.Size = new System.Drawing.Size(72, 17); + this.checkBoxAlignRight.TabIndex = 10; + this.checkBoxAlignRight.Text = "Align right"; + this.checkBoxAlignRight.UseVisualStyleBackColor = true; + this.checkBoxAlignRight.CheckedChanged += new System.EventHandler(this.checkBoxAlignRight_CheckedChanged); + // // labelInfo // this.labelInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -1050,6 +651,17 @@ namespace Nikse.SubtitleEdit.Forms this.labelInfo.TabIndex = 1; this.labelInfo.Text = "Info"; // + // checkBoxRightToLeft + // + this.checkBoxRightToLeft.AutoSize = true; + this.checkBoxRightToLeft.Location = new System.Drawing.Point(120, 114); + this.checkBoxRightToLeft.Name = "checkBoxRightToLeft"; + this.checkBoxRightToLeft.Size = new System.Drawing.Size(80, 17); + this.checkBoxRightToLeft.TabIndex = 9; + this.checkBoxRightToLeft.Text = "Right to left"; + this.checkBoxRightToLeft.UseVisualStyleBackColor = true; + this.checkBoxRightToLeft.CheckedChanged += new System.EventHandler(this.checkBoxRightToLeft_CheckedChanged); + // // comboBoxSubtitleFont // this.comboBoxSubtitleFont.BackColor = System.Drawing.SystemColors.Window; @@ -1083,6 +695,29 @@ namespace Nikse.SubtitleEdit.Forms this.labelSubtitleFont.TabIndex = 7; this.labelSubtitleFont.Text = "Subtitle font"; // + // groupBoxVideo + // + this.groupBoxVideo.Controls.Add(this.labelCrfHint); + this.groupBoxVideo.Controls.Add(this.buttonVideoChooseStandardRes); + this.groupBoxVideo.Controls.Add(this.labelResolution); + this.groupBoxVideo.Controls.Add(this.numericUpDownWidth); + this.groupBoxVideo.Controls.Add(this.numericUpDownHeight); + this.groupBoxVideo.Controls.Add(this.labelX); + this.groupBoxVideo.Controls.Add(this.labelPreset); + this.groupBoxVideo.Controls.Add(this.comboBoxTune); + this.groupBoxVideo.Controls.Add(this.comboBoxPreset); + this.groupBoxVideo.Controls.Add(this.labelTune); + this.groupBoxVideo.Controls.Add(this.labelCRF); + this.groupBoxVideo.Controls.Add(this.comboBoxVideoEncoding); + this.groupBoxVideo.Controls.Add(this.comboBoxCrf); + this.groupBoxVideo.Controls.Add(this.labelVideoEncoding); + this.groupBoxVideo.Location = new System.Drawing.Point(6, 141); + this.groupBoxVideo.Name = "groupBoxVideo"; + this.groupBoxVideo.Size = new System.Drawing.Size(406, 166); + this.groupBoxVideo.TabIndex = 70; + this.groupBoxVideo.TabStop = false; + this.groupBoxVideo.Text = "Video"; + // // labelCrfHint // this.labelCrfHint.AutoSize = true; @@ -1094,6 +729,18 @@ namespace Nikse.SubtitleEdit.Forms this.labelCrfHint.TabIndex = 32; this.labelCrfHint.Text = "Hint"; // + // buttonVideoChooseStandardRes + // + this.buttonVideoChooseStandardRes.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.buttonVideoChooseStandardRes.Location = new System.Drawing.Point(257, 15); + this.buttonVideoChooseStandardRes.Name = "buttonVideoChooseStandardRes"; + this.buttonVideoChooseStandardRes.Size = new System.Drawing.Size(69, 23); + this.buttonVideoChooseStandardRes.TabIndex = 3; + this.buttonVideoChooseStandardRes.Text = "..."; + this.buttonVideoChooseStandardRes.UseVisualStyleBackColor = true; + this.buttonVideoChooseStandardRes.Click += new System.EventHandler(this.buttonVideoChooseStandardRes_Click); + // // labelResolution // this.labelResolution.AutoSize = true; @@ -1210,7 +857,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxTune.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxTune.DropDownWidth = 121; this.comboBoxTune.FormattingEnabled = true; - this.comboBoxTune.Items.AddRange(new object[] { + this.comboBoxTune.Items.AddRange(new string[] { "", "film", "animation", @@ -1238,7 +885,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxPreset.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxPreset.DropDownWidth = 121; this.comboBoxPreset.FormattingEnabled = true; - this.comboBoxPreset.Items.AddRange(new object[] { + this.comboBoxPreset.Items.AddRange(new string[] { "ultrafast", "superfast", "veryfast", @@ -1289,7 +936,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxVideoEncoding.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxVideoEncoding.DropDownWidth = 121; this.comboBoxVideoEncoding.FormattingEnabled = true; - this.comboBoxVideoEncoding.Items.AddRange(new object[] { + this.comboBoxVideoEncoding.Items.AddRange(new string[] { "libx264", "libx265", "libvpx-vp9", @@ -1322,7 +969,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxCrf.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxCrf.DropDownWidth = 121; this.comboBoxCrf.FormattingEnabled = true; - this.comboBoxCrf.Items.AddRange(new object[] { + this.comboBoxCrf.Items.AddRange(new string[] { "17", "18", "19", @@ -1354,6 +1001,45 @@ namespace Nikse.SubtitleEdit.Forms this.labelVideoEncoding.TabIndex = 3; this.labelVideoEncoding.Text = "Video enc"; // + // groupBoxAudio + // + this.groupBoxAudio.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBoxAudio.Controls.Add(this.listViewAudioTracks); + this.groupBoxAudio.Controls.Add(this.labelAudioEnc); + this.groupBoxAudio.Controls.Add(this.comboBoxAudioBitRate); + this.groupBoxAudio.Controls.Add(this.comboBoxAudioEnc); + this.groupBoxAudio.Controls.Add(this.labelAudioBitRate); + this.groupBoxAudio.Controls.Add(this.checkBoxMakeStereo); + this.groupBoxAudio.Controls.Add(this.labelAudioSampleRate); + this.groupBoxAudio.Controls.Add(this.comboBoxAudioSampleRate); + this.groupBoxAudio.Location = new System.Drawing.Point(430, 141); + this.groupBoxAudio.Name = "groupBoxAudio"; + this.groupBoxAudio.Size = new System.Drawing.Size(425, 166); + this.groupBoxAudio.TabIndex = 90; + this.groupBoxAudio.TabStop = false; + this.groupBoxAudio.Text = "Audio"; + // + // listViewAudioTracks + // + this.listViewAudioTracks.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listViewAudioTracks.CheckBoxes = true; + this.listViewAudioTracks.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderAudioTrack}); + this.listViewAudioTracks.HideSelection = false; + this.listViewAudioTracks.Location = new System.Drawing.Point(229, 29); + this.listViewAudioTracks.Name = "listViewAudioTracks"; + this.listViewAudioTracks.Size = new System.Drawing.Size(190, 122); + this.listViewAudioTracks.TabIndex = 45; + this.listViewAudioTracks.UseCompatibleStateImageBehavior = false; + this.listViewAudioTracks.View = System.Windows.Forms.View.Details; + // + // columnHeaderAudioTrack + // + this.columnHeaderAudioTrack.Text = "Audio tracks"; + this.columnHeaderAudioTrack.Width = 160; + // // labelAudioEnc // this.labelAudioEnc.AutoSize = true; @@ -1376,7 +1062,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxAudioBitRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxAudioBitRate.DropDownWidth = 121; this.comboBoxAudioBitRate.FormattingEnabled = true; - this.comboBoxAudioBitRate.Items.AddRange(new object[] { + this.comboBoxAudioBitRate.Items.AddRange(new string[] { "64k", "128k", "160k", @@ -1406,7 +1092,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxAudioEnc.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxAudioEnc.DropDownWidth = 121; this.comboBoxAudioEnc.FormattingEnabled = true; - this.comboBoxAudioEnc.Items.AddRange(new object[] { + this.comboBoxAudioEnc.Items.AddRange(new string[] { "copy", "aac"}); this.comboBoxAudioEnc.Location = new System.Drawing.Point(91, 29); @@ -1429,6 +1115,18 @@ namespace Nikse.SubtitleEdit.Forms this.labelAudioBitRate.TabIndex = 4; this.labelAudioBitRate.Text = "Bit rate"; // + // checkBoxMakeStereo + // + this.checkBoxMakeStereo.AutoSize = true; + this.checkBoxMakeStereo.Checked = true; + this.checkBoxMakeStereo.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBoxMakeStereo.Location = new System.Drawing.Point(91, 58); + this.checkBoxMakeStereo.Name = "checkBoxMakeStereo"; + this.checkBoxMakeStereo.Size = new System.Drawing.Size(57, 17); + this.checkBoxMakeStereo.TabIndex = 2; + this.checkBoxMakeStereo.Text = "Stereo"; + this.checkBoxMakeStereo.UseVisualStyleBackColor = true; + // // labelAudioSampleRate // this.labelAudioSampleRate.AutoSize = true; @@ -1451,7 +1149,7 @@ namespace Nikse.SubtitleEdit.Forms this.comboBoxAudioSampleRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.comboBoxAudioSampleRate.DropDownWidth = 121; this.comboBoxAudioSampleRate.FormattingEnabled = true; - this.comboBoxAudioSampleRate.Items.AddRange(new object[] { + this.comboBoxAudioSampleRate.Items.AddRange(new string[] { "44100 Hz", "48000 Hz", "88200 Hz", @@ -1514,6 +1212,17 @@ namespace Nikse.SubtitleEdit.Forms this.labelFileSize.TabIndex = 12; this.labelFileSize.Text = "File size in MB"; // + // checkBoxTargetFileSize + // + this.checkBoxTargetFileSize.AutoSize = true; + this.checkBoxTargetFileSize.Location = new System.Drawing.Point(22, 327); + this.checkBoxTargetFileSize.Name = "checkBoxTargetFileSize"; + this.checkBoxTargetFileSize.Size = new System.Drawing.Size(192, 17); + this.checkBoxTargetFileSize.TabIndex = 100; + this.checkBoxTargetFileSize.Text = "Target file size (two pass encoding)"; + this.checkBoxTargetFileSize.UseVisualStyleBackColor = true; + this.checkBoxTargetFileSize.CheckedChanged += new System.EventHandler(this.checkBoxTargetFileSize_CheckedChanged); + // // labelFileName // this.labelFileName.AutoSize = true; @@ -1570,6 +1279,116 @@ namespace Nikse.SubtitleEdit.Forms this.labelFontSize.TabIndex = 0; this.labelFontSize.Text = "Font size"; // + // listViewBatch + // + this.listViewBatch.AllowDrop = true; + this.listViewBatch.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listViewBatch.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.columnHeaderVideoFile, + this.columnHeaderResolution, + this.columnHeaderSize, + this.columnHeaderSubtitleFile, + this.columnHeaderStatus}); + this.listViewBatch.ContextMenuStrip = this.contextMenuStripBatch; + this.listViewBatch.FullRowSelect = true; + this.listViewBatch.HideSelection = false; + this.listViewBatch.Location = new System.Drawing.Point(6, 316); + this.listViewBatch.Name = "listViewBatch"; + this.listViewBatch.Size = new System.Drawing.Size(852, 191); + this.listViewBatch.TabIndex = 3; + this.listViewBatch.UseCompatibleStateImageBehavior = false; + this.listViewBatch.View = System.Windows.Forms.View.Details; + this.listViewBatch.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listViewBatch_ColumnClick); + this.listViewBatch.DragDrop += new System.Windows.Forms.DragEventHandler(this.listViewBatch_DragDrop); + this.listViewBatch.DragEnter += new System.Windows.Forms.DragEventHandler(this.listViewBatch_DragEnter); + this.listViewBatch.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listViewBatch_KeyDown); + // + // columnHeaderVideoFile + // + this.columnHeaderVideoFile.Text = "Video file"; + this.columnHeaderVideoFile.Width = 420; + // + // columnHeaderResolution + // + this.columnHeaderResolution.Text = "Resolution"; + this.columnHeaderResolution.Width = 80; + // + // columnHeaderSize + // + this.columnHeaderSize.Text = "Size"; + this.columnHeaderSize.Width = 80; + // + // columnHeaderSubtitleFile + // + this.columnHeaderSubtitleFile.Text = "Subtitle file"; + this.columnHeaderSubtitleFile.Width = 180; + // + // columnHeaderStatus + // + this.columnHeaderStatus.Text = "Status"; + this.columnHeaderStatus.Width = 80; + // + // contextMenuStripBatch + // + this.contextMenuStripBatch.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.addFilesToolStripMenuItem, + this.toolStripSeparator2, + this.pickSubtitleFileToolStripMenuItem, + this.removeSubtitleFileToolStripMenuItem, + this.toolStripSeparator1, + this.deleteToolStripMenuItem, + this.clearToolStripMenuItem}); + this.contextMenuStripBatch.Name = "contextMenuStripBatch"; + this.contextMenuStripBatch.Size = new System.Drawing.Size(179, 126); + this.contextMenuStripBatch.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripBatch_Opening); + // + // addFilesToolStripMenuItem + // + this.addFilesToolStripMenuItem.Name = "addFilesToolStripMenuItem"; + this.addFilesToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.addFilesToolStripMenuItem.Text = "Add video files..."; + this.addFilesToolStripMenuItem.Click += new System.EventHandler(this.addFilesToolStripMenuItem_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(175, 6); + // + // pickSubtitleFileToolStripMenuItem + // + this.pickSubtitleFileToolStripMenuItem.Name = "pickSubtitleFileToolStripMenuItem"; + this.pickSubtitleFileToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.pickSubtitleFileToolStripMenuItem.Text = "Pick subtitle file..."; + this.pickSubtitleFileToolStripMenuItem.Click += new System.EventHandler(this.pickSubtitleFileToolStripMenuItem_Click); + // + // removeSubtitleFileToolStripMenuItem + // + this.removeSubtitleFileToolStripMenuItem.Name = "removeSubtitleFileToolStripMenuItem"; + this.removeSubtitleFileToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.removeSubtitleFileToolStripMenuItem.Text = "Remove subtitle file"; + this.removeSubtitleFileToolStripMenuItem.Click += new System.EventHandler(this.removeSubtitleFileToolStripMenuItem_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(175, 6); + // + // deleteToolStripMenuItem + // + this.deleteToolStripMenuItem.Name = "deleteToolStripMenuItem"; + this.deleteToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.deleteToolStripMenuItem.Text = "Delete"; + this.deleteToolStripMenuItem.Click += new System.EventHandler(this.deleteToolStripMenuItem_Click); + // + // clearToolStripMenuItem + // + this.clearToolStripMenuItem.Name = "clearToolStripMenuItem"; + this.clearToolStripMenuItem.Size = new System.Drawing.Size(178, 22); + this.clearToolStripMenuItem.Text = "Clear"; + this.clearToolStripMenuItem.Click += new System.EventHandler(this.clearToolStripMenuItem_Click); + // // videoPlayerContainer1 // this.videoPlayerContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -1595,6 +1414,187 @@ namespace Nikse.SubtitleEdit.Forms this.videoPlayerContainer1.VideoWidth = 0; this.videoPlayerContainer1.Volume = 0D; // + // buttonOutputFileSettings + // + this.buttonOutputFileSettings.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.buttonOutputFileSettings.Location = new System.Drawing.Point(255, 513); + this.buttonOutputFileSettings.Name = "buttonOutputFileSettings"; + this.buttonOutputFileSettings.Size = new System.Drawing.Size(167, 23); + this.buttonOutputFileSettings.TabIndex = 148; + this.buttonOutputFileSettings.Text = "Output file/folder..."; + this.buttonOutputFileSettings.UseVisualStyleBackColor = true; + this.buttonOutputFileSettings.Click += new System.EventHandler(this.buttonOutputFileSettings_Click); + // + // buttonPreview + // + this.buttonPreview.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonPreview.Location = new System.Drawing.Point(621, 586); + this.buttonPreview.Name = "buttonPreview"; + this.buttonPreview.Size = new System.Drawing.Size(121, 23); + this.buttonPreview.TabIndex = 130; + this.buttonPreview.Text = "Preview"; + this.buttonPreview.UseVisualStyleBackColor = true; + this.buttonPreview.Click += new System.EventHandler(this.buttonPreview_Click); + // + // linkLabelHelp + // + this.linkLabelHelp.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.linkLabelHelp.AutoSize = true; + this.linkLabelHelp.Location = new System.Drawing.Point(844, 563); + this.linkLabelHelp.Name = "linkLabelHelp"; + this.linkLabelHelp.Size = new System.Drawing.Size(29, 13); + this.linkLabelHelp.TabIndex = 120; + this.linkLabelHelp.TabStop = true; + this.linkLabelHelp.Text = "Help"; + this.linkLabelHelp.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabelHelp_LinkClicked); + // + // contextMenuStripRes + // + this.contextMenuStripRes.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.useSourceResolutionToolStripMenuItem, + this.x2160ToolStripMenuItem, + this.uHD3840x2160ToolStripMenuItem, + this.k2048x1080ToolStripMenuItem, + this.dCI2KScope2048x858ToolStripMenuItem, + this.dCI2KFlat1998x1080ToolStripMenuItem, + this.toolStripMenuItemYouTubeShort, + this.p1920x1080ToolStripMenuItem, + this.x1080ToolStripMenuItem, + this.p1280x720ToolStripMenuItem, + this.x720ToolStripMenuItem, + this.p848x480ToolStripMenuItem, + this.pAL720x576ToolStripMenuItem, + this.nTSC720x480ToolStripMenuItem, + this.x352ToolStripMenuItem, + this.x272ToolStripMenuItem}); + this.contextMenuStripRes.Name = "contextMenuStripRes"; + this.contextMenuStripRes.Size = new System.Drawing.Size(221, 356); + this.contextMenuStripRes.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStripRes_Opening); + // + // useSourceResolutionToolStripMenuItem + // + this.useSourceResolutionToolStripMenuItem.Name = "useSourceResolutionToolStripMenuItem"; + this.useSourceResolutionToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.useSourceResolutionToolStripMenuItem.Text = "Use source resoluton"; + this.useSourceResolutionToolStripMenuItem.Click += new System.EventHandler(this.useSourceResolutionToolStripMenuItem_Click); + // + // x2160ToolStripMenuItem + // + this.x2160ToolStripMenuItem.Name = "x2160ToolStripMenuItem"; + this.x2160ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.x2160ToolStripMenuItem.Text = "4K (4096x2160)"; + this.x2160ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // uHD3840x2160ToolStripMenuItem + // + this.uHD3840x2160ToolStripMenuItem.Name = "uHD3840x2160ToolStripMenuItem"; + this.uHD3840x2160ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.uHD3840x2160ToolStripMenuItem.Text = "UHD (3840x2160)"; + this.uHD3840x2160ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // k2048x1080ToolStripMenuItem + // + this.k2048x1080ToolStripMenuItem.Name = "k2048x1080ToolStripMenuItem"; + this.k2048x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.k2048x1080ToolStripMenuItem.Text = "2K (2048x1080)"; + this.k2048x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // dCI2KScope2048x858ToolStripMenuItem + // + this.dCI2KScope2048x858ToolStripMenuItem.Name = "dCI2KScope2048x858ToolStripMenuItem"; + this.dCI2KScope2048x858ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.dCI2KScope2048x858ToolStripMenuItem.Text = "DCI 2K Scope (2048x858)"; + this.dCI2KScope2048x858ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // dCI2KFlat1998x1080ToolStripMenuItem + // + this.dCI2KFlat1998x1080ToolStripMenuItem.Name = "dCI2KFlat1998x1080ToolStripMenuItem"; + this.dCI2KFlat1998x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.dCI2KFlat1998x1080ToolStripMenuItem.Text = "DCI 2K Flat (1998x1080)"; + this.dCI2KFlat1998x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // toolStripMenuItemYouTubeShort + // + this.toolStripMenuItemYouTubeShort.Name = "toolStripMenuItemYouTubeShort"; + this.toolStripMenuItemYouTubeShort.Size = new System.Drawing.Size(220, 22); + this.toolStripMenuItemYouTubeShort.Text = "YouTube shorts (1080x1920)"; + this.toolStripMenuItemYouTubeShort.Click += new System.EventHandler(this.ResolutionPickClick); + // + // p1920x1080ToolStripMenuItem + // + this.p1920x1080ToolStripMenuItem.Name = "p1920x1080ToolStripMenuItem"; + this.p1920x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.p1920x1080ToolStripMenuItem.Text = "1080p (1920x1080)"; + this.p1920x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // x1080ToolStripMenuItem + // + this.x1080ToolStripMenuItem.Name = "x1080ToolStripMenuItem"; + this.x1080ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.x1080ToolStripMenuItem.Text = "1440x1080"; + this.x1080ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // p1280x720ToolStripMenuItem + // + this.p1280x720ToolStripMenuItem.Name = "p1280x720ToolStripMenuItem"; + this.p1280x720ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.p1280x720ToolStripMenuItem.Text = "720p (1280x720)"; + this.p1280x720ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // x720ToolStripMenuItem + // + this.x720ToolStripMenuItem.Name = "x720ToolStripMenuItem"; + this.x720ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.x720ToolStripMenuItem.Text = "960x720"; + this.x720ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // p848x480ToolStripMenuItem + // + this.p848x480ToolStripMenuItem.Name = "p848x480ToolStripMenuItem"; + this.p848x480ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.p848x480ToolStripMenuItem.Text = "480p (848x480)"; + this.p848x480ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // pAL720x576ToolStripMenuItem + // + this.pAL720x576ToolStripMenuItem.Name = "pAL720x576ToolStripMenuItem"; + this.pAL720x576ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.pAL720x576ToolStripMenuItem.Text = "PAL (720x576)"; + this.pAL720x576ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // nTSC720x480ToolStripMenuItem + // + this.nTSC720x480ToolStripMenuItem.Name = "nTSC720x480ToolStripMenuItem"; + this.nTSC720x480ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.nTSC720x480ToolStripMenuItem.Text = "NTSC (720x480)"; + this.nTSC720x480ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // x352ToolStripMenuItem + // + this.x352ToolStripMenuItem.Name = "x352ToolStripMenuItem"; + this.x352ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.x352ToolStripMenuItem.Text = "640x352"; + this.x352ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // x272ToolStripMenuItem + // + this.x272ToolStripMenuItem.Name = "x272ToolStripMenuItem"; + this.x272ToolStripMenuItem.Size = new System.Drawing.Size(220, 22); + this.x272ToolStripMenuItem.Text = "640x272"; + this.x272ToolStripMenuItem.Click += new System.EventHandler(this.ResolutionPickClick); + // + // buttonMode + // + this.buttonMode.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.buttonMode.ImeMode = System.Windows.Forms.ImeMode.NoControl; + this.buttonMode.Location = new System.Drawing.Point(748, 586); + this.buttonMode.Name = "buttonMode"; + this.buttonMode.Size = new System.Drawing.Size(125, 23); + this.buttonMode.TabIndex = 143; + this.buttonMode.Text = "Batch mode"; + this.buttonMode.UseVisualStyleBackColor = true; + this.buttonMode.Click += new System.EventHandler(this.buttonMode_Click); + // // labelPreviewPleaseWait // this.labelPreviewPleaseWait.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); diff --git a/src/ui/Forms/GenerateVideoWithHardSubs.cs b/src/ui/Forms/GenerateVideoWithHardSubs.cs index 4458db121..93432fa1d 100644 --- a/src/ui/Forms/GenerateVideoWithHardSubs.cs +++ b/src/ui/Forms/GenerateVideoWithHardSubs.cs @@ -42,6 +42,7 @@ namespace Nikse.SubtitleEdit.Forms public bool BatchMode { get; set; } public string BatchInfo { get; set; } private readonly List _batchVideoAndSubList; + private const int ListViewBatchSubItemIndexColumnVideoSize = 2; private const int ListViewBatchSubItemIndexColumnSubtitleFile = 3; private const int ListViewBatchSubItemIndexColumnStatus = 4; @@ -2109,9 +2110,23 @@ namespace Nikse.SubtitleEdit.Forms return; } - foreach (var fileName in openFileDialog1.FileNames) + try { - AddInputFile(fileName); + Cursor = Cursors.WaitCursor; + Application.DoEvents(); + for (var i = 0; i < listViewBatch.Columns.Count; i++) + { + ListViewSorter.SetSortArrow(listViewBatch.Columns[i], SortOrder.None); + } + + foreach (var fileName in openFileDialog1.FileNames) + { + AddInputFile(fileName); + } + } + finally + { + Cursor = Cursors.Default; } } } @@ -2163,9 +2178,9 @@ namespace Nikse.SubtitleEdit.Forms item.SubtitleFileFileSizeInBytes = new FileInfo(subFileName).Length; } - var width = 0; - var height = 0; var mediaInfo = FfmpegMediaInfo.Parse(fileName); + int width; + int height; if (mediaInfo.VideoWidth > 0 && mediaInfo.VideoHeight > 0) { width = mediaInfo.VideoWidth; @@ -2390,9 +2405,42 @@ namespace Nikse.SubtitleEdit.Forms useSourceResolutionToolStripMenuItem.Visible = BatchMode; } - private void nikseLabelOutputFileFolder_Click(object sender, EventArgs e) + private void listViewBatch_ColumnClick(object sender, ColumnClickEventArgs e) { + if (_converting || listViewBatch.Items.Count == 0) + { + return; + } + var lv = (ListView)sender; + if (!(lv.ListViewItemSorter is ListViewSorter sorter)) + { + sorter = new ListViewSorter + { + ColumnNumber = e.Column, + IsDisplayFileSize = e.Column == ListViewBatchSubItemIndexColumnVideoSize, + }; + lv.ListViewItemSorter = sorter; + } + + if (e.Column == sorter.ColumnNumber) + { + sorter.Descending = !sorter.Descending; // inverse sort direction + } + else + { + sorter.ColumnNumber = e.Column; + } + + lv.Sort(); + + ListViewSorter.SetSortArrow(listViewBatch.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); + + _batchVideoAndSubList.Clear(); + foreach (ListViewItem item in listViewBatch.Items) + { + _batchVideoAndSubList.Add((BatchVideoAndSub)item.Tag); + } } } } From f8db47dba625a2a43681adc8aaaa471abbc8265b Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 06:43:30 +0200 Subject: [PATCH 25/28] Skip audio files - thx Leon :) Work on #8272 --- src/ui/Forms/GenerateVideoWithHardSubs.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ui/Forms/GenerateVideoWithHardSubs.cs b/src/ui/Forms/GenerateVideoWithHardSubs.cs index 93432fa1d..fad969fe1 100644 --- a/src/ui/Forms/GenerateVideoWithHardSubs.cs +++ b/src/ui/Forms/GenerateVideoWithHardSubs.cs @@ -2207,6 +2207,12 @@ namespace Nikse.SubtitleEdit.Forms height = vInfo.Height; } + if (width == 0 || height == 0) + { + SeLogger.Error("Skipping burn-in file with no video: " + fileName); + return; // skip audio or damaged files + } + var listViewItem = new ListViewItem(fileName); listViewItem.Tag = item; listViewItem.SubItems.Add($"{width}x{height}"); From a72ebd04d5e2685fba53aa0c5008490727eb4ca2 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 18:22:47 +0200 Subject: [PATCH 26/28] Fix sort en gen video w hardsub --- src/ui/Forms/GenerateVideoWithHardSubs.cs | 40 ++++++++++++++++------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/ui/Forms/GenerateVideoWithHardSubs.cs b/src/ui/Forms/GenerateVideoWithHardSubs.cs index fad969fe1..722e7ca6a 100644 --- a/src/ui/Forms/GenerateVideoWithHardSubs.cs +++ b/src/ui/Forms/GenerateVideoWithHardSubs.cs @@ -979,12 +979,18 @@ namespace Nikse.SubtitleEdit.Forms if (_abort) { process.Kill(); + return false; } var v = (int)_processedFrames; SetProgress(v); } + if (_abort) + { + return false; + } + if (process.ExitCode != 0) { _log.AppendLine("ffmpeg exit code: " + process.ExitCode); @@ -2113,6 +2119,7 @@ namespace Nikse.SubtitleEdit.Forms try { Cursor = Cursors.WaitCursor; + Refresh(); Application.DoEvents(); for (var i = 0; i < listViewBatch.Columns.Count; i++) { @@ -2121,6 +2128,7 @@ namespace Nikse.SubtitleEdit.Forms foreach (var fileName in openFileDialog1.FileNames) { + Application.DoEvents(); AddInputFile(fileName); } } @@ -2297,13 +2305,14 @@ namespace Nikse.SubtitleEdit.Forms return; } - try - { - var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop); - labelPleaseWait.Visible = true; + var fileNames = (string[])e.Data.GetData(DataFormats.FileDrop); + labelPleaseWait.Visible = true; - TaskDelayHelper.RunDelayed(TimeSpan.FromMilliseconds(5), () => + TaskDelayHelper.RunDelayed(TimeSpan.FromMilliseconds(5), () => + { + try { + Cursor = Cursors.WaitCursor; foreach (var fileName in fileNames) { if (FileUtil.IsDirectory(fileName)) @@ -2312,15 +2321,17 @@ namespace Nikse.SubtitleEdit.Forms } else { + Application.DoEvents(); AddInputFile(fileName); } } - }); - } - finally - { - labelPleaseWait.Visible = false; - } + } + finally + { + Cursor = Cursors.Default; + labelPleaseWait.Visible = false; + } + }); } private void SearchFolder(string path) @@ -2418,6 +2429,11 @@ namespace Nikse.SubtitleEdit.Forms return; } + for (var i = 0; i < listViewBatch.Columns.Count; i++) + { + ListViewSorter.SetSortArrow(listViewBatch.Columns[i], SortOrder.None); + } + var lv = (ListView)sender; if (!(lv.ListViewItemSorter is ListViewSorter sorter)) { @@ -2436,6 +2452,8 @@ namespace Nikse.SubtitleEdit.Forms else { sorter.ColumnNumber = e.Column; + sorter.Descending = false; + sorter.IsDisplayFileSize = e.Column == ListViewBatchSubItemIndexColumnVideoSize; } lv.Sort(); From 03cef9b709106ee5a0c18330fdfe2fc2332f91d8 Mon Sep 17 00:00:00 2001 From: Nikolaj Olsson Date: Mon, 29 Apr 2024 19:38:35 +0200 Subject: [PATCH 27/28] Add sort direction icon in "Batch convert" --- src/ui/Forms/BatchConvert.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ui/Forms/BatchConvert.cs b/src/ui/Forms/BatchConvert.cs index 9e8ec11fc..71e77afee 100644 --- a/src/ui/Forms/BatchConvert.cs +++ b/src/ui/Forms/BatchConvert.cs @@ -3992,6 +3992,11 @@ namespace Nikse.SubtitleEdit.Forms return; } + for (var i = 0; i < listViewInputFiles.Columns.Count; i++) + { + ListViewSorter.SetSortArrow(listViewInputFiles.Columns[i], SortOrder.None); + } + if (!(listViewInputFiles.ListViewItemSorter is ListViewSorter sorter)) { sorter = new ListViewSorter @@ -4014,7 +4019,10 @@ namespace Nikse.SubtitleEdit.Forms sorter.IsNumber = false; sorter.IsDisplayFileSize = e.Column == columnHeaderSize.DisplayIndex; } + listViewInputFiles.Sort(); + + ListViewSorter.SetSortArrow(listViewInputFiles.Columns[e.Column], sorter.Descending ? SortOrder.Descending : SortOrder.Ascending); } private void buttonBrowseEncoding_Click(object sender, EventArgs e) From eee7a8851a1607f0e14b421dcbc1e7e38f74a662 Mon Sep 17 00:00:00 2001 From: bovirus <1262554+bovirus@users.noreply.github.com> Date: Mon, 29 Apr 2024 20:51:47 +0200 Subject: [PATCH 28/28] Update Italian language --- src/ui/Languages/it-IT.xml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ui/Languages/it-IT.xml b/src/ui/Languages/it-IT.xml index 21a5d2786..061f7507d 100644 --- a/src/ui/Languages/it-IT.xml +++ b/src/ui/Languages/it-IT.xml @@ -3,7 +3,7 @@ Subtitle Edit 4.0.5 - Tradotto da NAMP e bovirus - Data traduzione: 26.04.2024 + Tradotto da NAMP e bovirus - Data traduzione: 29.04.2024 it-IT OK @@ -1610,6 +1610,7 @@ Per usare una chiave API inserisci la chiave API di traduzione di Google in "Opz Vista sorgente Vista elenco Estrai audio... + Info media @@ -3168,6 +3169,11 @@ Vuoi continuare? Regolazione velocità: {0} / {1}... Unione traccia audio: {0} / {1}... Generazione parlato dal testo: {0} / {1}... + Rivedi clip audio + Rivedi e modifica/rimuovi clip audio + Riproduci + Continua automaticamente + Rigenera Tempistica SMPTE