using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Globalization; using System.Windows.Forms; using System.Windows.Interop; using System.Windows; using MpvNet.ExtensionMethod; using MpvNet.Windows.WinForms; using MpvNet.Windows.WPF.Views; using MpvNet.Windows.WPF; using MpvNet.Windows.WPF.MsgBox; using MpvNet.Windows.Help; using MpvNet.Help; namespace MpvNet; public class GuiCommand { Dictionary>>? _commands; public event Action? ScaleWindow; public event Action? MoveWindow; public event Action? WindowScaleNet; public event Action? ShowMenu; public static GuiCommand Current { get; } = new(); public Dictionary>> Commands => _commands ??= new() { ["add-to-path"] = args => AddToPath(), ["edit-conf-file"] = EditCongFile, ["install-command-palette"] = args => InstallCommandPalette(), ["load-audio"] = LoadAudio, ["load-sub"] = LoadSubtitle, ["move-window"] = args => MoveWindow?.Invoke(args[0]), ["open-clipboard"] = OpenFromClipboard, ["open-files"] = OpenFiles, ["open-optical-media"] = Open_DVD_Or_BD_Folder, ["reg-file-assoc"] = RegisterFileAssociations, ["remove-from-path"] = args => RemoveFromPath(), ["scale-window"] = args => ScaleWindow?.Invoke(float.Parse(args[0], CultureInfo.InvariantCulture)), ["show-about"] = args => ShowDialog(typeof(AboutWindow)), ["show-bindings"] = args => ShowBindings(), ["show-commands"] = args => ShowCommands(), ["show-conf-editor"] = args => ShowDialog(typeof(ConfWindow)), ["show-decoders"] = args => ShowDecoders(), ["show-demuxers"] = args => ShowDemuxers(), ["show-info"] = args => ShowMediaInfo(new[] { "osd" }), ["show-input-editor"] = args => ShowDialog(typeof(InputWindow)), ["show-keys"] = args => ShowKeys(), ["show-media-info"] = ShowMediaInfo, ["show-menu"] = args => ShowMenu?.Invoke(), ["show-profiles"] = args => Msg.ShowInfo(Player.GetProfiles()), ["show-properties"] = args => Player.Command("script-binding select/show-properties"), ["show-protocols"] = args => ShowProtocols(), ["show-recent-in-command-palette"] = args => ShowRecentFilesInCommandPalette(), ["stream-quality"] = args => StreamQuality(), ["window-scale"] = args => WindowScaleNet?.Invoke(float.Parse(args[0], CultureInfo.InvariantCulture)), // deprecated ["show-recent"] = args => ShowRemoved(), // deprecated ["quick-bookmark"] = args => QuickBookmark(), // deprecated ["show-history"] = args => ShowHistory(), // deprecated ["show-playlist"] = args => Player.Command("script-binding select/select-playlist"), // deprecated ["show-command-palette"] = args => Player.Command("script-binding select/select-binding"), // deprecated ["show-audio-tracks"] = args => Player.Command("script-binding select/select-aid"), // deprecated ["show-subtitle-tracks"] = args => Player.Command("script-binding select/select-sid"), // deprecated ["show-audio-devices"] = args => Player.Command("script-binding select/select-audio-device"), // deprecated }; void ShowDialog(Type winType) { Window? win = Activator.CreateInstance(winType) as Window; new WindowInteropHelper(win).Owner = MainForm.Instance!.Handle; win?.ShowDialog(); } void LoadSubtitle(IList args) { using var dialog = new OpenFileDialog(); string path = Player.GetPropertyString("path"); if (File.Exists(path)) dialog.InitialDirectory = Path.GetDirectoryName(path); dialog.Multiselect = true; if (dialog.ShowDialog() == DialogResult.OK) foreach (string filename in dialog.FileNames) Player.CommandV("sub-add", filename); } void OpenFiles(IList args) { bool append = false; foreach (string arg in args) if (arg == "append") append = true; using var dialog = new OpenFileDialog() { Multiselect = true }; if (dialog.ShowDialog() == DialogResult.OK) Player.LoadFiles(dialog.FileNames, true, append); } void Open_DVD_Or_BD_Folder(IList args) { var dialog = new FolderBrowserDialog(); if (dialog.ShowDialog() == DialogResult.OK) Player.LoadDiskFolder(dialog.SelectedPath); } void EditCongFile(IList args) { string file = Player.ConfigFolder + args[0]; if (!File.Exists(file)) { string msg = $"{args[0]} does not exist. Would you like to create it?"; if (Msg.ShowQuestion(msg) == MessageBoxResult.OK) File.WriteAllText(file, ""); } if (File.Exists(file)) ProcessHelp.ShellExecute(WinApiHelp.GetAppPathForExtension("txt"), "\"" + file + "\""); } void ShowTextWithEditor(string name, string text) { string file = Path.Combine(Path.GetTempPath(), name + ".txt"); App.TempFiles.Add(file); File.WriteAllText(file, BR + text.Trim() + BR); ProcessHelp.ShellExecute(WinApiHelp.GetAppPathForExtension("txt"), "\"" + file + "\""); } void ShowCommands() { string json = Core.GetPropertyString("command-list"); var enumerator = JsonDocument.Parse(json).RootElement.EnumerateArray(); var commands = enumerator.OrderBy(it => it.GetProperty("name").GetString()); StringBuilder sb = new StringBuilder(); foreach (var cmd in commands) { sb.AppendLine(); sb.AppendLine(cmd.GetProperty("name").GetString()); foreach (var args in cmd.GetProperty("args").EnumerateArray()) { string value = args.GetProperty("name").GetString() + " <" + args.GetProperty("type").GetString()!.ToLower() + ">"; if (args.GetProperty("optional").GetBoolean()) value = "[" + value + "]"; sb.AppendLine(" " + value); } } string header = BR + "https://mpv.io/manual/master/#list-of-input-commands" + BR2 + "https://github.com/stax76/mpv-scripts#command_palette" + BR; ShowTextWithEditor("Input Commands", header + sb.ToString()); } void ShowKeys() => ShowTextWithEditor("Keys", Core.GetPropertyString("input-key-list").Replace(",", BR)); void ShowProtocols() => ShowTextWithEditor("Protocols", Core.GetPropertyString("protocol-list").Replace(",", BR)); void ShowDecoders() => ShowTextWithEditor("Decoders", Core.GetPropertyOsdString("decoder-list").Replace(",", BR)); void ShowDemuxers() => ShowTextWithEditor("Demuxers", Core.GetPropertyOsdString("demuxer-lavf-list").Replace(",", BR)); void OpenFromClipboard(IList args) { bool append = args.Count == 1 && args[0] == "append"; if (System.Windows.Forms.Clipboard.ContainsFileDropList()) { string[] files = System.Windows.Forms.Clipboard.GetFileDropList().Cast().ToArray(); Player.LoadFiles(files, false, append); if (append) Player.CommandV("show-text", _("Files/URLs were added to the playlist")); } else { string clipboard = System.Windows.Forms.Clipboard.GetText(); List files = new List(); foreach (string i in clipboard.Split(BR.ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) if (i.Contains("://") || File.Exists(i)) files.Add(i); if (files.Count == 0) { Terminal.WriteError(_("The clipboard does not contain a valid URL or file.")); return; } Player.LoadFiles(files.ToArray(), false, append); if (append) Player.CommandV("show-text", _("Files/URLs were added to the playlist")); } } void LoadAudio(IList args) { using var dialog = new OpenFileDialog(); string path = Player.GetPropertyString("path"); if (File.Exists(path)) dialog.InitialDirectory = Path.GetDirectoryName(path); dialog.Multiselect = true; if (dialog.ShowDialog() == DialogResult.OK) foreach (string i in dialog.FileNames) Player.CommandV("audio-add", i); } void RegisterFileAssociations(IList args) { string perceivedType = args[0]; string[] extensions = Array.Empty(); switch (perceivedType) { case "video": extensions = FileTypes.GetVideoExts(); break; case "audio": extensions = FileTypes.GetAudioExts(); break; case "image": extensions = FileTypes.GetImgExts(); break; } try { using Process proc = new Process(); proc.StartInfo.FileName = Environment.ProcessPath; proc.StartInfo.Arguments = "--register-file-associations " + perceivedType + " " + string.Join(" ", extensions); proc.StartInfo.Verb = "runas"; proc.StartInfo.UseShellExecute = true; proc.Start(); proc.WaitForExit(); if (proc.ExitCode == 0) { string msgRestart = _("File Explorer icons will refresh after process restart."); if (perceivedType == "unreg") Msg.ShowInfo(_("File associations were successfully removed.") + BR2 + msgRestart); else Msg.ShowInfo(_("File associations were successfully created.") + BR2 + msgRestart); } else Msg.ShowError(_("Error creating file associations.")); } catch { } } void InstallCommandPalette() { if (Msg.ShowQuestion("Install command palette?") != MessageBoxResult.OK) return; try { Environment.SetEnvironmentVariable("MPV_CONFIG_DIR", Player.ConfigFolder); using Process proc = new Process(); proc.StartInfo.FileName = "powershell"; proc.StartInfo.Arguments = "-executionpolicy bypass -nologo -noexit -noprofile -command \"irm https://raw.githubusercontent.com/stax76/mpv-scripts/refs/heads/main/powershell/command_palette_installer.ps1 | iex\""; proc.Start(); } catch { } } void StreamQuality() { int version = Player.GetPropertyInt("user-data/command-palette/version"); if (version >= 2) Player.Command("script-message-to command_palette show-command-palette \"Stream Quality\""); else { var r = Msg.ShowQuestion("The Stream Quality feature requires the command palette to be installed." + BR2 + "Would you like to install the command palette now?"); if (r == MessageBoxResult.OK) Player.Command("script-message-to mpvnet install-command-palette"); } } void ShowRecentFilesInCommandPalette() { Obj o = new(); o.title = "Recent Files"; o.selected_index = 0; var items = new List(); foreach (string file in App.Settings.RecentFiles) items.Add(new Item() { title = Path.GetFileName(file), value = new string []{ "loadfile", file }, hint = file}); o.items = items.ToArray(); string json = JsonSerializer.Serialize(o); Player.CommandV("script-message", "show-command-palette-json", json); } class Obj { public string title { get; set; } = ""; public int selected_index { get; set; } = 0; public Item[] items { get; set; } = Array.Empty(); } class Item { public string[] value { get; set; } = Array.Empty(); public string title { get; set; } = ""; public string hint { get; set; } = ""; } void ShowMediaInfo(IList args) { if (Player.PlaylistPos == -1) return; bool full = args.Contains("full"); bool raw = args.Contains("raw"); bool editor = args.Contains("editor"); bool osd = args.Contains("osd") || args == null || args.Count == 0; long fileSize = 0; string text = ""; string path = Player.GetPropertyString("path"); if (File.Exists(path) && osd) { if (FileTypes.IsAudio(path.Ext())) { text = Player.GetPropertyOsdString("filtered-metadata"); Player.CommandV("show-text", text, "5000"); return; } else if (FileTypes.IsImage(path.Ext())) { fileSize = new FileInfo(path).Length; text = "Width: " + Player.GetPropertyInt("width") + "\n" + "Height: " + Player.GetPropertyInt("height") + "\n" + "Size: " + Convert.ToInt32(fileSize / 1024.0) + " KB\n" + "Type: " + path.Ext().ToUpper(); Player.CommandV("show-text", text, "5000"); return; } } if (path.Contains("://")) { if (path.Contains("://")) path = Player.GetPropertyString("media-title"); string videoFormat = Player.GetPropertyString("video-format").ToUpper(); string audioCodec = Player.GetPropertyString("audio-codec-name").ToUpper(); int width = Player.GetPropertyInt("video-params/w"); int height = Player.GetPropertyInt("video-params/h"); TimeSpan len = TimeSpan.FromSeconds(Player.GetPropertyDouble("duration")); text = path.FileName() + "\n"; text += FormatTime(len.TotalMinutes) + ":" + FormatTime(len.Seconds) + "\n"; if (fileSize > 0) text += Convert.ToInt32(fileSize / 1024.0 / 1024.0) + " MB\n"; text += $"{width} x {height}\n"; text += $"{videoFormat}\n{audioCodec}"; Player.CommandV("show-text", text, "5000"); return; } if (App.MediaInfo && !osd && File.Exists(path) && !path.Contains(@"\\.\pipe\")) using (MediaInfo mediaInfo = new MediaInfo(path)) text = Regex.Replace(mediaInfo.GetSummary(full, raw), "Unique ID.+", ""); else { Player.UpdateExternalTracks(); text = "N: " + Player.GetPropertyString("filename") + BR; lock (Player.MediaTracksLock) foreach (MediaTrack track in Player.MediaTracks) text += track.Text + BR; } text = text.TrimEx(); if (editor) ShowTextWithEditor("media-info", text); else if (osd) Command.ShowText(text.Replace("\r", ""), 5000, 16); else { MessageBoxEx.SetFont("Consolas"); Msg.ShowInfo(text); MessageBoxEx.SetFont("Segoe UI"); } } string FormatTime(double value) => ((int)value).ToString("00"); void ShowBindings() => ShowTextWithEditor("Bindings", Player.UsedInputConfContent); void AddToPath() { string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User)!; if (path.ToLower().Contains(Folder.Startup.TrimEnd(Path.DirectorySeparatorChar).ToLower())) { Msg.ShowWarning(_("mpv.net is already in the Path environment variable.")); return; } Environment.SetEnvironmentVariable("Path", Folder.Startup.TrimEnd(Path.DirectorySeparatorChar) + ";" + path, EnvironmentVariableTarget.User); Msg.ShowInfo(_("mpv.net was successfully added to the Path environment variable.")); } void RemoveFromPath() { string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User)!; if (!path.Contains(Folder.Startup.TrimEnd(Path.DirectorySeparatorChar))) { Msg.ShowWarning(_("mpv.net was not found in the Path environment variable.")); return; } path = path.Replace(Folder.Startup.TrimEnd(Path.DirectorySeparatorChar), ""); path = path.Replace(";;", ";").Trim(';'); Environment.SetEnvironmentVariable("Path", path, EnvironmentVariableTarget.User); Msg.ShowInfo(_("mpv.net was successfully removed from the Path environment variable.")); } // deprecated void QuickBookmark() => Msg.ShowInfo(_("This feature was removed, but there are user scripts:") + BR2 + "https://github.com/stax76/mpv-scripts/blob/main/misc.lua"); // deprecated void ShowHistory() => Msg.ShowInfo(_("This feature was removed, but there are user scripts:") + BR2 + "https://github.com/stax76/mpv-scripts/blob/main/history.lua"); // deprecated void ShowRemoved() => Msg.ShowInfo(_("This feature was removed.")); }