replace v6 with experimental v7 code

This commit is contained in:
stax76
2023-10-24 11:17:45 +02:00
parent fb27bb8727
commit 5706d7b66d
212 changed files with 15014 additions and 12173 deletions

215
src/MpvNet/App.cs Normal file
View File

@@ -0,0 +1,215 @@

using CommunityToolkit.Mvvm.Messaging;
using MpvNet.ExtensionMethod;
using MpvNet.Help;
using MpvNet.MVVM;
namespace MpvNet;
public class AppClass
{
public List<string> TempFiles { get; } = new List<string>();
public string ConfPath { get => Player.ConfigFolder + "mpvnet.conf"; }
public string ProcessInstance { get; set; } = "single";
public string DarkMode { get; set; } = "always";
public string DarkTheme { get; set; } = "dark";
public string LightTheme { get; set; } = "light";
public string StartSize { get; set; } = "height-session";
public bool AutoLoadFolder { get; set; } = true;
public bool DebugMode { get; set; }
public bool Exit { get; set; }
public bool IsTerminalAttached { get; } = Environment.GetEnvironmentVariable("_started_from_console") == "yes";
public bool MediaInfo { get; set; } = true;
public bool Queue { get; set; }
public bool RememberVolume { get; set; } = true;
public bool RememberWindowPosition { get; set; }
public int StartThreshold { get; set; } = 1500;
public int RecentCount { get; set; } = 15;
public float AutofitAudio { get; set; } = 0.7f;
public float AutofitImage { get; set; } = 0.8f;
public float MinimumAspectRatio { get; set; }
public float MinimumAspectRatioAudio { get; set; }
readonly ExtensionLoader _extensionManager = new ExtensionLoader();
AppSettings? _settings;
public AppClass()
{
_extensionManager.UnhandledException += ex => Terminal.WriteError(ex);
StrongReferenceMessenger.Default.Register<MainWindowIsLoadedMessage>(this, (r, msg) =>
{
TaskHelp.Run(() => _extensionManager.LoadFolder(Player.ConfigFolder + "extensions"));
});
}
public AppSettings Settings => _settings ??= SettingsManager.Load();
public void Init()
{
var useless1 = Player.ConfigFolder;
var useless2 = Player.Conf;
foreach (var i in Conf)
ProcessProperty(i.Key, i.Value, true);
if (DebugMode)
{
string filePath = Player.ConfigFolder + "MpvNet-debug.log";
if (File.Exists(filePath))
File.Delete(filePath);
Trace.Listeners.Add(new TextWriterTraceListener(filePath));
Trace.AutoFlush = true;
}
Player.Shutdown += Player_Shutdown;
Player.Initialized += Player_Initialized;
}
public static string About => "Copyright (C) 2000-2023 mpv.net/mpv/mplayer\n" +
$"{AppInfo.Product} {AppInfo.Version}" + GetLastWriteTime(Environment.ProcessPath!) + "\n" +
$"{Player.GetPropertyString("mpv-version")}" + GetLastWriteTime(Folder.Startup + "libmpv-2.dll") + "\n" +
$"ffmpeg {Player.GetPropertyString("ffmpeg-version")}\n" + "\nGPL v2 License";
static string GetLastWriteTime(string path)
{
if (IsStoreVersion)
return "";
return $" ({File.GetLastWriteTime(path).ToShortDateString()})";
}
static bool IsStoreVersion => Folder.Startup.Contains("FrankSkare.mpvnet");
void Player_Initialized()
{
if (RememberVolume)
{
Player.SetPropertyInt("volume", Settings.Volume);
Player.SetPropertyString("mute", Settings.Mute);
}
}
void Player_Shutdown()
{
Settings.Volume = Player.GetPropertyInt("volume");
Settings.Mute = Player.GetPropertyString("mute");
SettingsManager.Save(Settings);
foreach (string file in TempFiles)
FileHelp.Delete(file);
}
Dictionary<string, string>? _Conf;
public Dictionary<string, string> Conf {
get {
if (_Conf == null)
{
_Conf = new Dictionary<string, string>();
if (File.Exists(ConfPath))
foreach (string i in File.ReadAllLines(ConfPath))
if (i.Contains('=') && !i.StartsWith("#"))
_Conf[i[..i.IndexOf("=")].Trim()] = i[(i.IndexOf("=") + 1)..].Trim();
}
return _Conf;
}
}
public bool ProcessProperty(string name, string value, bool writeError = false)
{
switch (name)
{
case "audio-file-extensions": FileTypes.Audio = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
case "auto-load-folder": AutoLoadFolder = value == "yes"; return true;
case "autofit-audio": AutofitAudio = value.Trim('%').ToInt(70) / 100f; return true;
case "autofit-image": AutofitImage = value.Trim('%').ToInt(80) / 100f; return true;
case "dark-mode": DarkMode = value; return true;
case "dark-theme": DarkTheme = value.Trim('\'', '"'); return true;
case "debug-mode": DebugMode = value == "yes"; return true;
case "image-file-extensions": FileTypes.Image = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
case "light-theme": LightTheme = value.Trim('\'', '"'); return true;
case "media-info": MediaInfo = value == "yes"; return true;
case "minimum-aspect-ratio-audio": MinimumAspectRatioAudio = value.ToFloat(); return true;
case "minimum-aspect-ratio": MinimumAspectRatio = value.ToFloat(); return true;
case "process-instance": ProcessInstance = value; return true;
case "queue": Queue = value == "yes"; return true;
case "recent-count": RecentCount = value.ToInt(15); return true;
case "remember-volume": RememberVolume = value == "yes"; return true;
case "remember-window-position": RememberWindowPosition = value == "yes"; return true;
case "start-size": StartSize = value; return true;
case "start-threshold": StartThreshold = value.ToInt(1500); return true;
case "video-file-extensions": FileTypes.Video = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
default:
if (writeError)
Terminal.WriteError($"unknown MpvNet.conf property: {name}");
return false;
}
}
public static (string Title, string Path) GetTitleAndPath(string input)
{
if (input.Contains('|'))
{
var a = input.Split('|');
return (a[1], a[0]);
}
return (input, input);
}
InputConf? _inputConf;
public InputConf InputConf => _inputConf ??= new InputConf(Player.ConfigFolder + "input.conf");
public void ApplyShowMenuFix()
{
if (Settings.ShowMenuFixApplied)
return;
if (File.Exists(InputConf.Path))
{
string content = File.ReadAllText(InputConf.Path);
if (!content.Contains("script-message mpvnet show-menu") &&
!content.Contains("script-message-to mpvnet show-menu"))
File.WriteAllText(InputConf.Path, BR + content.Trim() + BR +
"MBTN_Right script-message-to mpvnet show-menu" + BR);
}
Settings.ShowMenuFixApplied = true;
}
public void ApplyInputDefaultBindingsFix()
{
if (Settings.InputDefaultBindingsFixApplied)
return;
if (File.Exists(Player.ConfPath))
{
string content = File.ReadAllText(Player.ConfPath);
if (content.Contains("input-default-bindings = no"))
File.WriteAllText(ConfPath, content.Replace("input-default-bindings = no", ""));
if (content.Contains("input-default-bindings=no"))
File.WriteAllText(ConfPath, content.Replace("input-default-bindings=no", ""));
}
Settings.InputDefaultBindingsFixApplied = true;
}
}

11
src/MpvNet/AppInfo.cs Normal file
View File

@@ -0,0 +1,11 @@

using System.Reflection;
namespace MpvNet;
public static class AppInfo
{
public static string Product => GetAssemblyAttribute<AssemblyProductAttribute>().Product;
public static Version Version => Assembly.GetEntryAssembly()!.GetName().Version!;
static T GetAssemblyAttribute<T>() => (T)(object)Assembly.GetEntryAssembly()!.GetCustomAttributes(typeof(T)).First();
}

44
src/MpvNet/Binding.cs Normal file
View File

@@ -0,0 +1,44 @@

using CommunityToolkit.Mvvm.ComponentModel;
namespace MpvNet;
public class Binding : ObservableObject
{
public string Path { get; set; }
public string Command { get; set; }
public string Comment { get; set; }
public bool IsMenu { get; set; }
public Binding()
{
Path = ""; Command = ""; Comment = "";
}
public Binding(string folder = "",
string name = "",
string command = "",
string input = "",
string comment = "")
{
if (folder != "" && name != "")
Path = folder + " > " + name;
else if (name != "")
Path = name;
else
Path = "";
Command = command;
Input = input;
Comment = comment == "" ? Path : comment;
}
string _input = "";
public string Input
{
get => _input;
set => SetProperty(ref _input, value);
}
}

28
src/MpvNet/Chapter.cs Normal file
View File

@@ -0,0 +1,28 @@

using MpvNet.ExtensionMethod;
namespace MpvNet;
public class Chapter
{
public string Title { get; set; } = "";
public double Time { get; set; }
string? _timeDisplay;
public string TimeDisplay
{
get
{
if (_timeDisplay == null)
{
_timeDisplay = TimeSpan.FromSeconds(Time).ToString();
if (_timeDisplay.ContainsEx("."))
_timeDisplay = _timeDisplay[.._timeDisplay.LastIndexOf(".")];
}
return _timeDisplay;
}
}
}

178
src/MpvNet/Command.cs Normal file
View File

@@ -0,0 +1,178 @@

using System.Globalization;
using MpvNet.Help;
namespace MpvNet;
public class Command
{
Dictionary<string, Action<IList<string>>>? _commands;
public static Command Current { get; } = new();
public Dictionary<string, Action<IList<string>>> Commands => _commands ??= new()
{
["open-conf-folder"] = args => ProcessHelp.ShellExecute(Player.ConfigFolder),
["play-pause"] = PlayPause,
["shell-execute"] = args => ProcessHelp.ShellExecute(args[0]),
["show-text"] = args => ShowText(args[0], Convert.ToInt32(args[1]), Convert.ToInt32(args[2])),
// backward compatibility
["playlist-add"] = args => PlaylistAdd(Convert.ToInt32(args[0])), // backward compatibility
["show-progress"] = args => ShowProgress(), // backward compatibility
["cycle-audio"] = args => CycleAudio(), // backward compatibility
["cycle-subtitles"] = args => CycleSubtitles(), // backward compatibility
["playlist-first"] = args => PlaylistFirst(), // backward compatibility
["playlist-last"] = args => PlaylistLast(), // backward compatibility
};
public string FormatTime(double value) => ((int)value).ToString("00");
public static void PlayPause(IList<string> args)
{
int count = Player.GetPropertyInt("playlist-count");
if (count > 0)
Player.Command("cycle pause");
else if (App.Settings.RecentFiles.Count > 0)
{
foreach (string i in App.Settings.RecentFiles)
{
if (i.Contains("://") || File.Exists(i))
{
Player.LoadFiles(new[] { i }, true, false);
break;
}
}
}
}
public static void ShowText(string text, int duration = 0, int fontSize = 0)
{
if (string.IsNullOrEmpty(text))
return;
if (duration == 0)
duration = Player.GetPropertyInt("osd-duration");
if (fontSize == 0)
fontSize = Player.GetPropertyInt("osd-font-size");
Player.Command("show-text \"${osd-ass-cc/0}{\\\\fs" + fontSize +
"}${osd-ass-cc/1}" + text + "\" " + duration);
}
// backward compatibility
public static void PlaylistAdd(int value)
{
int pos = Player.PlaylistPos;
int count = Player.GetPropertyInt("playlist-count");
if (count < 2)
return;
pos += value;
if (pos < 0)
pos = count - 1;
if (pos > count - 1)
pos = 0;
Player.SetPropertyInt("playlist-pos", pos);
}
// backward compatibility
public void ShowProgress()
{
TimeSpan position = TimeSpan.FromSeconds(Player.GetPropertyDouble("time-pos"));
TimeSpan duration = TimeSpan.FromSeconds(Player.GetPropertyDouble("duration"));
string text = FormatTime(position.TotalMinutes) + ":" +
FormatTime(position.Seconds) + " / " +
FormatTime(duration.TotalMinutes) + ":" +
FormatTime(duration.Seconds) + " " +
DateTime.Now.ToString("H:mm dddd d MMMM", CultureInfo.InvariantCulture);
Player.CommandV("show-text", text, "5000");
}
// backward compatibility
public static void CycleAudio()
{
Player.UpdateExternalTracks();
lock (Player.MediaTracksLock)
{
MediaTrack[] tracks = Player.MediaTracks.Where(track => track.Type == "a").ToArray();
if (tracks.Length < 1)
{
Player.CommandV("show-text", "No audio tracks");
return;
}
int aid = Player.GetPropertyInt("aid");
if (tracks.Length > 1)
{
if (++aid > tracks.Length)
aid = 1;
Player.SetPropertyInt("aid", aid);
}
Player.CommandV("show-text", aid + "/" + tracks.Length + ": " + tracks[aid - 1].Text[3..], "5000");
}
}
// backward compatibility
public static void CycleSubtitles()
{
Player.UpdateExternalTracks();
lock (Player.MediaTracksLock)
{
MediaTrack[] tracks = Player.MediaTracks.Where(track => track.Type == "s").ToArray();
if (tracks.Length < 1)
{
Player.CommandV("show-text", "No subtitles");
return;
}
int sid = Player.GetPropertyInt("sid");
if (tracks.Length > 1)
{
if (++sid > tracks.Length)
sid = 0;
Player.SetPropertyInt("sid", sid);
}
if (sid == 0)
Player.CommandV("show-text", "No subtitle");
else
Player.CommandV("show-text", sid + "/" + tracks.Length + ": " + tracks[sid - 1].Text[3..], "5000");
}
}
// backward compatibility
public static void PlaylistFirst()
{
if (Player.PlaylistPos != 0)
Player.SetPropertyInt("playlist-pos", 0);
}
// backward compatibility
public static void PlaylistLast()
{
int count = Player.GetPropertyInt("playlist-count");
if (Player.PlaylistPos < count - 1)
Player.SetPropertyInt("playlist-pos", count - 1);
}
}

View File

@@ -0,0 +1,41 @@

using System.Reflection;
using MpvNet.ExtensionMethod;
namespace MpvNet;
public class ExtensionLoader
{
public event Action<Exception>? UnhandledException;
readonly List<object?> _refs = new();
void LoadDll(string path)
{
if (!File.Exists(path))
return;
try
{
Assembly asm = Assembly.LoadFile(path);
var type = asm.GetTypes().Where(typeof(IExtension).IsAssignableFrom).First();
_refs.Add(Activator.CreateInstance(type));
}
catch (Exception ex)
{
UnhandledException?.Invoke(ex);
}
}
public void LoadFolder(string path)
{
if (Directory.Exists(path))
foreach (string dir in Directory.GetDirectories(path))
LoadDll(dir.AddSep() + Path.GetFileName(dir) + ".dll");
}
}
public interface IExtension
{
}

View File

@@ -0,0 +1,7 @@

namespace MpvNet.ExtensionMethod;
public static class ObjectExtension
{
public static string ToStringEx(this object instance) => instance?.ToString() ?? "";
}

View File

@@ -0,0 +1,67 @@

namespace MpvNet.ExtensionMethod;
public static class PathStringExtension
{
public static string Ext(this string filepath) => filepath.Ext(false);
public static string Ext(this string filepath, bool includeDot)
{
if (string.IsNullOrEmpty(filepath))
return "";
char[] chars = filepath.ToCharArray();
for (int x = filepath.Length - 1; x >= 0; x--)
{
if (chars[x] == Path.DirectorySeparatorChar)
return "";
if (chars[x] == '.')
return filepath.Substring(x + (includeDot ? 0 : 1)).ToLowerInvariant();
}
return "";
}
public static string FileName(this string instance)
{
if (string.IsNullOrEmpty(instance))
return "";
int index = instance.LastIndexOf('\\');
if (index > -1)
return instance.Substring(index + 1);
index = instance.LastIndexOf('/');
if (index > -1)
return instance.Substring(index + 1);
return instance;
}
public static string ShortPath(this string instance, int maxLength)
{
if (string.IsNullOrEmpty(instance))
return "";
if (instance.Length > maxLength && instance.Substring(1, 2) == ":\\")
instance = instance[..3] + "...\\" + instance.FileName();
return instance;
}
// Ensure trailing directory separator char
public static string AddSep(this string instance)
{
if (string.IsNullOrEmpty(instance))
return "";
if (!instance.EndsWith(Path.DirectorySeparatorChar.ToString()))
instance = instance + Path.DirectorySeparatorChar;
return instance;
}
}

View File

@@ -0,0 +1,30 @@

using System.Globalization;
namespace MpvNet.ExtensionMethod;
public static class StringExtension
{
public static string ToUpperEx(this string instance) => (instance != null) ? instance.ToUpperInvariant() : "";
public static string ToLowerEx(this string instance) => (instance != null) ? instance.ToLowerInvariant() : "";
public static string TrimEx(this string? instance) => (instance == null) ? "" : instance.Trim();
public static int ToInt(this string instance, int defaultValue = 0)
{
if (int.TryParse(instance, out int result))
return result;
return defaultValue;
}
public static float ToFloat(this string instance) =>
float.Parse(instance.Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture);
public static bool ContainsEx(this string? instance, string? value) =>
!string.IsNullOrEmpty(instance) && !string.IsNullOrEmpty(value) && instance.Contains(value);
public static bool StartsWithEx(this string? instance, string? value) =>
(instance != null && value != null) && instance.StartsWith(value);
}

20
src/MpvNet/FileTypes.cs Normal file
View File

@@ -0,0 +1,20 @@

using MpvNet.ExtensionMethod;
namespace MpvNet;
public static class FileTypes
{
public static string[] Video { get; set; } = "mkv mp4 avi mov flv mpg webm wmv ts vob 264 265 asf avc avs dav h264 h265 hevc m2t m2ts m2v m4v mpeg mpv mts vpy y4m".Split(' ');
public static string[] Audio { get; set; } = "mp3 flac m4a mka mp2 ogg opus aac ac3 dts dtshd dtshr dtsma eac3 mpa mpc thd w64 wav".Split(' ');
public static string[] Image { get; set; } = { "jpg", "bmp", "png", "gif", "webp" };
public static string[] Subtitle { get; } = { "srt", "ass", "idx", "sub", "sup", "ttxt", "txt", "ssa", "smi", "mks" };
public static bool IsImage(string extension) => Image.Contains(extension);
public static bool IsAudio(string extension) => Audio.Contains(extension);
public static bool IsMedia(string extension) =>
Video.Contains(extension) || Audio.Contains(extension) || Image.Contains(extension);
public static IEnumerable<string> GetMediaFiles(IEnumerable<string> files) => files.Where(i => IsMedia(i.Ext()));
}

10
src/MpvNet/Folder.cs Normal file
View File

@@ -0,0 +1,10 @@

using MpvNet.ExtensionMethod;
namespace MpvNet;
public class Folder
{
public static string Startup { get; } = Path.GetDirectoryName(Environment.ProcessPath)!.AddSep();
public static string AppData { get; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).AddSep();
}

11
src/MpvNet/Global.cs Normal file
View File

@@ -0,0 +1,11 @@

namespace MpvNet;
public static class Global
{
public static readonly string BR = Environment.NewLine;
public static readonly string BR2 = Environment.NewLine + Environment.NewLine;
public static readonly MainPlayer Player = new MainPlayer();
public static readonly MainPlayer Core = Player; // backward compatibility
public static readonly AppClass App = new AppClass();
}

View File

@@ -0,0 +1,8 @@
global using System;
global using System.Collections.Generic;
global using System.Diagnostics;
global using System.IO;
global using System.Linq;
global using static MpvNet.Global;

View File

@@ -0,0 +1,20 @@

namespace MpvNet.Help;
public static class FileHelp
{
public static void Delete(string path)
{
try
{
if (File.Exists(path))
File.Delete(path);
}
catch (Exception ex)
{
Terminal.WriteError("Failed to delete file:" + BR + path + BR + ex.Message);
}
}
public static string ReadTextFile(string path) => File.Exists(path) ? File.ReadAllText(path) : "";
}

View File

@@ -0,0 +1,26 @@

namespace MpvNet.Help;
public class MpvHelp
{
public static string? WM_APPCOMMAND_to_mpv_key(int value) => value switch
{
5 => "SEARCH", // BROWSER_SEARCH
6 => "FAVORITES", // BROWSER_FAVORITES
7 => "HOMEPAGE", // BROWSER_HOME
15 => "MAIL", // LAUNCH_MAIL
33 => "PRINT", // PRINT
11 => "NEXT", // MEDIA_NEXTTRACK
12 => "PREV", // MEDIA_PREVIOUSTRACK
13 => "STOP", // MEDIA_STOP
14 => "PLAYPAUSE", // MEDIA_PLAY_PAUSE
46 => "PLAY", // MEDIA_PLAY
47 => "PAUSE", // MEDIA_PAUSE
48 => "RECORD", // MEDIA_RECORD
49 => "FORWARD", // MEDIA_FAST_FORWARD
50 => "REWIND", // MEDIA_REWIND
51 => "CHANNEL_UP", // MEDIA_CHANNEL_UP
52 => "CHANNEL_DOWN", // MEDIA_CHANNEL_DOWN
_ => null,
};
}

View File

@@ -0,0 +1,15 @@
namespace MpvNet.Help;
public static class ProcessHelp
{
public static void Execute(string file, string arguments = "", bool shellExecute = false)
{
using Process proc = new Process();
proc.StartInfo.FileName = file;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.UseShellExecute = shellExecute;
proc.Start();
}
public static void ShellExecute(string file, string arguments = "") => Execute(file, arguments, true);
}

View File

@@ -0,0 +1,16 @@

using System.Security.Cryptography;
using System.Text;
namespace MpvNet.Help;
public static class StringHelp
{
public static string GetMD5Hash(string txt)
{
using MD5 md5 = MD5.Create();
byte[] inputBuffer = Encoding.UTF8.GetBytes(txt);
byte[] hashBuffer = md5.ComputeHash(inputBuffer);
return BitConverter.ToString(md5.ComputeHash(inputBuffer)).Replace("-", "");
}
}

View File

@@ -0,0 +1,21 @@

using System.Threading.Tasks;
namespace MpvNet.Help;
public class TaskHelp
{
public static void Run(Action action)
{
Task.Run(() => {
try
{
action.Invoke();
}
catch (Exception e)
{
Terminal.WriteError(e);
}
});
}
}

74
src/MpvNet/InputConf.cs Normal file
View File

@@ -0,0 +1,74 @@

using MpvNet.Help;
namespace MpvNet;
public class InputConf
{
string? _content;
bool? _hasMenu;
public InputConf(string path) { Path = path; }
public string Path { get; }
public string Content
{
get => _content ??= FileHelp.ReadTextFile(Path);
set => _content = value;
}
public bool HasMenu => _hasMenu ??= Content.Contains("#menu:");
public List<Binding> GetMenuBindings()
{
var confbindings = InputHelp.Parse(Content);
if (HasMenu)
return confbindings;
var defaultBindings = InputHelp.GetDefaults();
foreach (Binding defaultBinding in defaultBindings)
foreach (Binding confBinding in confbindings)
if (defaultBinding.Input == confBinding.Input &&
defaultBinding.Command != confBinding.Command)
{
defaultBinding.Input = "";
}
foreach (Binding defaultBinding in defaultBindings)
foreach (Binding confBinding in confbindings)
if (defaultBinding.Command == confBinding.Command)
defaultBinding.Input = confBinding.Input;
return defaultBindings;
}
public string GetContent()
{
if (HasMenu)
return Content;
else
{
var defaults = InputHelp.GetDefaults();
var removed = new List<Binding>();
var conf = InputHelp.Parse(Content);
foreach (Binding defaultBinding in defaults)
foreach (Binding confBinding in conf)
if (defaultBinding.Command == confBinding.Command &&
defaultBinding.Comment == confBinding.Comment)
{
defaultBinding.Input = confBinding.Input;
removed.Add(confBinding);
}
foreach (Binding binding in removed)
conf.Remove(binding);
defaults.AddRange(conf);
return InputHelp.ConvertToString(defaults);
}
}
}

376
src/MpvNet/InputHelp.cs Normal file
View File

@@ -0,0 +1,376 @@

using System.Text;
namespace MpvNet;
public static class InputHelp
{
public static List<Binding> GetDefaults()
{
List<Binding> bindings = new List<Binding>()
{
new (_("File"), _("Open Files..."), "script-message-to mpvnet open-files", "o"),
new (_("File"), _("Open URL or file from clipboard"), "script-message-to mpvnet open-clipboard", "V"),
new (_("File"), _("Open DVD/Blu-ray Drive/Folder..."), "script-message-to mpvnet open-optical-media"),
new (_("File"), "-"),
new (_("File"), _("Load external audio files..."), "script-message-to mpvnet load-audio", "Alt+a"),
new (_("File"), _("Load external subtitle files..."), "script-message-to mpvnet load-sub", "Alt+s"),
new (_("File"), "-"),
new (_("File"), _("Add files to playlist..."), "script-message-to mpvnet open-files append"),
new (_("File"), "-"),
new (_("File"), _("Recent")),
new (_("Playback"), _("Play/Pause"), "script-message-to mpvnet play-pause", "Space"),
new (_("Playback"), _("Stop"), "stop", "Ctrl+s"),
new (_("Navigate"), _("Previous File"), "playlist-prev", "F11"),
new (_("Navigate"), _("Next File"), "playlist-next", "F12"),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Next Chapter"), "add chapter 1", "PGUP"),
new (_("Navigate"), _("Previous Chapter"), "add chapter -1", "PGDWN"),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Jump To Next Frame"), "frame-step", "."),
new (_("Navigate"), _("Jump To Previous Frame"), "frame-back-step", ","),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Jump 5 sec forward"), "seek 5", "Right"),
new (_("Navigate"), _("Jump 5 sec backward"), "seek -5", "Left"),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Jump 30 sec forward"), "seek 30", "Up"),
new (_("Navigate"), _("Jump 30 sec backward"), "seek -30", "Down"),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Jump 5 min forward"), "seek 300", "Ctrl+Right"),
new (_("Navigate"), _("Jump 5 min backward"), "seek -300", "Ctrl+Left"),
new (_("Navigate"), "-"),
new (_("Navigate"), _("Titles")),
new (_("Navigate"), _("Chapters")),
new (_("Pan & Scan"), _("Decrease Size"), "add video-zoom -0.1", "Ctrl+-"),
new (_("Pan & Scan"), _("Increase Size"), "add video-zoom 0.1", "Ctrl++"),
new (_("Pan & Scan"), "-"),
new (_("Pan & Scan"), _("Move Left"), "add video-pan-x -0.01", "Ctrl+KP4"),
new (_("Pan & Scan"), _("Move Right"), "add video-pan-x 0.01", "Ctrl+KP6"),
new (_("Pan & Scan"), "-"),
new (_("Pan & Scan"), _("Move Up"), "add video-pan-y -0.01", "Ctrl+KP8"),
new (_("Pan & Scan"), _("Move Down"), "add video-pan-y 0.01", "Ctrl+KP2"),
new (_("Pan & Scan"), "-"),
new (_("Pan & Scan"), _("Decrease Height"), "add panscan -0.1", "w"),
new (_("Pan & Scan"), _("Increase Height"), "add panscan 0.1", "W"),
new (_("Pan & Scan"), "-"),
new (_("Pan & Scan"), _("Reset"), "set video-zoom 0; set video-pan-x 0; set video-pan-y 0", "Ctrl+BS"),
new (_("Video"), _("Decrease Contrast"), "add contrast -1", "Ctrl+1"),
new (_("Video"), _("Increase Contrast"), "add contrast 1", "Ctrl+2"),
new (_("Video"), "-"),
new (_("Video"), _("Decrease Brightness"), "add brightness -1", "Ctrl+3"),
new (_("Video"), _("Increase Brightness"), "add brightness 1", "Ctrl+4"),
new (_("Video"), "-"),
new (_("Video"), _("Decrease Gamma"), "add gamma -1", "Ctrl+5"),
new (_("Video"), _("Increase Gamma"), "add gamma 1", "Ctrl+6"),
new (_("Video"), "-"),
new (_("Video"), _("Decrease Saturation"), "add saturation -1", "Ctrl+7"),
new (_("Video"), _("Increase Saturation"), "add saturation 1", "Ctrl+8"),
new (_("Video"), "-"),
new (_("Video"), _("Take Screenshot"), "async screenshot", "s"),
new (_("Video"), _("Take Screenshot without subtitles"), "async screenshot video", "S"),
new (_("Video"), _("Toggle Deinterlace"), "cycle deinterlace", "d"),
new (_("Video"), _("Cycle Aspect Ratio"), "cycle-values video-aspect 16:9 4:3 2.35:1 -1", "a"),
new (_("Video"), _("Rotate Video"), "cycle-values video-rotate 90 180 270 0", "Ctrl+r"),
new (_("Audio"), _("Cycle/Next"), "cycle audio", "KP7"),
new (_("Audio"), "-"),
new (_("Audio"), _("Delay +0.1"), "add audio-delay 0.1", "Ctrl+d"),
new (_("Audio"), _("Delay -0.1"), "add audio-delay -0.1", "Ctrl+D"),
new (_("Subtitle"), _("Cycle/Next"), "cycle sub", "KP8"),
new (_("Subtitle"), _("Toggle Visibility"), "cycle sub-visibility", "v"),
new (_("Subtitle"), "-"),
new (_("Subtitle"), _("Delay -0.1"), "add sub-delay -0.1", "z"),
new (_("Subtitle"), _("Delay +0.1"), "add sub-delay 0.1", "Z"),
new (_("Subtitle"), "-"),
new (_("Subtitle"), _("Move Up"), "add sub-pos -1", "r"),
new (_("Subtitle"), _("Move Down"), "add sub-pos 1", "R"),
new (_("Subtitle"), "-"),
new (_("Subtitle"), _("Decrease Subtitle Font Size"), "add sub-scale -0.1", "F"),
new (_("Subtitle"), _("Increase Subtitle Font Size"), "add sub-scale 0.1", "G"),
new (_("Subtitle"), "-"),
new (_("Subtitle > Advanced"), _("Toggle overriding SSA/ASS styles with normal styles"), "cycle-values sub-ass-override force no", "u"),
new ("", _("Track")),
new (_("Volume"), _("Up"), "add volume 2", "+"),
new (_("Volume"), _("Down"), "add volume -2", "-"),
new (_("Volume"), "-"),
new (_("Volume"), _("Mute"), "cycle mute", "m"),
new (_("Speed"), _("-10%"), "multiply speed 1/1.1", "["),
new (_("Speed"), _("+10%"), "multiply speed 1.1", "]"),
new (_("Speed"), "-"),
new (_("Speed"), _("Half"), "multiply speed 0.5", "{"),
new (_("Speed"), _("Double"), "multiply speed 2.0", "}"),
new (_("Speed"), "-"),
new (_("Speed"), _("Reset"), "set speed 1", "BS"),
new (_("View"), _("Fullscreen"), "cycle fullscreen", "Enter"),
new (_("View > Zoom"), _("Enlarge"), "script-message-to mpvnet scale-window 1.2", "Alt++"),
new (_("View > Zoom"), _("Shrink"), "script-message-to mpvnet scale-window 0.8", "Alt+-"),
new (_("View > Zoom"), "-"),
new (_("View > Zoom"), _("50 %"), "script-message-to mpvnet window-scale 0.5", "Alt+0"),
new (_("View > Zoom"), _("100 %"), "script-message-to mpvnet window-scale 1.0", "Alt+1"),
new (_("View > Zoom"), _("200 %"), "script-message-to mpvnet window-scale 2.0", "Alt+2"),
new (_("View > Zoom"), _("300 %"), "script-message-to mpvnet window-scale 3.0", "Alt+3"),
new (_("View > Move"), _("Left"), "script-message-to mpvnet move-window left", "Alt+Left"),
new (_("View > Move"), _("Right"), "script-message-to mpvnet move-window right", "Alt+Right"),
new (_("View > Move"), _("Top"), "script-message-to mpvnet move-window top", "Alt+Up"),
new (_("View > Move"), _("Bottom"), "script-message-to mpvnet move-window bottom", "Alt+Down"),
new (_("View > Move"), _("Center"), "script-message-to mpvnet move-window center", "Alt+BS"),
new (_("View"), _("Show Profiles"), "script-message-to mpvnet show-profiles", "Ctrl+P"),
new (_("View"), _("Toggle Border"), "cycle border", "b"),
new (_("View"), _("Toggle On Top"), "cycle ontop", "Ctrl+t"),
new (_("View"), _("Toggle Statistics"), "script-binding stats/display-stats-toggle", "t"),
new (_("View"), _("Toggle OSC Visibility"), "script-binding osc/visibility", "Del"),
new (_("View"), _("Show Media Info On-Screen"), "script-message-to mpvnet show-media-info osd", "i"),
new (_("View"), _("Show Media Info Message Box"), "script-message-to mpvnet show-media-info msgbox", "Ctrl+m"),
new (_("View"), _("Show Progress"), "show-progress", "p"),
new (_("View > Advanced"), _("Show Console"), "script-binding console/enable", "`"),
new (_("View > Advanced"), _("Show Audio Devices"), "script-message-to mpvnet show-audio-devices"),
new (_("View > Advanced"), _("Show Commands"), "script-message-to mpvnet show-commands", "C"),
new (_("View > Advanced"), _("Show Demuxers"), "script-message-to mpvnet show-demuxers"),
new (_("View > Advanced"), _("Show Decoders"), "script-message-to mpvnet show-decoders"),
new ("", _("Profile")),
new (_("Settings"), _("Show Config Editor"), "script-message-to mpvnet show-conf-editor", "Ctrl+,"),
new (_("Settings"), _("Show Input Editor"), "script-message-to mpvnet show-input-editor", "Ctrl+i"),
new (_("Settings"), _("Open Config Folder"), "script-message-to mpvnet open-conf-folder", "Ctrl+f"),
new (_("Settings > Setup"), _("Register video file associations"), "script-message-to mpvnet reg-file-assoc video"),
new (_("Settings > Setup"), _("Register audio file associations"), "script-message-to mpvnet reg-file-assoc audio"),
new (_("Settings > Setup"), _("Register image file associations"), "script-message-to mpvnet reg-file-assoc image"),
new (_("Settings > Setup"), _("Unregister file associations"), "script-message-to mpvnet reg-file-assoc unreg"),
new (_("Tools"), _("Set/clear A-B loop points"), "ab-loop", "l"),
new (_("Tools"), _("Toggle infinite file looping"), "cycle-values loop-file inf no", "L"),
new (_("Tools"), _("Shuffle Playlist"), "playlist-shuffle"),
new (_("Tools"), _("Toggle Hardware Decoding"), "cycle-values hwdec auto no", "Ctrl+h"),
new (_("Tools"), _("Exit"), "quit", "Esc"),
new (_("Tools"), _("Exit Watch Later"), "quit-watch-later", "Q"),
new (_("Tools"), _("Show current file in File Explorer"), @"run powershell -command ""explorer.exe '/select,' ( \""${path}\"" -replace '/', '\\' )""", "e"),
new (_("Help"), _("Website mpv"), "script-message-to mpvnet shell-execute https://mpv.io"),
new (_("Help"), _("Website mpv.net"), "script-message-to mpvnet shell-execute https://github.com/mpvnet-player/mpv.net"),
new (_("Help"), "-"),
new (_("Help"), _("Manual mpv"), "script-message-to mpvnet shell-execute https://mpv.io/manual/stable"),
new (_("Help"), _("Manual mpv.net"), "script-message-to mpvnet shell-execute https://github.com/mpvnet-player/mpvnet/blob/master/docs/manual.md"),
new (_("Help"), "-"),
new (_("Help"), _("About mpv.net"), "script-message-to mpvnet show-about"),
new ("", _("Exit"), "quit", "Esc"),
new ("", "", "quit", "q", _("Exit")),
new ("", "", "script-message-to mpvnet show-menu", "MBTN_Right", _("Show Menu")),
new ("", "", "quit", "Power", _("Exit")),
new ("", "", "cycle pause", "Play", _("Play/Pause")),
new ("", "", "cycle pause", "Pause", _("Play/Pause")),
new ("", "", "cycle pause", "PlayPause", _("Play/Pause")),
new ("", "", "cycle pause", "MBTN_Mid", _("Play/Pause")),
new ("", "", "stop", "Stop", _("Stop")),
new ("", "", "seek 60", "Forward", _("Forward")),
new ("", "", "seek -60", "Rewind", _("Backward")),
new ("", "", "add volume 2", "Wheel_Up", _("Volume Up")),
new ("", "", "add volume -2", "Wheel_Down", _("Volume Down")),
new ("", "", "add volume 2", "Wheel_Right", _("Volume Up")),
new ("", "", "add volume -2", "Wheel_Left", _("Volume Down")),
new ("", "", "playlist-prev", "Prev", _("Previous File")),
new ("", "", "playlist-next", "Next", _("Next File")),
new ("", "", "playlist-prev", "MBTN_Back", _("Previous File")),
new ("", "", "playlist-next", "MBTN_Forward", _("Next File")),
new ("", "", "playlist-prev", "<", _("Previous File")),
new ("", "", "playlist-next", ">", _("Next File")),
new ("", "", "ignore", "MBTN_Left", _("Ignore left mouse butten")),
new ("", "", "cycle fullscreen", "f", _("Fullscreen")),
new ("", "", "cycle fullscreen", "MBTN_Left_DBL", _("Fullscreen")),
new ("", "", "cycle fullscreen", "KP_Enter", _("Fullscreen")),
new ("", "", "no-osd seek 1 exact", "Shift+Right", _("Seek Forward")),
new ("", "", "no-osd seek -1 exact", "Shift+Left", _("Seek Backward")),
new ("", "", "no-osd seek 5 exact", "Shift+Up", _("Seek Forward")),
new ("", "", "no-osd seek -5 exact", "Shift+Down", _("Seek Backward")),
new ("", "", "revert-seek", "Shift+BS", _("Undo previous (or marked) seek")),
new ("", "", "revert-seek mark", "Shift+Ctrl+BS", _("Mark position for revert-seek")),
new ("", "", "no-osd sub-seek -1", "Ctrl+Shift+Left", _("Seek to previous subtitle")),
new ("", "", "no-osd sub-seek 1", "Ctrl+Shift+Right", _("Seek to next subtitle")),
new ("", "", "no-osd seek 5", "Ctrl+Wheel_Up", _("Seek Forward")),
new ("", "", "no-osd seek -5", "Ctrl+Wheel_Down", _("Seek Backward")),
//new (_("Command Palette"), _("Commands"), "script-message-to mpvnet show-command-palette", "F1"),
};
return bindings;
}
public static string ConvertToString(List<Binding> bindings)
{
StringBuilder sb = new StringBuilder();
foreach (Binding binding in bindings)
{
string cmd = binding.Command.Trim();
string input = binding.Input.Trim();
string comment = binding.IsMenu ? "menu: " + binding.Path : binding.Comment.Trim();
input = input == "" ? "_" : input;
string line = input.PadRight(10) + " ";
line += cmd == "" ? "ignore" : cmd;
if (comment != "")
line = line.PadRight(40) + " # " + comment;
sb.AppendLine(line);
}
return sb.ToString();
}
public static List<Binding> Parse(string content)
{
var bindings = new List<Binding>();
if (string.IsNullOrEmpty(content))
return bindings;
foreach (string it in content.Split('\r', '\n'))
{
string line = it.Trim();
if (line.StartsWith("#") || !line.Contains(' '))
continue;
Binding binding = new Binding();
binding.Input = line[..line.IndexOf(" ")];
if (binding.Input == "_")
binding.Input = "";
if (binding.Input.Contains("CTRL+"))
binding.Input = binding.Input.Replace("CTRL+", "Ctrl+");
if (binding.Input.Contains("ctrl+"))
binding.Input = binding.Input.Replace("ctrl+", "Ctrl+");
if (binding.Input.Contains("SHIFT+"))
binding.Input = binding.Input.Replace("SHIFT+", "Shift+");
if (binding.Input.Contains("shift+"))
binding.Input = binding.Input.Replace("shift+", "Shift+");
if (binding.Input.Contains("ALT+"))
binding.Input = binding.Input.Replace("ALT+", "Alt+");
if (binding.Input.Contains("alt+"))
binding.Input = binding.Input.Replace("alt+", "Alt+");
line = line[(line.IndexOf(" ") + 1)..];
if (line.Contains("#menu:"))
{
binding.Path = line[(line.IndexOf("#menu:") + 6)..].Trim();
binding.Comment = binding.Path;
binding.IsMenu = true;
line = line[..line.IndexOf("#menu:")];
if (binding.Path.Contains(';'))
binding.Path = binding.Path[(binding.Path.IndexOf(";") + 1)..].Trim();
}
else if (line.Contains('#'))
{
binding.Comment = line[(line.IndexOf("#") + 1)..].Trim();
line = line[..line.IndexOf("#")];
}
binding.Command = line.Trim();
if (binding.Command.ToLower() == "ignore")
binding.Command = "";
bindings.Add(binding);
}
return bindings;
}
public static List<Binding> GetReducedBindings(List<Binding> bindings)
{
var defaultBindings = GetDefaults();
var removedBindings = new List<Binding>();
foreach (Binding defaultBinding in defaultBindings)
{
foreach (Binding binding in bindings)
{
if (defaultBinding.Input == binding.Input &&
defaultBinding.Command == binding.Command &&
defaultBinding.Comment == binding.Comment)
{
removedBindings.Add(binding);
}
}
}
foreach (Binding binding in bindings.ToArray())
if (removedBindings.Contains(binding))
bindings.Remove(binding);
return bindings.ToList();
}
public static List<Binding> GetEditorBindings(string content)
{
var defaults = GetDefaults();
var conf = Parse(content);
var removed = new List<Binding>();
foreach (Binding defaultBinding in defaults)
{
foreach (Binding confBinding in conf)
{
if (defaultBinding.Command == confBinding.Command &&
defaultBinding.Comment == confBinding.Comment)
{
defaultBinding.Input = confBinding.Input;
removed.Add(confBinding);
}
}
}
foreach (Binding binding in removed)
conf.Remove(binding);
defaults.AddRange(conf);
return defaults;
}
public static List<Binding> GetBindingsFromContent(string content)
{
var bindings = new List<Binding>();
if (!string.IsNullOrEmpty(content))
{
foreach (string line in content.Split('\r', '\n'))
{
string value = line.Trim();
if (value.StartsWith("#"))
continue;
if (!value.Contains(' '))
continue;
Binding binding = new Binding();
binding.Input = value[..value.IndexOf(" ")];
if (binding.Input == "_")
binding.Input = "";
value = value[(value.IndexOf(" ") + 1)..];
if (value.Contains("#menu:"))
{
binding.Path = value[(value.IndexOf("#menu:") + 6)..].Trim();
value = value[..value.IndexOf("#menu:")];
if (binding.Path.Contains(';'))
binding.Path = binding.Path[(binding.Path.IndexOf(";") + 1)..].Trim();
}
binding.Command = value.Trim();
if (binding.Command == "")
continue;
if (binding.Command.ToLower() == "ignore")
binding.Command = "";
bindings.Add(binding);
}
}
return bindings;
}
public static string _(string value) => value;
}

View File

@@ -0,0 +1,688 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-03-27 09:20+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:29
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:30
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:31
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:32
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:33
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:34
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:35
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:36
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:37
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:38
msgid "File"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:29
msgid "Open Files..."
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:30
msgid "Open URL or file from clipboard"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:31
msgid "Open DVD/Blu-ray Drive/Folder..."
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:33
msgid "Load external audio files..."
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:34
msgid "Load external subtitle files..."
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:36
msgid "Add files to playlist..."
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:38
msgid "Recent"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:39
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:40
msgid "Playback"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:39
msgid "Play/Pause"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:40
msgid "Stop"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:41
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:42
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:43
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:44
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:45
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:46
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:47
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:48
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:49
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:50
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:51
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:52
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:53
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:54
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:55
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:56
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:57
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:58
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:59
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:60
msgid "Navigate"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:41
msgid "Previous File"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:42
msgid "Next File"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:44
msgid "Next Chapter"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:45
msgid "Previous Chapter"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:47
msgid "Jump Next Frame"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:48
msgid "Jump Previous Frame"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:50
msgid "Jump 5 sec forward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:51
msgid "Jump 5 sec backward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:53
msgid "Jump 30 sec forward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:54
msgid "Jump 30 sec backward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:56
msgid "Jump 5 min forward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:57
msgid "Jump 5 min backward"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:59
msgid "Titles"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:60
msgid "Chapters"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:61
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:62
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:63
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:64
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:65
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:66
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:67
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:68
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:69
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:70
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:71
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:72
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:73
msgid "Pan & Scan"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:61
msgid "Decrease Size"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:62
msgid "Increase Size"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:64
msgid "Move Left"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:65
msgid "Move Right"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:67
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:101
msgid "Move Up"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:68
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:102
msgid "Move Down"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:70
msgid "Decrease Height"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:71
msgid "Increase Height"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:73
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:117
msgid "Reset"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:74
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:75
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:76
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:77
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:78
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:79
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:80
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:81
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:82
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:83
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:84
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:85
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:86
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:87
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:88
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:89
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:90
msgid "Video"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:74
msgid "Decrease Contrast"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:75
msgid "Increase Contrast"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:77
msgid "Decrease Brightness"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:78
msgid "Increase Brightness"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:80
msgid "Decrease Gamma"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:81
msgid "Increase Gamma"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:83
msgid "Decrease Saturation"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:84
msgid "Increase Saturation"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:86
msgid "Take Screenshot"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:87
msgid "Take Screenshot without subtitles"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:88
msgid "Toggle Deinterlace"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:89
msgid "Cycle Aspect Ratio"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:90
msgid "Rotate Video"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:91
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:92
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:93
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:94
msgid "Audio"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:91
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:95
msgid "Cycle/Next"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:93
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:99
msgid "Delay +0.1"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:94
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:98
msgid "Delay -0.1"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:95
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:96
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:97
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:98
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:99
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:100
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:101
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:102
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:103
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:104
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:105
msgid "Subtitle"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:96
msgid "Toggle Visibility"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:104
msgid "Decrease Subtitle Font Size"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:105
msgid "Increase Subtitle Font Size"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:106
msgid "Track"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:107
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:108
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:109
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:110
msgid "Volume"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:107
msgid "Up"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:108
msgid "Down"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:110
msgid "Mute"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:111
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:112
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:113
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:114
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:115
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:116
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:117
msgid "Speed"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:111
msgid "-10%"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:112
msgid "+10%"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:114
msgid "Half"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:115
msgid "Double"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:118
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:119
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:132
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:133
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:134
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:135
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:136
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:137
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:138
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:139
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:140
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:141
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:142
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:143
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:144
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:145
msgid "View"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:118
msgid "Command Palette"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:119
msgid "Toggle Fullscreen"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:120
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:121
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:122
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:123
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:124
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:125
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:126
msgid "View > Zoom"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:120
msgid "Enlarge"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:121
msgid "Shrink"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:123
msgid "50 %"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:124
msgid "100 %"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:125
msgid "200 %"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:126
msgid "300 %"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:127
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:128
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:129
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:130
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:131
msgid "View > Move"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:127
msgid "Left"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:128
msgid "Right"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:129
msgid "Top"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:130
msgid "Bottom"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:131
msgid "Center"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:132
msgid "Show Playlist"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:133
msgid "Show Profile Selection"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:134
msgid "Show Profiles"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:135
msgid "Show Audio Tracks"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:136
msgid "Show Subtitle Tracks"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:137
msgid "Show Chapters"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:138
msgid "Toggle Border"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:139
msgid "Toggle On Top"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:140
msgid "Toggle Statistics"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:141
msgid "Toggle OSC Visibility"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:142
msgid "Show Media Info"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:143
msgid "Show Media Info Advanced"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:144
msgid "Show Progress"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:145
msgid "Show Recent"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:146
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:147
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:148
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:149
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:150
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:151
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:152
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:153
msgid "View > Advanced"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:146
msgid "Show Console"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:147
msgid "Show Audio Devices"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:148
msgid "Show Properties"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:149
msgid "Show Commands"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:150
msgid "Show Demuxers"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:151
msgid "Show Decoders"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:152
msgid "Show Protocols"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:153
msgid "Show Keys"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:154
msgid "Profile"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:155
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:156
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:157
msgid "Settings"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:155
msgid "Show Config Editor"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:156
msgid "Show Input Editor"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:157
msgid "Open Config Folder"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:158
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:159
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:160
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:161
msgid "Settings > Setup"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:158
msgid "Register video file associations"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:159
msgid "Register audio file associations"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:160
msgid "Register image file associations"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:161
msgid "Unregister file associations"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:162
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:163
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:164
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:165
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:166
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:167
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:168
msgid "Tools"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:162
msgid "Set/clear A-B loop points"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:163
msgid "Toggle infinite file looping"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:164
msgid "Shuffle Playlist"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:165
msgid "Toggle Hardware Decoding"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:166
msgid "Exit"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:167
msgid "Exit Watch Later"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:168
msgid "Show current file in File Explorer"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:169
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:170
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:171
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:172
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:173
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:174
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:175
msgid "Help"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:169
msgid "Website mpv"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:170
msgid "Website mpvnet"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:172
msgid "Manual mpv"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:173
msgid "Manual mpvnet"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet\UI\InputBinding.cs:175
msgid "About mpvnet"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet.Windows\Properties\Resources.Designer.cs:79
msgid "editor_conf"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet.Windows\Properties\Resources.Designer.cs:101
msgid "input_conf"
msgstr ""
#: D:\Projects\CS\mpvnet\src\MpvNet.Windows\Properties\Resources.Designer.cs:146
msgid "theme"
msgstr ""

View File

@@ -0,0 +1,32 @@
# # Extract msgids from xaml files in project into pot file.
# . $PSScriptRoot/XGetText-Xaml.ps1
# $xamlFiles = Get-ChildItem -Recurse -File -Filter *.xaml |
# Where { $_.FullName -NotLike '*\obj\*' } | ForEach-Object { $_.FullName }
# XGetText-Xaml -o obj/xamlmessages.pot -k Gettext,GettextFormatConverter $xamlFiles
$Root = $PSScriptRoot
# Write list of .cs files into CS-Files.txt file
Get-ChildItem $Root/../.. -Recurse -File -Filter '*.cs' |
where { $_ -notmatch '[/\\]obj[/\\]' } |
foreach { $_.FullName } |
Out-File $Root/CS-Files.txt
# Extract msgids from cs files in project into pot file
xgettext --force-po --from-code=UTF-8 '--language=c#' -o $Root/CS-Messages.pot --files-from=$Root/CS-Files.txt --keyword=_
# # Merge two pot files into one
# msgcat.exe --use-first -o obj/result.pot obj/CS-Messages.pot obj/xamlmessages.pot
# Update po files with most recent msgids
$Locales = @("de")
$PoFiles = $Locales | foreach { 'Locale/' + $_ + '/LC_MESSAGES/MpvNet.po' }
$PoFiles | foreach {
# msgmerge --sort-output --update $_ $Root/CS-Messages.pot 2> $null
}
# echo "Po files updated with current msgIds: " $poFiles
# echo "You may now edit these files with PoEdit (https://poedit.net/)"

View File

@@ -0,0 +1,73 @@
function XGetText-Xaml {
param(
[Parameter(Mandatory=$true,
Position=0,
ParameterSetName="sourceFiles",
HelpMessage="XAML files to extract msgids from.")]
[ValidateNotNullOrEmpty()]
[string[]]$sourceFiles,
[Parameter(Mandatory=$true,
HelpMessage="Additional keywords that match MarkupExtensions enclosing msgids to be extracted.")]
[Alias("k")]
[string[]]
$Keywords,
[Parameter(Mandatory=$false,
HelpMessage="Write output to specified file.")]
[Alias("o")]
[string]$output="messages.pot")
$extractedIds = New-Object -TypeName System.Collections.Hashtable
ForEach ($keyword in $Keywords)
{
ForEach ($sourceFile in $sourceFiles)
{
Select-String $sourceFile -Pattern $("{[a-z]?[a-z0-9]*:"+$keyword+ " (([^}{]|{[^}]*})*)}") -AllMatches | ForEach-Object {
$filename = $sourceFile
$lineNumber = $_.LineNumber
$_.Matches | ForEach-Object {
$msgid = $_.Groups[1].ToString()
if ($msgid.StartsWith("'") -and $msgid.EndsWith("'")){
$msgid = $msgid.Substring(1, $msgid.Length-2);
}
$msgid = $msgid.Replace("\'", "'")
$msgid = $msgid.Replace("\,", ",")
if (-Not $extractedIds.ContainsKey($msgid))
{
$extractedIds.Add($msgid, @{Locations = New-Object System.Collections.ArrayList})
}
[void] $extractedIds[$msgid].Locations.Add('#: ' + $Filename + ':' + $LineNumber)
}
}
}
}
$result = '#, fuzzy
msgid ""
msgstr ""
"POT-Creation-Date: ' + $(Get-Date -Format 'yyyy-mm-dd HH:mmK') + '\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n\n"' + [System.Environment]::NewLine + [System.Environment]::NewLine
$extractedIds.GetEnumerator() | ForEach-Object {
if ($_.Key -like '*|*' ) {
$msgid = $_.Key.Substring($_.Key.indexof("|") +1)
$msgctxt = $_.Key.Substring(0, $_.Key.indexof("|"))
$result = $result + $($_.Value.Locations -join [System.Environment]::NewLine) + [System.Environment]::NewLine + "msgctxt """ + $msgctxt + """" + [System.Environment]::NewLine + "msgid """ + $msgid + """" + [System.Environment]::NewLine + "msgstr """"" + [System.Environment]::NewLine + [System.Environment]::NewLine + [System.Environment]::NewLine
}
else {
$result = $result + $($_.Value.Locations -join [System.Environment]::NewLine) + [System.Environment]::NewLine + "msgid """ + $_.Key + """" + [System.Environment]::NewLine + "msgstr """"" + [System.Environment]::NewLine + [System.Environment]::NewLine + [System.Environment]::NewLine
}
}
if ($output -eq '-') {
Write-Output $result.ToString()
} else {
[System.IO.File]::WriteAllLines($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($output), ($result -replace "\r", ""))
}
}

View File

@@ -0,0 +1,183 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-03-18 08:39+0100\n"
"PO-Revision-Date: 2019-03-18 08:40+0100\n"
"Last-Translator: Frank Skare <frank.skare.de@gmail.com>\n"
"Language-Team: \n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 2.2.1\n"
"X-Poedit-Basepath: ../../..\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-KeywordsList: Localizer.Noop\n"
"X-Poedit-SearchPath-0: .\n"
#: MainWindow.xaml:71
msgid "Binding string format support: {0:n0}"
msgstr "Binding-string-format-unterstützung {0:n0}"
#: MemoryLeakTestWindow.xaml:15
msgid ""
"Close this window to test that the attached behavior releases its event "
"listeners from the culture tracker."
msgstr ""
"Schließen Sie dieses Fenster, um zu testen, ob das angehängte Verhalten "
"seine Ereignis-Listener vom Culture Tracker freigibt."
#: MainWindow.xaml:38
msgid "Danish"
msgstr "Dänisch"
#: C:\Git\ngettext-avalonia\NGettext.Avalonia.Example\MainWindow.xaml.cs:17
msgid "Deferred localization"
msgstr "Aaufgeschobene Übersetzung"
#: MainWindow.xaml:56
msgid "Deferred localization example:"
msgstr "Beispiel von aufgeschobene Übersetzung:"
#: MainWindow.xaml:65
msgid "Enclosing single-quotes (') are optional"
msgstr "Einschließende einfache Anführungszeichen (') sind optional"
#: MainWindow.xaml:66
msgid "Enclosing single-quotes (apostrophes) are optional."
msgstr "Einschließende einfache Anführungszeichen (Apostrophe) sind optional."
#: MainWindow.xaml:34
msgid "English"
msgstr "Englisch"
#: MainWindow.xaml:69
msgctxt "Example"
msgid "GNOME glib syntax context example"
msgstr "GNOME glib-Syntaxkontextbeispiel"
#: MainWindow.xaml:36
msgid "German"
msgstr "Deutsch"
#: MainWindow.xaml:46
msgid "Localized date time format example:"
msgstr "Lokales Datumsformatbeispiel:"
#: MainWindow.xaml:51
msgid "Localized enum combo example:"
msgstr "Lokalisierte Enum-Combobox-Beispiel:"
#: MainWindow.xaml:41
msgid "Localized number format example:"
msgstr "Lokales Zahlenformatbeispiel:"
#: MainWindow.xaml:76
msgid "Memory leak test progress:"
msgstr "Speicherlecktestfortschritt:"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\MainWindow.xaml.cs:97
msgid "NGettext.Avalonia Example"
msgstr "NGettext.Avalonia-Beispiel"
#: MainWindow.xaml:73
msgid "Run TrackCurrentCultureBehavior memory leak test"
msgstr "Den TrackCurrentCultureBehavior-Speicherlecktest ausführen"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\MainWindow.xaml.cs:99
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\MainWindow.xaml.cs:100
msgid "Singular"
msgid_plural "Plural"
msgstr[0] "Singular"
msgstr[1] "Plural"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\MainWindow.xaml.cs:102
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\MainWindow.xaml.cs:103
#, csharp-format
msgid "Singular {0:n3}"
msgid_plural "Plural {0:n3}"
msgstr[0] "Singular {0:n3}"
msgstr[1] "Plural {0:n3}"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\ExampleEnum.cs:16
msgctxt "EnumMsgId example"
msgid "Some fourth value"
msgstr "Ein vierter Wert"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\ExampleEnum.cs:10
msgid "Some other value"
msgstr "Ein anderer Wert"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\ExampleEnum.cs:13
msgid "Some third value"
msgstr "Ein dritter Wert"
#: C:\Git\ngettext-wpf\NGettext.Avalonia.Example\ExampleEnum.cs:7
msgid "Some value"
msgstr "Ein gewisser Wert"
#: MainWindow.xaml:70
msgid "Text with punctuation: 1, 2, 3"
msgstr "Tekst mit Zeichensetzung: 1, 2, 3"
#: MainWindow.xaml:67
msgid "Unenclosed escaped single-quotes (') are supported."
msgstr "Unclosed maskierte einfache Anführungszeichen (') werden unterstützt."
#: MainWindow.xaml:68
msgid "Unicode™ in msgIds is supported"
msgstr "Unicode™ in msgIds wird unterstützt"
#: MainWindow.xaml:61
msgid "Varying cAsE ExamPle:"
msgstr "Beispiel von Variation zwiScHen Groß- und KleInBUchstaBen:"
#: MainWindow.xaml:60
msgid "Varying case example:"
msgstr "Beispiel von Variation zwischen Groß- und Kleinbuchstaben:"
#, fuzzy
#~ msgctxt ""
#~ msgid ""
#~ msgstr ""
#~ "Project-Id-Version: \n"
#~ "Report-Msgid-Bugs-To: \n"
#~ "POT-Creation-Date: 2019-02-18 13:15+0100\n"
#~ "PO-Revision-Date: 2018-09-28 11:43+0200\n"
#~ "Last-Translator: Robert Jørgensgaard Engdahl <rje@accuratech.dk>\n"
#~ "Language-Team: \n"
#~ "Language: de_DE\n"
#~ "MIME-Version: 1.0\n"
#~ "Content-Type: text/plain; charset=UTF-8\n"
#~ "Content-Transfer-Encoding: 8bit\n"
#~ "X-Generator: Poedit 2.1.1\n"
#~ "X-Poedit-Basepath: ../../..\n"
#~ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#~ "X-Poedit-KeywordsList: Localizer.Noop\n"
#~ "X-Poedit-SearchPath-0: .\n"
#, fuzzy
#~ msgctxt "test"
#~ msgid ""
#~ msgstr ""
#~ "Project-Id-Version: \n"
#~ "Report-Msgid-Bugs-To: \n"
#~ "POT-Creation-Date: 2019-02-18 13:21+0100\n"
#~ "PO-Revision-Date: 2018-09-28 11:43+0200\n"
#~ "Last-Translator: Robert Jørgensgaard Engdahl <rje@accuratech.dk>\n"
#~ "Language-Team: \n"
#~ "Language: de_DE\n"
#~ "MIME-Version: 1.0\n"
#~ "Content-Type: text/plain; charset=UTF-8\n"
#~ "Content-Transfer-Encoding: 8bit\n"
#~ "X-Generator: Poedit 2.1.1\n"
#~ "X-Poedit-Basepath: ../../..\n"
#~ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#~ "X-Poedit-KeywordsList: Localizer.Noop\n"
#~ "X-Poedit-SearchPath-0: .\n"
#, fuzzy
#~ msgctxt "EnumMsgId example"
#~ msgid "Some third value"
#~ msgstr "Ein dritter Wert"

View File

@@ -0,0 +1,13 @@

using CommunityToolkit.Mvvm.Messaging.Messages;
namespace MpvNet.MVVM;
public class MainWindowIsLoadedMessage { }
//public class ScaleWindowMessage : ValueChangedMessage<float>
//{
// public ScaleWindowMessage(float value) : base(value)
// {
// }
//}

10
src/MpvNet/MediaTrack.cs Normal file
View File

@@ -0,0 +1,10 @@

namespace MpvNet;
public class MediaTrack
{
public int ID { get; set; }
public bool External { get; set; }
public string Text { get; set; } = "";
public string Type { get; set; } = "";
}

492
src/MpvNet/MpvClient.cs Normal file
View File

@@ -0,0 +1,492 @@

using System.Runtime.InteropServices;
using static MpvNet.Native.LibMpv;
namespace MpvNet;
public class MpvClient
{
public event Action<string[]>? ClientMessage; // client-message MPV_EVENT_CLIENT_MESSAGE
public event Action<mpv_log_level, string>? LogMessage; // log-message MPV_EVENT_LOG_MESSAGE
public event Action<mpv_end_file_reason>? EndFile; // end-file MPV_EVENT_END_FILE
public event Action? Shutdown; // shutdown MPV_EVENT_SHUTDOWN
public event Action? GetPropertyReply; // get-property-reply MPV_EVENT_GET_PROPERTY_REPLY
public event Action? SetPropertyReply; // set-property-reply MPV_EVENT_SET_PROPERTY_REPLY
public event Action? CommandReply; // command-reply MPV_EVENT_COMMAND_REPLY
public event Action? StartFile; // start-file MPV_EVENT_START_FILE
public event Action? FileLoaded; // file-loaded MPV_EVENT_FILE_LOADED
public event Action? VideoReconfig; // video-reconfig MPV_EVENT_VIDEO_RECONFIG
public event Action? AudioReconfig; // audio-reconfig MPV_EVENT_AUDIO_RECONFIG
public event Action? Seek; // seek MPV_EVENT_SEEK
public event Action? PlaybackRestart; // playback-restart MPV_EVENT_PLAYBACK_RESTART
public Dictionary<string, List<Action>> PropChangeActions { get; set; } = new Dictionary<string, List<Action>>();
public Dictionary<string, List<Action<int>>> IntPropChangeActions { get; set; } = new Dictionary<string, List<Action<int>>>();
public Dictionary<string, List<Action<bool>>> BoolPropChangeActions { get; set; } = new Dictionary<string, List<Action<bool>>>();
public Dictionary<string, List<Action<double>>> DoublePropChangeActions { get; set; } = new Dictionary<string, List<Action<double>>>();
public Dictionary<string, List<Action<string>>> StringPropChangeActions { get; set; } = new Dictionary<string, List<Action<string>>>();
public nint Handle { get; set; }
public void EventLoop()
{
while (true)
{
IntPtr ptr = mpv_wait_event(Handle, -1);
mpv_event evt = (mpv_event)Marshal.PtrToStructure(ptr, typeof(mpv_event))!;
try
{
switch (evt.event_id)
{
case mpv_event_id.MPV_EVENT_SHUTDOWN:
OnShutdown();
return;
case mpv_event_id.MPV_EVENT_LOG_MESSAGE:
{
var data = (mpv_event_log_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_log_message))!;
OnLogMessage(data);
}
break;
case mpv_event_id.MPV_EVENT_CLIENT_MESSAGE:
{
var data = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message))!;
OnClientMessage(data);
}
break;
case mpv_event_id.MPV_EVENT_VIDEO_RECONFIG:
OnVideoReconfig();
break;
case mpv_event_id.MPV_EVENT_END_FILE:
{
var data = (mpv_event_end_file)Marshal.PtrToStructure(evt.data, typeof(mpv_event_end_file))!;
OnEndFile(data);
}
break;
case mpv_event_id.MPV_EVENT_FILE_LOADED:
OnFileLoaded();
break;
case mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
{
var data = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property))!;
OnPropertyChange(data);
}
break;
case mpv_event_id.MPV_EVENT_GET_PROPERTY_REPLY:
OnGetPropertyReply();
break;
case mpv_event_id.MPV_EVENT_SET_PROPERTY_REPLY:
OnSetPropertyReply();
break;
case mpv_event_id.MPV_EVENT_COMMAND_REPLY:
OnCommandReply();
break;
case mpv_event_id.MPV_EVENT_START_FILE:
OnStartFile();
break;
case mpv_event_id.MPV_EVENT_AUDIO_RECONFIG:
OnAudioReconfig();
break;
case mpv_event_id.MPV_EVENT_SEEK:
OnSeek();
break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
OnPlaybackRestart();
break;
}
}
catch (Exception ex)
{
Terminal.WriteError(ex);
}
}
}
protected virtual void OnClientMessage(mpv_event_client_message data) =>
ClientMessage?.Invoke(ConvertFromUtf8Strings(data.args, data.num_args));
protected virtual void OnLogMessage(mpv_event_log_message data)
{
if (LogMessage != null)
{
string msg = $"[{ConvertFromUtf8(data.prefix)}] {ConvertFromUtf8(data.text)}";
LogMessage.Invoke(data.log_level, msg);
}
}
protected virtual void OnPropertyChange(mpv_event_property data)
{
if (data.format == mpv_format.MPV_FORMAT_FLAG)
{
lock (BoolPropChangeActions)
foreach (var pair in BoolPropChangeActions)
if (pair.Key == data.name)
{
bool value = Marshal.PtrToStructure<int>(data.data) == 1;
foreach (var action in pair.Value)
action.Invoke(value);
}
}
else if (data.format == mpv_format.MPV_FORMAT_STRING)
{
lock (StringPropChangeActions)
foreach (var pair in StringPropChangeActions)
if (pair.Key == data.name)
{
string value = ConvertFromUtf8(Marshal.PtrToStructure<IntPtr>(data.data));
foreach (var action in pair.Value)
action.Invoke(value);
}
}
else if (data.format == mpv_format.MPV_FORMAT_INT64)
{
lock (IntPropChangeActions)
foreach (var pair in IntPropChangeActions)
if (pair.Key == data.name)
{
int value = Marshal.PtrToStructure<int>(data.data);
foreach (var action in pair.Value)
action.Invoke(value);
}
}
else if (data.format == mpv_format.MPV_FORMAT_NONE)
{
lock (PropChangeActions)
foreach (var pair in PropChangeActions)
if (pair.Key == data.name)
foreach (var action in pair.Value)
action.Invoke();
}
else if (data.format == mpv_format.MPV_FORMAT_DOUBLE)
{
lock (DoublePropChangeActions)
foreach (var pair in DoublePropChangeActions)
if (pair.Key == data.name)
{
double value = Marshal.PtrToStructure<double>(data.data);
foreach (var action in pair.Value)
action.Invoke(value);
}
}
}
protected virtual void OnEndFile(mpv_event_end_file data) => EndFile?.Invoke((mpv_end_file_reason)data.reason);
protected virtual void OnFileLoaded() => FileLoaded?.Invoke();
protected virtual void OnShutdown() => Shutdown?.Invoke();
protected virtual void OnGetPropertyReply() => GetPropertyReply?.Invoke();
protected virtual void OnSetPropertyReply() => SetPropertyReply?.Invoke();
protected virtual void OnCommandReply() => CommandReply?.Invoke();
protected virtual void OnStartFile() => StartFile?.Invoke();
protected virtual void OnVideoReconfig() => VideoReconfig?.Invoke();
protected virtual void OnAudioReconfig() => AudioReconfig?.Invoke();
protected virtual void OnSeek() => Seek?.Invoke();
protected virtual void OnPlaybackRestart() => PlaybackRestart?.Invoke();
public void Command(string command)
{
mpv_error err = mpv_command_string(Handle, command);
if (err < 0)
HandleError(err, "error executing command: " + command);
}
public void CommandV(params string[] args)
{
int count = args.Length + 1;
IntPtr[] pointers = new IntPtr[count];
IntPtr rootPtr = Marshal.AllocHGlobal(IntPtr.Size * count);
for (int index = 0; index < args.Length; index++)
{
var bytes = GetUtf8Bytes(args[index]);
IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, ptr, bytes.Length);
pointers[index] = ptr;
}
Marshal.Copy(pointers, 0, rootPtr, count);
mpv_error err = mpv_command(Handle, rootPtr);
foreach (IntPtr ptr in pointers)
Marshal.FreeHGlobal(ptr);
Marshal.FreeHGlobal(rootPtr);
if (err < 0)
HandleError(err, "error executing command: " + string.Join("\n", args));
}
public string Expand(string? value)
{
if (value == null)
return "";
if (!value.Contains("${"))
return value;
string[] args = { "expand-text", value };
int count = args.Length + 1;
IntPtr[] pointers = new IntPtr[count];
IntPtr rootPtr = Marshal.AllocHGlobal(IntPtr.Size * count);
for (int index = 0; index < args.Length; index++)
{
var bytes = GetUtf8Bytes(args[index]);
IntPtr ptr = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, ptr, bytes.Length);
pointers[index] = ptr;
}
Marshal.Copy(pointers, 0, rootPtr, count);
IntPtr resultNodePtr = Marshal.AllocHGlobal(16);
mpv_error err = mpv_command_ret(Handle, rootPtr, resultNodePtr);
foreach (IntPtr ptr in pointers)
Marshal.FreeHGlobal(ptr);
Marshal.FreeHGlobal(rootPtr);
if (err < 0)
{
HandleError(err, "error executing command: " + string.Join("\n", args));
Marshal.FreeHGlobal(resultNodePtr);
return "property expansion error";
}
mpv_node resultNode = Marshal.PtrToStructure<mpv_node>(resultNodePtr);
string ret = ConvertFromUtf8(resultNode.str);
mpv_free_node_contents(resultNodePtr);
Marshal.FreeHGlobal(resultNodePtr);
return ret;
}
public bool GetPropertyBool(string name)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_FLAG, out IntPtr lpBuffer);
if (err < 0)
HandleError(err, "error getting property: " + name);
return lpBuffer.ToInt32() != 0;
}
public void SetPropertyBool(string name, bool value)
{
long val = value ? 1 : 0;
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_FLAG, ref val);
if (err < 0)
HandleError(err, $"error setting property: {name} = {value}");
}
public int GetPropertyInt(string name)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
if (err < 0 && App.DebugMode)
HandleError(err, "error getting property: " + name);
return lpBuffer.ToInt32();
}
public void SetPropertyInt(string name, int value)
{
long val = value;
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val);
if (err < 0)
HandleError(err, $"error setting property: {name} = {value}");
}
public void SetPropertyLong(string name, long value)
{
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref value);
if (err < 0)
HandleError(err, $"error setting property: {name} = {value}");
}
public long GetPropertyLong(string name)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
if (err < 0)
HandleError(err, "error getting property: " + name);
return lpBuffer.ToInt64();
}
public double GetPropertyDouble(string name, bool handleError = true)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_DOUBLE, out double value);
if (err < 0 && handleError && App.DebugMode)
HandleError(err, "error getting property: " + name);
return value;
}
public void SetPropertyDouble(string name, double value)
{
double val = value;
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val);
if (err < 0)
HandleError(err, $"error setting property: {name} = {value}");
}
public string GetPropertyString(string name)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer);
if (err == 0)
{
string ret = ConvertFromUtf8(lpBuffer);
mpv_free(lpBuffer);
return ret;
}
if (err < 0 && App.DebugMode)
HandleError(err, "error getting property: " + name);
return "";
}
public void SetPropertyString(string name, string value)
{
byte[] bytes = GetUtf8Bytes(value);
mpv_error err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes);
if (err < 0)
HandleError(err, $"error setting property: {name} = {value}");
}
public string GetPropertyOsdString(string name)
{
mpv_error err = mpv_get_property(Handle, GetUtf8Bytes(name),
mpv_format.MPV_FORMAT_OSD_STRING, out IntPtr lpBuffer);
if (err == 0)
{
string ret = ConvertFromUtf8(lpBuffer);
mpv_free(lpBuffer);
return ret;
}
if (err < 0)
HandleError(err, "error getting property: " + name);
return "";
}
public void ObservePropertyInt(string name, Action<int> action)
{
lock (IntPropChangeActions)
{
if (!IntPropChangeActions.ContainsKey(name))
{
mpv_error err = mpv_observe_property(Handle, 0, name, mpv_format.MPV_FORMAT_INT64);
if (err < 0)
HandleError(err, "error observing property: " + name);
else
IntPropChangeActions[name] = new List<Action<int>>();
}
if (IntPropChangeActions.ContainsKey(name))
IntPropChangeActions[name].Add(action);
}
}
public void ObservePropertyDouble(string name, Action<double> action)
{
lock (DoublePropChangeActions)
{
if (!DoublePropChangeActions.ContainsKey(name))
{
mpv_error err = mpv_observe_property(Handle, 0, name, mpv_format.MPV_FORMAT_DOUBLE);
if (err < 0)
HandleError(err, "error observing property: " + name);
else
DoublePropChangeActions[name] = new List<Action<double>>();
}
if (DoublePropChangeActions.ContainsKey(name))
DoublePropChangeActions[name].Add(action);
}
}
public void ObservePropertyBool(string name, Action<bool> action)
{
lock (BoolPropChangeActions)
{
if (!BoolPropChangeActions.ContainsKey(name))
{
mpv_error err = mpv_observe_property(Handle, 0, name, mpv_format.MPV_FORMAT_FLAG);
if (err < 0)
HandleError(err, "error observing property: " + name);
else
BoolPropChangeActions[name] = new List<Action<bool>>();
}
if (BoolPropChangeActions.ContainsKey(name))
BoolPropChangeActions[name].Add(action);
}
}
public void ObservePropertyString(string name, Action<string> action)
{
lock (StringPropChangeActions)
{
if (!StringPropChangeActions.ContainsKey(name))
{
mpv_error err = mpv_observe_property(Handle, 0, name, mpv_format.MPV_FORMAT_STRING);
if (err < 0)
HandleError(err, "error observing property: " + name);
else
StringPropChangeActions[name] = new List<Action<string>>();
}
if (StringPropChangeActions.ContainsKey(name))
StringPropChangeActions[name].Add(action);
}
}
public void ObserveProperty(string name, Action action)
{
lock (PropChangeActions)
{
if (!PropChangeActions.ContainsKey(name))
{
mpv_error err = mpv_observe_property(Handle, 0, name, mpv_format.MPV_FORMAT_NONE);
if (err < 0)
HandleError(err, "error observing property: " + name);
else
PropChangeActions[name] = new List<Action>();
}
if (PropChangeActions.ContainsKey(name))
PropChangeActions[name].Add(action);
}
}
static void HandleError(mpv_error err, string msg)
{
Terminal.WriteError(msg);
Terminal.WriteError(GetError(err));
}
}

27
src/MpvNet/MpvNet.csproj Normal file
View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AssemblyName>libmpvnet</AssemblyName>
<Product>mpv.net</Product>
<Nullable>enable</Nullable>
<RootNamespace>MpvNet</RootNamespace>
</PropertyGroup>
<ItemGroup>
<None Include="..\..\docs\changelog.md" Link="Docs\changelog.md" />
<None Include="..\..\docs\manual.md" Link="Docs\manual.md" />
<None Include="..\..\README.md" Link="Docs\README.md" />
</ItemGroup>
<ItemGroup>
<Folder Include="Extension\" />
<Folder Include="Input\" />
<Folder Include="Docs\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
</ItemGroup>
</Project>

242
src/MpvNet/Native/LibMpv.cs Normal file
View File

@@ -0,0 +1,242 @@

#pragma warning disable IDE1006 // type name starts with underscore
#pragma warning disable CA1401 // P/Invokes should not be visible
#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments
using System.Runtime.InteropServices;
using System.Text;
namespace MpvNet.Native;
public static class LibMpv
{
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern nint mpv_create();
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern nint mpv_create_client(nint mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_initialize(nint mpvHandle);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mpv_destroy(nint mpvHandle);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_command(nint mpvHandle, nint strings);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_command_string(nint mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_command_ret(nint mpvHandle, nint strings, nint node);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mpv_free_node_contents(nint node);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern nint mpv_error_string(mpv_error error);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_request_log_messages(nint mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string min_level);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option(nint mpvHandle, byte[] name, mpv_format format, ref long data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option_string(nint mpvHandle, byte[] name, byte[] value);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_get_property(nint mpvHandle, byte[] name, mpv_format format, out nint data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_get_property(nint mpvHandle, byte[] name, mpv_format format, out double data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_set_property(nint mpvHandle, byte[] name, mpv_format format, ref byte[] data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_set_property(nint mpvHandle, byte[] name, mpv_format format, ref long data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_set_property(nint mpvHandle, byte[] name, mpv_format format, ref double data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_observe_property(nint mpvHandle, ulong reply_userdata, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, mpv_format format);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_unobserve_property(nint mpvHandle, ulong registered_reply_userdata);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mpv_free(nint data);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern nint mpv_wait_event(nint mpvHandle, double timeout);
[DllImport("libmpv-2.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_request_event(nint mpvHandle, mpv_event_id id, int enable);
public enum mpv_error
{
MPV_ERROR_SUCCESS = 0,
MPV_ERROR_EVENT_QUEUE_FULL = -1,
MPV_ERROR_NOMEM = -2,
MPV_ERROR_UNINITIALIZED = -3,
MPV_ERROR_INVALID_PARAMETER = -4,
MPV_ERROR_OPTION_NOT_FOUND = -5,
MPV_ERROR_OPTION_FORMAT = -6,
MPV_ERROR_OPTION_ERROR = -7,
MPV_ERROR_PROPERTY_NOT_FOUND = -8,
MPV_ERROR_PROPERTY_FORMAT = -9,
MPV_ERROR_PROPERTY_UNAVAILABLE = -10,
MPV_ERROR_PROPERTY_ERROR = -11,
MPV_ERROR_COMMAND = -12,
MPV_ERROR_LOADING_FAILED = -13,
MPV_ERROR_AO_INIT_FAILED = -14,
MPV_ERROR_VO_INIT_FAILED = -15,
MPV_ERROR_NOTHING_TO_PLAY = -16,
MPV_ERROR_UNKNOWN_FORMAT = -17,
MPV_ERROR_UNSUPPORTED = -18,
MPV_ERROR_NOT_IMPLEMENTED = -19,
MPV_ERROR_GENERIC = -20
}
public enum mpv_event_id
{
MPV_EVENT_NONE = 0,
MPV_EVENT_SHUTDOWN = 1,
MPV_EVENT_LOG_MESSAGE = 2,
MPV_EVENT_GET_PROPERTY_REPLY = 3,
MPV_EVENT_SET_PROPERTY_REPLY = 4,
MPV_EVENT_COMMAND_REPLY = 5,
MPV_EVENT_START_FILE = 6,
MPV_EVENT_END_FILE = 7,
MPV_EVENT_FILE_LOADED = 8,
MPV_EVENT_IDLE = 11, //deprecated
MPV_EVENT_TICK = 14, //deprecated
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
MPV_EVENT_CLIENT_MESSAGE = 16,
MPV_EVENT_VIDEO_RECONFIG = 17,
MPV_EVENT_AUDIO_RECONFIG = 18,
MPV_EVENT_SEEK = 20,
MPV_EVENT_PLAYBACK_RESTART = 21,
MPV_EVENT_PROPERTY_CHANGE = 22,
MPV_EVENT_QUEUE_OVERFLOW = 24,
MPV_EVENT_HOOK = 25
}
public enum mpv_format
{
MPV_FORMAT_NONE = 0,
MPV_FORMAT_STRING = 1,
MPV_FORMAT_OSD_STRING = 2,
MPV_FORMAT_FLAG = 3,
MPV_FORMAT_INT64 = 4,
MPV_FORMAT_DOUBLE = 5,
MPV_FORMAT_NODE = 6,
MPV_FORMAT_NODE_ARRAY = 7,
MPV_FORMAT_NODE_MAP = 8,
MPV_FORMAT_BYTE_ARRAY = 9
}
public enum mpv_log_level
{
MPV_LOG_LEVEL_NONE = 0,
MPV_LOG_LEVEL_FATAL = 10,
MPV_LOG_LEVEL_ERROR = 20,
MPV_LOG_LEVEL_WARN = 30,
MPV_LOG_LEVEL_INFO = 40,
MPV_LOG_LEVEL_V = 50,
MPV_LOG_LEVEL_DEBUG = 60,
MPV_LOG_LEVEL_TRACE = 70,
}
public enum mpv_end_file_reason
{
MPV_END_FILE_REASON_EOF = 0,
MPV_END_FILE_REASON_STOP = 2,
MPV_END_FILE_REASON_QUIT = 3,
MPV_END_FILE_REASON_ERROR = 4,
MPV_END_FILE_REASON_REDIRECT = 5
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message
{
public nint prefix;
public nint level;
public nint text;
public mpv_log_level log_level;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event
{
public mpv_event_id event_id;
public int error;
public ulong reply_userdata;
public nint data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_client_message
{
public int num_args;
public nint args;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_property
{
public string name;
public mpv_format format;
public nint data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_end_file
{
public int reason;
public int error;
}
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct mpv_node
{
[FieldOffset(0)] public nint str;
[FieldOffset(0)] public int flag;
[FieldOffset(0)] public long int64;
[FieldOffset(0)] public double dbl;
[FieldOffset(0)] public nint list;
[FieldOffset(0)] public nint ba;
[FieldOffset(8)] public mpv_format format;
}
public static string[] ConvertFromUtf8Strings(nint utf8StringArray, int stringCount)
{
nint[] intPtrArray = new nint[stringCount];
string[] stringArray = new string[stringCount];
Marshal.Copy(utf8StringArray, intPtrArray, 0, stringCount);
for (int i = 0; i < stringCount; i++)
stringArray[i] = ConvertFromUtf8(intPtrArray[i]);
return stringArray;
}
public static string ConvertFromUtf8(nint nativeUtf8)
{
int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0)
++len;
byte[] buffer = new byte[len];
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
public static string GetError(mpv_error err) => ConvertFromUtf8(mpv_error_string(err));
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
}

View File

@@ -0,0 +1,125 @@

using System.Runtime.InteropServices;
namespace MpvNet;
public class MediaInfo : IDisposable
{
readonly IntPtr Handle;
public MediaInfo(string file)
{
if ((Handle = MediaInfo_New()) == IntPtr.Zero)
throw new Exception("Failed to call MediaInfo_New");
if (MediaInfo_Open(Handle, file) == 0)
throw new Exception("Error MediaInfo_Open");
}
public string GetInfo(MediaInfoStreamKind kind, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, kind, 0,
parameter, MediaInfoKind.Text, MediaInfoKind.Name)) ?? "";
}
public int GetCount(MediaInfoStreamKind kind) => MediaInfo_Count_Get(Handle, kind, -1);
public string GetGeneral(string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.General,
0, parameter, MediaInfoKind.Text, MediaInfoKind.Name)) ?? "";
}
public string GetVideo(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name)) ?? "";
}
public string GetAudio(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Audio,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name)) ?? "";
}
public string GetText(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Text,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name)) ?? "";
}
public string GetSummary(bool complete, bool rawView)
{
MediaInfo_Option(Handle, "Language", rawView ? "raw" : "");
MediaInfo_Option(Handle, "Complete", complete ? "1" : "0");
return Marshal.PtrToStringUni(MediaInfo_Inform(Handle, 0)) ?? "";
}
bool Disposed;
public void Dispose()
{
if (!Disposed)
{
if (Handle != IntPtr.Zero)
{
MediaInfo_Close(Handle);
MediaInfo_Delete(Handle);
}
Disposed = true;
GC.SuppressFinalize(this);
}
}
~MediaInfo() { Dispose(); }
[DllImport("MediaInfo.dll")]
static extern IntPtr MediaInfo_New();
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern int MediaInfo_Open(IntPtr handle, string path);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern IntPtr MediaInfo_Option(IntPtr handle, string option, string value);
[DllImport("MediaInfo.dll")]
static extern IntPtr MediaInfo_Inform(IntPtr handle, int reserved);
[DllImport("MediaInfo.dll")]
static extern int MediaInfo_Close(IntPtr handle);
[DllImport("MediaInfo.dll")]
static extern void MediaInfo_Delete(IntPtr handle);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern IntPtr MediaInfo_Get(IntPtr handle, MediaInfoStreamKind kind,
int stream, string parameter, MediaInfoKind infoKind, MediaInfoKind searchKind);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern int MediaInfo_Count_Get(IntPtr handle, MediaInfoStreamKind streamKind, int stream);
}
public enum MediaInfoStreamKind
{
General,
Video,
Audio,
Text,
Other,
Image,
Menu,
Max,
}
public enum MediaInfoKind
{
Name,
Text,
Measure,
Options,
NameText,
MeasureText,
Info,
HowTo
}

View File

@@ -0,0 +1,17 @@

using System.Runtime.InteropServices;
using System.Collections;
namespace MpvNet.Native;
public class StringLogicalComparer : IComparer, IComparer<string>
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
static extern int StrCmpLogical(string? x, string? y);
static int IComparer_Compare(object? x, object? y) => StrCmpLogical(x!.ToString(), y!.ToString());
static int IComparerOfString_Compare(string? x, string? y) => StrCmpLogical(x, y);
int IComparer.Compare(object? x, object? y) => IComparer_Compare(x, y);
int IComparer<string>.Compare(string? x, string? y) => IComparerOfString_Compare(x, y);
}

1167
src/MpvNet/Player.cs Normal file

File diff suppressed because it is too large Load Diff

60
src/MpvNet/Settings.cs Normal file
View File

@@ -0,0 +1,60 @@

using System.Text;
using System.Xml.Serialization;
using System.Xml;
using System.Drawing;
namespace MpvNet;
[Serializable()]
public class AppSettings
{
public bool InputDefaultBindingsFixApplied;
public bool ShowMenuFixApplied;
public int Volume = 70;
public List<string> RecentFiles = new List<string>();
public Point WindowLocation;
public Point WindowPosition;
public Size WindowSize;
public string ConfigEditorSearch = "Video:";
public string Mute = "no";
}
class SettingsManager
{
public static string SettingsFile => Player.ConfigFolder + "settings.xml";
public static AppSettings Load()
{
if (!File.Exists(SettingsFile))
return new AppSettings();
try
{
XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));
using FileStream fs = new FileStream(SettingsFile, FileMode.Open);
return (AppSettings)serializer.Deserialize(fs)!;
}
catch (Exception ex)
{
Terminal.WriteError(ex.ToString());
return new AppSettings();
}
}
public static void Save(object obj)
{
try
{
using XmlTextWriter writer = new XmlTextWriter(SettingsFile, Encoding.UTF8);
writer.Formatting = Formatting.Indented;
writer.Indentation = 4;
XmlSerializer serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(writer, obj);
}
catch (Exception ex)
{
Terminal.WriteError(ex.ToString());
}
}
}

40
src/MpvNet/Terminal.cs Normal file
View File

@@ -0,0 +1,40 @@

namespace MpvNet;
public static class Terminal
{
static int Padding { get; } = 60;
public static void WriteError(object obj, string module = "mpv.net") => Write(obj, module, ConsoleColor.DarkRed, false);
public static void Write(object obj, string module = "mpv.net") => Write(obj, module, ConsoleColor.Black, true);
public static void Write(object obj, string module, ConsoleColor color, bool useDefaultColor)
{
string text = obj + "";
if (text == "")
return;
if (!string.IsNullOrEmpty(module))
module = "[" + module + "] ";
if (useDefaultColor)
Console.ResetColor();
else
Console.ForegroundColor = color;
text = module + text;
if (text.Length < Padding)
text = text.PadRight(Padding);
if (color == ConsoleColor.Red || color == ConsoleColor.DarkRed)
Console.Error.WriteLine(text);
else
Console.WriteLine(text);
Console.ResetColor();
Trace.WriteLine(obj);
}
}