diff --git a/Changelog.txt b/Changelog.txt index 3a5822fa16..4df658e8fd 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,12 +1,20 @@ Subtitle Edit Changelog 4.0.11 (xth March 2025) BETA +* NEW + * Shortcut for "Split line and play" - thx Leon + * Shortcut for "Cycle audio track" - thx darnn * IMPROVED: + * Update Italian translation - thx bovirus + * Update Portuguese translation - thx hugok79 * Update Polish translation - thx admas + * Update Chinese translation - thx nkh0472 + * Update Finnish translation - thx Teijo S * Include Whisper CuBlas again * Add simple retry for DeepL translate - thx rifatozkancomtr/dante3732 * Add Icelandic to Gemini - thx dhofverberg * Update NLLB API from v2 to v4 - thx chelis10 + * "Auto continue" is now a little faster - thx Andrebavila * FIXED: * Make cmd line parameters case insensitive - thx czyrichard * Fix LRC ms start time reading - thx AverageHoarder @@ -14,6 +22,7 @@ * Fix for "un-minimize" ocr window - thx GC * Fix "Remove text for HI" cmd line - thx Manujito * Fix for Netflix QC (write-out numbers 1-10) - thx senna2 + * Fix RemoveTextForHI webvtt issue - thx Manujito 4.0.10 (22nd December 2024) @@ -383,7 +392,7 @@ * Update Italian language - thx bovirus * Update Portuguese translation - thx hugok79 * Update Polish translation - thx admas - * Update Finnish translation - thx Teijo + * Update Finnish translation - thx Teijo S * Update Chinese translation - thx nkh0472 * Update French translation - thx Pierre * Update Bulgarian translation - thx Калин diff --git a/src/libse/Settings/Settings.cs b/src/libse/Settings/Settings.cs index 41d04d2152..15865e3f24 100644 --- a/src/libse/Settings/Settings.cs +++ b/src/libse/Settings/Settings.cs @@ -6713,6 +6713,12 @@ internal static void ReadShortcuts(XmlDocument doc, Shortcuts shortcuts) shortcuts.GeneralFocusTextBox = subNode.InnerText; } + subNode = node.SelectSingleNode("GeneralCycleAudioTrack"); + if (subNode != null) + { + shortcuts.GeneralCycleAudioTrack = subNode.InnerText; + } + subNode = node.SelectSingleNode("MainFileNew"); if (subNode != null) { @@ -10009,6 +10015,7 @@ internal static void WriteShortcuts(Shortcuts shortcuts, XmlWriter textWriter) textWriter.WriteElementString("GeneralTogglePreviewOnVideo", shortcuts.GeneralTogglePreviewOnVideo); textWriter.WriteElementString("GeneralHelp", shortcuts.GeneralHelp); textWriter.WriteElementString("GeneralFocusTextBox", shortcuts.GeneralFocusTextBox); + textWriter.WriteElementString("GeneralCycleAudioTrack", shortcuts.GeneralCycleAudioTrack); textWriter.WriteElementString("MainFileNew", shortcuts.MainFileNew); textWriter.WriteElementString("MainFileOpen", shortcuts.MainFileOpen); textWriter.WriteElementString("MainFileOpenKeepVideo", shortcuts.MainFileOpenKeepVideo); diff --git a/src/libse/Settings/Shortcuts.cs b/src/libse/Settings/Shortcuts.cs index 9530cdf460..5f48279417 100644 --- a/src/libse/Settings/Shortcuts.cs +++ b/src/libse/Settings/Shortcuts.cs @@ -79,6 +79,7 @@ public class Shortcuts public string GeneralTakeAutoBackup { get; set; } public string GeneralHelp { get; set; } public string GeneralFocusTextBox { get; set; } + public string GeneralCycleAudioTrack { get; set; } // File public string MainFileNew { get; set; } diff --git a/src/ui/Forms/Main.cs b/src/ui/Forms/Main.cs index 8310d2c7ed..c117538ce9 100644 --- a/src/ui/Forms/Main.cs +++ b/src/ui/Forms/Main.cs @@ -17566,6 +17566,11 @@ internal void MainKeyDown(object sender, KeyEventArgs e) e.SuppressKeyPress = true; } + else if (_shortcuts.MainGeneralCycleAudioTrack == e.KeyData) + { + CycleAudioTracks(); + e.SuppressKeyPress = true; + } else if (_shortcuts.MainGeneralClearBookmarks == e.KeyData) { ClearBookmarks(); @@ -19518,6 +19523,80 @@ private void ToggleCasingListView() } } + + private void CycleAudioTracks() + { + //TODO: Use "Index" for both players (or what works with ffmpeg audio extraction) + + var audioTracks = new List(); + + if (mediaPlayer.VideoPlayer is LibMpvDynamic libMpv) + { + try + { + audioTracks = libMpv.AudioTracks; + if (audioTracks.Count <= 1) + { + return; + } + + var track = audioTracks.FirstOrDefault(p => p.Index == VideoAudioTrackNumber); + var idx = audioTracks.IndexOf(track) + 1; + if (idx < 0 || idx >= audioTracks.Count) + { + idx = 0; + } + var audioTrack = audioTracks[idx]; + libMpv.AudioTrackNumber = audioTrack.Index; + VideoAudioTrackNumber = audioTrack.Index; + ShowStatus(audioTrack.ToString()); + } + catch + { + return; + } + } + else if (mediaPlayer.VideoPlayer is LibVlcDynamic libVlc) + { + try + { + audioTracks = libVlc.GetAudioTracks(); + if (audioTracks.Count <= 1) + { + return; + } + + var track = audioTracks.FirstOrDefault(p => p.TrackNumber == VideoAudioTrackNumber); + var idx = audioTracks.IndexOf(track) + 1; + if (idx < 0 || idx >= audioTracks.Count) + { + idx = 0; + } + var audioTrack = audioTracks[idx]; + libVlc.AudioTrackNumber = audioTrack.TrackNumber; // Index or TrackNumber? + VideoAudioTrackNumber = audioTrack.TrackNumber; // Index or TrackNumber? + ShowStatus(audioTrack.ToString()); + } + catch + { + return; + } + } + } + + private AudioTrack CycleAudioTrack(List audioTracks) + { + var track = audioTracks.FirstOrDefault(p => p.Index == VideoAudioTrackNumber); + var idx = audioTracks.IndexOf(track) + 1; + if (idx < 0 || idx >= audioTracks.Count) + { + idx = 0; + } + + ShowStatus(audioTracks[idx].ToString()); + return audioTracks[idx]; + } + private void ShowCheckFixTimingViaShotChanges() { using (var form = new AdjustTimingViaShotChanges(_subtitle, _videoFileName, audioVisualizer.WavePeaks, audioVisualizer.ShotChanges)) @@ -29972,9 +30051,9 @@ private void ToolStripMenuItemVideoDropDownOpening(object sender, EventArgs e) for (int i = 0; i < audioTracks.Count; i++) { var at = audioTracks[i]; - toolStripMenuItemSetAudioTrack.DropDownItems.Add(string.IsNullOrWhiteSpace(at.Value) ? at.Key.ToString(CultureInfo.InvariantCulture) : at.Value, null, ChooseAudioTrack); - toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1].Tag = at.Key.ToString(CultureInfo.InvariantCulture); - if (at.Key == VideoAudioTrackNumber) + toolStripMenuItemSetAudioTrack.DropDownItems.Add(at.ToString(), null, ChooseAudioTrack); + toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1].Tag = at.TrackNumber.ToString(CultureInfo.InvariantCulture); + if (at.TrackNumber == VideoAudioTrackNumber) { ((ToolStripMenuItem)toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1]).Checked = true; } @@ -30004,9 +30083,8 @@ private void ToolStripMenuItemVideoDropDownOpening(object sender, EventArgs e) for (int i = 0; i < audioTracks.Count; i++) { var at = audioTracks[i]; - var trackText = string.IsNullOrWhiteSpace(at.Value) ? at.Key.ToString(CultureInfo.InvariantCulture) : "Track " + at.Key + " - " + char.ToUpper(at.Value[0]) + at.Value.Substring(1); - toolStripMenuItemSetAudioTrack.DropDownItems.Add(trackText, null, ChooseAudioTrack); - toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1].Tag = at.Key.ToString(CultureInfo.InvariantCulture); + toolStripMenuItemSetAudioTrack.DropDownItems.Add(at.ToString(), null, ChooseAudioTrack); + toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1].Tag = at.TrackNumber.ToString(CultureInfo.InvariantCulture); if (i == VideoAudioTrackNumber) { ((ToolStripMenuItem)toolStripMenuItemSetAudioTrack.DropDownItems[toolStripMenuItemSetAudioTrack.DropDownItems.Count - 1]).Checked = true; diff --git a/src/ui/Forms/Options/Settings.cs b/src/ui/Forms/Options/Settings.cs index ecf391f6d4..87760256a8 100644 --- a/src/ui/Forms/Options/Settings.cs +++ b/src/ui/Forms/Options/Settings.cs @@ -1568,6 +1568,7 @@ private void MakeShortcutsTreeView(LanguageStructure.Settings language) AddNode(generalNode, language.GoToNextSubtitleAndFocusWaveform, nameof(Configuration.Settings.Shortcuts.GeneralGoToNextSubtitleAndFocusWaveform)); AddNode(generalNode, language.ToggleBookmarks, nameof(Configuration.Settings.Shortcuts.GeneralToggleBookmarks)); AddNode(generalNode, language.FocusTextBox, nameof(Configuration.Settings.Shortcuts.GeneralFocusTextBox)); + AddNode(generalNode, language.CycleAudioTracks, nameof(Configuration.Settings.Shortcuts.GeneralCycleAudioTrack)); AddNode(generalNode, language.ToggleBookmarksWithComment, nameof(Configuration.Settings.Shortcuts.GeneralToggleBookmarksWithText), true); AddNode(generalNode, LanguageSettings.Current.Bookmarks.EditBookmark, nameof(Configuration.Settings.Shortcuts.GeneralEditBookmarks), true); AddNode(generalNode, language.ClearBookmarks, nameof(Configuration.Settings.Shortcuts.GeneralClearBookmarks)); diff --git a/src/ui/Logic/Language.cs b/src/ui/Logic/Language.cs index 487785a0ac..55f1747879 100644 --- a/src/ui/Logic/Language.cs +++ b/src/ui/Logic/Language.cs @@ -2705,6 +2705,7 @@ can edit in same subtitle file (collaboration)", SettingsName = "Settings", ToggleBookmarks = "Toggle bookmarks", FocusTextBox = "Focus text box", + CycleAudioTracks = "Cycle audio tracks", ToggleBookmarksWithComment = "Toggle bookmarks - add comment", ClearBookmarks = "Clear bookmarks", ExportBookmarks = "Export bookmarks...", diff --git a/src/ui/Logic/LanguageStructure.cs b/src/ui/Logic/LanguageStructure.cs index 5da2baac60..e418b1557a 100644 --- a/src/ui/Logic/LanguageStructure.cs +++ b/src/ui/Logic/LanguageStructure.cs @@ -2513,6 +2513,7 @@ public class Settings public string SettingsName { get; set; } public string ToggleBookmarks { get; set; } public string FocusTextBox { get; set; } + public string CycleAudioTracks { get; set; } public string ToggleBookmarksWithComment { get; set; } public string ClearBookmarks { get; set; } public string ExportBookmarks { get; set; } diff --git a/src/ui/Logic/MainShortcuts.cs b/src/ui/Logic/MainShortcuts.cs index 8375347ab0..c17c44480f 100644 --- a/src/ui/Logic/MainShortcuts.cs +++ b/src/ui/Logic/MainShortcuts.cs @@ -113,6 +113,7 @@ public class MainShortcuts public Keys MainAutoCalcCurrentDurationByMinReadingSpeed { get; set; } public Keys MainGeneralToggleBookmarks { get; set; } public Keys MainGeneralFocusTextBox { get; set; } + public Keys MainGeneralCycleAudioTrack { get; set; } public Keys MainGeneralToggleBookmarksAddComment { get; set; } public Keys MainGeneralEditBookmark { get; set; } public Keys MainGeneralClearBookmarks { get; set; } @@ -382,6 +383,7 @@ public void SetShortcuts() MainAutoCalcCurrentDurationByMinReadingSpeed = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralAutoCalcCurrentDurationByMinReadingSpeed); MainGeneralToggleBookmarks = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralToggleBookmarks); MainGeneralFocusTextBox = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralFocusTextBox); + MainGeneralCycleAudioTrack = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralCycleAudioTrack); MainGeneralToggleBookmarksAddComment = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralToggleBookmarksWithText); MainGeneralEditBookmark = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralEditBookmarks); MainGeneralClearBookmarks = UiUtil.GetKeys(Configuration.Settings.Shortcuts.GeneralClearBookmarks); diff --git a/src/ui/Logic/VideoPlayers/AudioTrack.cs b/src/ui/Logic/VideoPlayers/AudioTrack.cs new file mode 100644 index 0000000000..592f143454 --- /dev/null +++ b/src/ui/Logic/VideoPlayers/AudioTrack.cs @@ -0,0 +1,23 @@ +using Nikse.SubtitleEdit.Core.Common; + +namespace Nikse.SubtitleEdit.Logic.VideoPlayers +{ + public class AudioTrack + { + public int TrackNumber { get; set; } + public string Name { get; set; } + public int Index { get; set; } + + public AudioTrack(int trackNumber, string name, int index) + { + TrackNumber = trackNumber; + Name = name; + Index = index; + } + + public override string ToString() + { + return $"{TrackNumber}: {Name.CapitalizeFirstLetter()}".TrimEnd(':').TrimEnd(); + } + } +} diff --git a/src/ui/Logic/VideoPlayers/LibMpvDynamic.cs b/src/ui/Logic/VideoPlayers/LibMpvDynamic.cs index dcc940c787..3a73b91a7e 100644 --- a/src/ui/Logic/VideoPlayers/LibMpvDynamic.cs +++ b/src/ui/Logic/VideoPlayers/LibMpvDynamic.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; @@ -436,8 +437,8 @@ public string VideoCodec public override bool IsPlaying => !IsPaused; - private List> _audioTrackIds; - public List> AudioTracks + private List _audioTrackIds; + public List AudioTracks { get { @@ -448,13 +449,14 @@ public List> AudioTracks if (_mpvHandle == IntPtr.Zero) { - return new List>(); + return new List(); } - _audioTrackIds = new List>(); + _audioTrackIds = new List(); var lpBuffer = IntPtr.Zero; _mpvGetPropertyString(_mpvHandle, GetUtf8Bytes("track-list"), MpvFormatString, ref lpBuffer); string trackListJson = Marshal.PtrToStringAnsi(lpBuffer); + var idx = 0; foreach (var json in Json.ReadObjectArray(trackListJson)) { var trackType = Json.ReadTag(json, "type"); @@ -463,7 +465,8 @@ public List> AudioTracks var lang = Json.ReadTag(json, "lang"); if (int.TryParse(Json.ReadTag(json, "id"), out var id)) { - _audioTrackIds.Add(new KeyValuePair(id, lang)); + _audioTrackIds.Add(new AudioTrack(id, lang, idx)); + idx++; } } } @@ -494,7 +497,8 @@ public int AudioTrackNumber return 0; } - var idx = _audioTrackIds.FindIndex(x => x.Key == id); + var audioTrack = _audioTrackIds.FirstOrDefault(x => x.TrackNumber == id); + var idx = audioTrack == null ? -1 : _audioTrackIds.IndexOf(audioTrack); var number = AudioTracks.Count > 1 && idx != -1 ? idx : 0; _mpvFree(lpBuffer); return number; @@ -504,7 +508,7 @@ public int AudioTrackNumber string id = "1"; if (AudioTracks.Count > 1 && value >= 0 && value < _audioTrackIds.Count) { - id = _audioTrackIds[value].Key.ToString(); + id = _audioTrackIds[value].TrackNumber.ToString(CultureInfo.InvariantCulture); } DoMpvCommand("set", "aid", id); } diff --git a/src/ui/Logic/VideoPlayers/LibVlcDynamic.cs b/src/ui/Logic/VideoPlayers/LibVlcDynamic.cs index f16367f064..0588eaa5ee 100644 --- a/src/ui/Logic/VideoPlayers/LibVlcDynamic.cs +++ b/src/ui/Logic/VideoPlayers/LibVlcDynamic.cs @@ -550,19 +550,21 @@ private struct TrackDescription public IntPtr PNext { get; set; } } - public List> GetAudioTracks() + public List GetAudioTracks() { int count = _libvlc_audio_get_track_count(_mediaPlayer); var trackDescriptionsPointer = _libvlc_audio_get_track_description(_mediaPlayer); - var trackDescriptionList = new List>(); + var trackDescriptionList = new List(); IntPtr trackDescriptionPointer = trackDescriptionsPointer; + var idx = 0; while (trackDescriptionPointer != IntPtr.Zero) { var trackDescription = (TrackDescription)Marshal.PtrToStructure(trackDescriptionPointer, typeof(TrackDescription)); string s = Marshal.PtrToStringAnsi(trackDescription.Name); if (trackDescription.Id != -1) // not disable { - trackDescriptionList.Add(new KeyValuePair(trackDescription.Id, s)); + trackDescriptionList.Add(new AudioTrack(trackDescription.Id, s, idx)); + idx++; } trackDescriptionPointer = trackDescription.PNext; }