Compare commits

..

39 Commits
4.1 ... 4.5

Author SHA1 Message Date
Frank Skare
cedd694e8e - 2019-07-01 02:45:22 +02:00
Frank Skare
d1c58585f5 - 2019-06-30 07:16:28 +02:00
Frank Skare
0fce626c46 - 2019-06-30 07:14:13 +02:00
Frank Skare
adb8f38c0d - 2019-06-30 02:09:30 +02:00
Frank Skare
735ee42580 - 2019-06-30 02:07:16 +02:00
Frank Skare
103ae81b8b - 2019-06-29 12:36:35 +02:00
Frank Skare
8e351193e1 - 2019-06-27 10:35:37 +02:00
Frank Skare
d57b9281a1 - 2019-06-27 07:51:19 +02:00
Frank Skare
e07627377e - 2019-06-27 07:44:23 +02:00
Frank Skare
8899f4b772 - 2019-06-27 06:59:34 +02:00
Frank Skare
e7fd268927 - 2019-06-27 02:43:17 +02:00
Frank Skare
c8fe3a4c43 - 2019-06-26 16:06:41 +02:00
Frank Skare
19c2502c31 - 2019-06-26 03:23:40 +02:00
Frank Skare
56dae4babd - 2019-06-26 03:16:40 +02:00
Frank Skare
bc20d7527c - 2019-06-26 02:54:59 +02:00
Frank Skare
3d325d4fe5 - 2019-06-25 05:14:58 +02:00
Frank Skare
ba0141cfb1 - 2019-06-24 09:04:18 +02:00
Frank Skare
d32c7b4056 - 2019-06-24 09:00:01 +02:00
Frank Skare
63f064ba1c - 2019-06-24 07:52:30 +02:00
Frank Skare
e5c9df31e4 - 2019-06-22 06:27:22 +02:00
Frank Skare
c0a8e895f7 - 2019-06-19 18:22:40 +02:00
Frank Skare
064ae44ecf - 2019-06-19 18:03:14 +02:00
Frank Skare
005ba6e5ea - 2019-06-18 16:20:02 +02:00
Frank Skare
9f6aba6cbd - 2019-06-18 16:08:28 +02:00
Frank Skare
87e3d9ca3e - 2019-06-17 21:35:14 +02:00
Frank Skare
29a0eae3d5 - 2019-06-16 12:46:04 +02:00
Frank Skare
01b2492cde - 2019-06-16 12:28:15 +02:00
Frank Skare
d2b1ca0c69 - 2019-06-16 12:18:36 +02:00
Frank Skare
91b429e6b0 4.2 2019-06-16 12:12:51 +02:00
Frank Skare
fa05e95892 - 2019-06-16 12:02:46 +02:00
Frank Skare
701a58a4bf - 2019-06-16 11:53:28 +02:00
Frank Skare
387c3f7896 - 2019-06-16 11:48:15 +02:00
Frank Skare
eeb0189577 - 2019-06-16 11:47:03 +02:00
Frank Skare
de4c2d6d70 - 2019-06-16 11:44:41 +02:00
Frank Skare
63ee598ebd support added to force single instance 2019-06-16 10:00:14 +02:00
Frank Skare
1bf8e568c5 - 2019-06-14 01:03:18 +02:00
Frank Skare
f8f58dba5e - 2019-06-14 00:23:51 +02:00
Frank Skare
ebb8ad351a - 2019-06-13 22:29:23 +02:00
Frank Skare
0202c34c6f - 2019-06-07 23:58:25 +02:00
30 changed files with 947 additions and 672 deletions

View File

@@ -1,4 +1,52 @@
### ### 4.5
- opening a URL manually no longer uses a input box but uses the clipboard directly
- the manifest was missing the company attribute which caused
mpv.net not appearing in the 'Open with' menu of the Windows File Explorer,
thanks to 44vince44 for pointing this out!!!
- new Python and C# script examples were added to the wiki and the scripting and
add-on documentation was improved
- invalid command line arguments were ignored, now an error message is shown
- a description on how to start mpv.net from Google Chrome was added to the
manual, it's useful to play videos from sites like YouTube, find the
description [here](https://github.com/stax76/mpv.net/blob/master/Manual.md#chrome-extension)
- new config setting remember-height added to remember the window height,
otherwise the video's native resolution is used
- support for protocols other then http added
### 4.4
- clipboard-monitoring was replaced by url-whitelist:
Keyword whitelist to monitor the clipboard for URLs to play.
Default: tube vimeo ard zdf
- some settings like colors didn't work because enclosing quotes were missing
- when single process queue is used the folder is no longer loaded
- the playlist is never cleared whenever the control key is down but
files and URLs are appended instead
- powershell script hosting bugs were fixed and a new powershell example script
was added to the [scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting#powershell)
- the menu entry for the command palette was renamed to 'Show All Commands' and
the default key binding was changed to F1 which is also the default in VS Code
- the default key binding of the Everything media search was changed to F3
- support for the mpv property 'border' was added to the config editor
to show/hide the window decoration (titlebar, border). A toggle menu item and
key binding (b) was added as well ([Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt#L135))
### 4.3.1
- there was a old bug setting the screen property
### 4.3
- there was new bug in file association feature
### 4.2
- the help and layout in the config editor was improved
- clipboard monitoring for URLs can be disabled in the settings
- the context menu has a new feature: Open > Add files to playlist,
it appends files to the playlist [(Default binding)](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt#L33)
- a setting was added to force using a single mpv.net process instance
### 4.1 ### 4.1

View File

@@ -7,6 +7,7 @@
* [Requirements](#requirements) * [Requirements](#requirements)
* [Installation](#installation) * [Installation](#installation)
+ [File Associations](#file-associations) + [File Associations](#file-associations)
+ [Chrome Extension](#chrome-extension)
* [Context Menu](#context-menu) * [Context Menu](#context-menu)
+ [Open > Open Files](#open--open-files) + [Open > Open Files](#open--open-files)
+ [Open > Open URL](#open--open-url) + [Open > Open URL](#open--open-url)
@@ -93,9 +94,9 @@
## About mpv.net ## About mpv.net
mpv.net is a media player for Windows. Similar like VLC mpv.net is not based on DirectShow like MPC, mpv.net is based on libmpv which in return is based on ffmpeg. mpv.net is a modern media player for Windows. mpv is similar to VLC not based on DirectShow like MPC, mpv.net is based on libmpv which in return is based on ffmpeg.
libmpv provides the majority of the features of the mpv media player, a fork of mplayer. mpv focuses on the usage of the command line interface, mpv.net retains the ability to be used from the command line and adds a simple and easy to use GUI on top of it. libmpv provides the majority of the features of the mpv media player, a fork of mplayer. mpv focuses on the usage of the command line interface, mpv.net retains the ability to be used from the command line and adds a modern GUI on top of it.
mpv.net is meant to be a small single person project, it's designed to be mpv compatible, can use the same settings as mpv and offers almost all mpv features because they are all contained in libmpv, this means the official [mpv manual](https://mpv.io/manual/master/) fully applies to mpv.net. mpv.net is meant to be a small single person project, it's designed to be mpv compatible, can use the same settings as mpv and offers almost all mpv features because they are all contained in libmpv, this means the official [mpv manual](https://mpv.io/manual/master/) fully applies to mpv.net.
@@ -119,7 +120,7 @@ If you are new to mpv.net the portable download is recommended, for regular mpv.
The x64 editions require more memory and have the advantage of decoders being typically first and better optimized for x64. The x64 editions require more memory and have the advantage of decoders being typically first and better optimized for x64.
x86 should though still be well supported and work. x86 should though still be well supported and work but it's not as well tested than x64.
### File Associations ### File Associations
@@ -129,6 +130,16 @@ Windows 10 prevents apps to register as the default app, to define the default v
It's also possible to change the default application using the Open With feature of the context menu in Windows File Explorer. It's also possible to change the default application using the Open With feature of the context menu in Windows File Explorer.
### Chrome Extension
In order to play videos from sites such as YouTube the Chrome Extension [Play with mpv](https://chrome.google.com/webstore/detail/play-with-mpv/hahklcmnfgffdlchjigehabfbiigleji) can be used.
Due to Chrome Extensions not being able to start a app, another app that communicates with the extension is required, this app can be downloaded [here](http://www.mediafire.com/file/6tmwdjsfknhmsxy/play-with-mpvnet-server.7z/file). The extension works only when the app is running, to have the app always running a link can be created in the auto start folder located at:
`C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
This will start the app on system start and have it running in the background. When the file association registration of mpv.net was executed then the app should find the location of mpv.net, alternativly the mpv.net folder can be added to the PATH environment variable.
## Context Menu ## Context Menu
The most important part of the user interface in mpv.net is the context menu, the menu can be customized as it is defined in the same file where the key bindings are defined. The most important part of the user interface in mpv.net is the context menu, the menu can be customized as it is defined in the same file where the key bindings are defined.
@@ -141,11 +152,15 @@ Another way to open files is the command line, it is used by the Windows File Ex
A third way is to drag and drop files on the main window. A third way is to drag and drop files on the main window.
Whenever the control key is pressed when files are opened, the playlist is not cleared but the files are appended to the playlist.
### Open > Open URL ### Open > Open URL
The Open URL menu entry can be used to open URLs for example from YouTube. The Open URL menu entry can be used to open URLs for example from YouTube.
mpv.net monitors the Windows clipboard and ask if URLs should be played in case it finds a URL in the clipboard. mpv.net monitors the Windows clipboard and ask if URLs should be played in case it finds a URL in the clipboard. This feature uses a keyword whitelist that can be configured in the config editor.
Whenever the control key is pressed when URLs are opened, the playlist is not cleared but the URLs are appended to the playlist.
### Open > Show media search ### Open > Show media search

View File

@@ -6,11 +6,11 @@ mpv.net is a modern media player for Windows that works just like [mpv](https://
#### Based on libmpv #### Based on libmpv
mpv.net is based on libmpv which offers a straightforward C API that was designed from the ground up to make mpv usable as a library and facilitate easy integration into other applications. mpv.net is based on libmpv which offers a straightforward C API that was designed from the ground up to make mpv usable as a library and facilitate easy integration into other applications. mpv is like vlc not based on DirectShow or Media Foundation.
#### Command Line Interface #### Command Line Interface
mpv.net has the CLI of mpv: [CLI switches](https://mpv.io/manual/master/#options). mpv.net has the [CLI of mpv](https://mpv.io/manual/master/#options).
#### High quality video output #### High quality video output
@@ -46,15 +46,15 @@ Table of contents
### Features ### Features
- Customizable context menu defined in the same file as the key bindings ([Screenshot](#context-menu-screenshot)) - Customizable context menu defined in the same file as the key bindings ([Screenshot](#context-menu-screenshot), [Defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt))
- Searchable config dialog ([Screenshot](#config-editor-screenshot)) - Searchable config dialog ([Screenshot](#config-editor-screenshot), [Defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt))
- Searchable input (key/mouse) binding editor ([Screenshot](#input-editor-screenshot)) - Searchable input (key/mouse) binding editor ([Screenshot](#input-editor-screenshot), [Defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt))
- Searchable command palette to quickly launch commands and look for keys ([Screenshot](#command-palette-screenshot)) - Searchable command palette to quickly launch commands and look for keys ([Screenshot](#command-palette-screenshot))
- Modern UI with dark mode ([Screenshot](#config-editor-screenshot)) - Modern UI with dark mode ([Screenshot](#config-editor-screenshot))
- Addon/extension API for .NET languages - Addon/extension API for .NET languages
- Scripting API for Python, C#, Lua, JavaScript and PowerShell ([wiki](https://github.com/stax76/mpv.net/wiki/Scripting)) - Scripting API for Python, C#, Lua, JavaScript and PowerShell ([wiki](https://github.com/stax76/mpv.net/wiki/Scripting))
- mpv's OSC, IPC and conf files - mpv's OSC, IPC and conf files
- Support of the same [CLI options](https://mpv.io/manual/master/#options) as mpv - [Command Line Interface](https://mpv.io/manual/master/#options)
- DXVA2 video decoding acceleration - DXVA2 video decoding acceleration
- OpenGL based video output capable of features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more - OpenGL based video output capable of features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more
- Search feature powered by [Everything](https://www.voidtools.com) to find and play media ([Screenshot](#media-search-screenshot)) - Search feature powered by [Everything](https://www.voidtools.com) to find and play media ([Screenshot](#media-search-screenshot))
@@ -139,6 +139,8 @@ if it's missing mpv.net generates it with the following defaults:
<https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt> <https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt>
mpv.net supports almost all mpv settings and features, [limitations are listed in the wiki](https://github.com/stax76/mpv.net/wiki/Limitations).
### Scripting ### Scripting
[Scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting) [Scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting)
@@ -188,6 +190,10 @@ Third party components:
[Issue tracker to report bugs and request features](https://github.com/stax76/mpv.net/issues) [Issue tracker to report bugs and request features](https://github.com/stax76/mpv.net/issues)
[frank.skare.de@gmail.com](mailto:frank.skare.de@gmail.com?Subject=mpv.net%20support)
Please click on the star at the top of the page and like mpv.net at [alternativeto.net](https://alternativeto.net/software/mpv-net/).
### Links ### Links
mpv manual: <https://mpv.io/manual/master/> mpv manual: <https://mpv.io/manual/master/>

View File

@@ -1,4 +1,7 @@
Imports System.ComponentModel.Composition 'this addon implements the C# scripting feature of mpv.net,
'it is based on CS-Script: https://www.cs-script.net/
Imports System.ComponentModel.Composition
Imports System.IO Imports System.IO
Imports System.Windows.Forms Imports System.Windows.Forms
@@ -13,8 +16,8 @@ Public Class CSScriptAddon
Sub New() Sub New()
Dim scriptFiles As New List(Of String) Dim scriptFiles As New List(Of String)
If Directory.Exists(mp.MpvConfFolder + "scripts") Then If Directory.Exists(mp.ConfFolder + "scripts") Then
scriptFiles.AddRange(Directory.GetFiles(mp.MpvConfFolder + "scripts", "*.cs")) scriptFiles.AddRange(Directory.GetFiles(mp.ConfFolder + "scripts", "*.cs"))
End If End If
If Directory.Exists(Application.StartupPath + "\scripts") Then If Directory.Exists(Application.StartupPath + "\scripts") Then

View File

@@ -1,4 +1,7 @@
using System; // this addon writes a rating to the filename of the current video,
// the input.conf defaults contain key bindings for this addon
using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -14,11 +17,11 @@ namespace RatingAddon
public RatingAddon() public RatingAddon()
{ {
mp.ClientMessage += mpv_ClientMessage; mp.ClientMessage += ClientMessage; //handles keys defined in input.conf
mp.Shutdown += mpv_Shutdown; mp.Shutdown += Shutdown;
} }
void mpv_Shutdown() void Shutdown()
{ {
foreach (var i in Dic) foreach (var i in Dic)
{ {
@@ -44,13 +47,11 @@ namespace RatingAddon
} }
} }
void mpv_ClientMessage(string[] args) //handles keys defined in input.conf
void ClientMessage(string[] args)
{ {
int rating; if (args[0] != "rate-file" || ! int.TryParse(args[1], out int rating))
if (args?.Length != 2 || args[0] != "rate-file" || ! int.TryParse(args[1], out rating))
return; return;
Dic[mp.get_property_string("path")] = rating; Dic[mp.get_property_string("path")] = rating;
mp.commandv("show-text", $"Rating: {rating}"); mp.commandv("show-text", $"Rating: {rating}");
} }

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Navigation; using System.Windows.Navigation;
@@ -44,11 +45,11 @@ namespace DynamicGUI
optionSetting.Options.Add(opt); optionSetting.Options.Add(opt);
} }
} }
else if (setting["default"].IsString) else
{ {
StringSetting stringSetting = new StringSetting(); StringSetting stringSetting = new StringSetting();
baseSetting = stringSetting; baseSetting = stringSetting;
stringSetting.Default = setting["default"]; stringSetting.Default = setting.HasKey("default") ? setting["default"].ToString() : "";
} }
baseSetting.Name = setting["name"]; baseSetting.Name = setting["name"];
@@ -102,13 +103,15 @@ namespace DynamicGUI
set => _Text = value; set => _Text = value;
} }
public bool IsChecked public bool Checked
{ {
get => OptionSetting.Value == Name ; get => OptionSetting.Value == Name ;
set { set { if (value) OptionSetting.Value = Name; }
if (value) }
OptionSetting.Value = Name;
} public Visibility Visibility
{
get => string.IsNullOrEmpty(Help) ? Visibility.Collapsed : Visibility.Visible;
} }
} }

View File

@@ -13,8 +13,8 @@
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<WrapPanel Orientation="Vertical"> <WrapPanel Orientation="Vertical">
<RadioButton x:Name="RadioButton" VerticalContentAlignment="Center" IsChecked="{Binding IsChecked}" GroupName="{Binding OptionSetting.Name}" Content="{Binding Text}" FontSize="16" FontWeight="Normal" VerticalAlignment="Top" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></RadioButton> <RadioButton x:Name="RadioButton" VerticalContentAlignment="Center" IsChecked="{Binding Checked}" GroupName="{Binding OptionSetting.Name}" Content="{Binding Text}" FontSize="16" FontWeight="Normal" VerticalAlignment="Top" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></RadioButton>
<TextBox x:Name="ItemHelpTextBox" TextWrapping="WrapWithOverflow" Text="{Binding Help}" Margin="10,0,0,0" BorderThickness="0" IsReadOnly="True" Padding="7,0,0,0" MinHeight="0" Foreground="{Binding Path=Foreground2, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox> <TextBox x:Name="ItemHelpTextBox" TextWrapping="WrapWithOverflow" Text="{Binding Help}" Visibility="{Binding Visibility}" Margin="10,0,0,0" BorderThickness="0" IsReadOnly="True" Padding="7,0,0,0" MinHeight="0" Foreground="{Binding Path=Foreground2, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
</WrapPanel> </WrapPanel>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View File

@@ -12,11 +12,13 @@ namespace DynamicGUI
OptionSetting = optionSetting; OptionSetting = optionSetting;
InitializeComponent(); InitializeComponent();
TitleTextBox.Text = optionSetting.Name; TitleTextBox.Text = optionSetting.Name;
if (string.IsNullOrEmpty(optionSetting.Help))
HelpTextBox.Visibility = Visibility.Collapsed;
HelpTextBox.Text = optionSetting.Help; HelpTextBox.Text = optionSetting.Help;
ItemsControl.ItemsSource = optionSetting.Options; ItemsControl.ItemsSource = optionSetting.Options;
Link.SetURL(optionSetting.HelpURL);
if (string.IsNullOrEmpty(optionSetting.HelpURL)) if (string.IsNullOrEmpty(optionSetting.HelpURL))
LinkTextBlock.Visibility = Visibility.Collapsed; LinkTextBlock.Visibility = Visibility.Collapsed;
Link.SetURL(optionSetting.HelpURL);
} }
private string _SearchableText; private string _SearchableText;

View File

@@ -18,7 +18,7 @@
<TextBox x:Name="ValueTextBox" Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="150" HorizontalAlignment="Left" Height="20" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" TextChanged="ValueTextBox_TextChanged"/> <TextBox x:Name="ValueTextBox" Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="150" HorizontalAlignment="Left" Height="20" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" TextChanged="ValueTextBox_TextChanged"/>
<Button x:Name="Button" Height="20" Grid.Column="1" Visibility="{Binding Path=Text, ElementName=StringSettingControl1}" Margin="5,0,0,0" Width="20" Click="Button_Click">...</Button> <Button x:Name="Button" Height="20" Grid.Column="1" Visibility="{Binding Path=Text, ElementName=StringSettingControl1}" Margin="5,0,0,0" Width="20" Click="Button_Click">...</Button>
</Grid> </Grid>
<TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" Margin="0,0,0,10" BorderThickness="0" IsReadOnly="True" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox> <TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" BorderThickness="0" IsReadOnly="True" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
<TextBlock x:Name="LinkTextBlock" Margin="0,10"> <TextBlock x:Name="LinkTextBlock" Margin="0,10">
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx> <local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
</TextBlock> </TextBlock>

View File

@@ -64,7 +64,8 @@ namespace DynamicGUI
{ {
d.FullOpen = true; d.FullOpen = true;
try { try {
d.Color = System.Drawing.ColorTranslator.FromHtml(ValueTextBox.Text); Color col = GetColor(ValueTextBox.Text);
d.Color = System.Drawing.Color.FromArgb(col.A, col.R, col.G, col.B);
} catch { } } catch { }
if (d.ShowDialog() == WinForms.DialogResult.OK) if (d.ShowDialog() == WinForms.DialogResult.OK)
ValueTextBox.Text = System.Drawing.ColorTranslator.ToHtml(d.Color); ValueTextBox.Text = System.Drawing.ColorTranslator.ToHtml(d.Color);
@@ -75,29 +76,31 @@ namespace DynamicGUI
private void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update(); private void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update();
Color GetColor(string value)
{
if (value.Contains("/"))
{
string[] a = value.Split('/');
if (a.Length == 3)
return Color.FromRgb(ToByte(a[0]), ToByte(a[1]), ToByte(a[2]));
else if (a.Length == 4)
return Color.FromArgb(ToByte(a[3]), ToByte(a[0]), ToByte(a[1]), ToByte(a[2]));
}
return (Color)ColorConverter.ConvertFromString(value);
Byte ToByte(string val) => Convert.ToByte(Convert.ToSingle(val, CultureInfo.InvariantCulture) * 255);
}
public void Update() public void Update()
{ {
if (StringSetting.Type == "color") if (StringSetting.Type == "color")
{ {
Color c = Colors.Transparent; Color c = Colors.Transparent;
if (ValueTextBox.Text != "") if (ValueTextBox.Text != "") try { c = GetColor(ValueTextBox.Text); } catch {}
{
try {
if (ValueTextBox.Text.Contains("/"))
{
string[] a = ValueTextBox.Text.Split('/');
if (a.Length == 3)
c = Color.FromRgb(ToByte(a[0]), ToByte(a[1]), ToByte(a[2]));
else if (a.Length == 4)
c = Color.FromArgb(ToByte(a[3]), ToByte(a[0]), ToByte(a[1]), ToByte(a[2]));
}
else
c = (Color)ColorConverter.ConvertFromString(ValueTextBox.Text);
} catch {}
}
ValueTextBox.Background = new SolidColorBrush(c); ValueTextBox.Background = new SolidColorBrush(c);
} }
Byte ToByte(string val) => Convert.ToByte(Convert.ToSingle(val, CultureInfo.InvariantCulture) * 255);
} }
} }
} }

View File

@@ -26,7 +26,7 @@ namespace mpvnet
foreach (string i in Directory.GetDirectories(dir)) foreach (string i in Directory.GetDirectories(dir))
catalog.Catalogs.Add(new DirectoryCatalog(i, "*Addon.dll")); catalog.Catalogs.Add(new DirectoryCatalog(i, "*Addon.dll"));
dir = mp.MpvConfFolder + "\\Addons"; dir = mp.ConfFolder + "\\Addons";
if (Directory.Exists(dir)) if (Directory.Exists(dir))
foreach (string i in Directory.GetDirectories(dir)) foreach (string i in Directory.GetDirectories(dir))

View File

@@ -7,6 +7,8 @@ using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Interop; using System.Windows.Interop;
using VB = Microsoft.VisualBasic;
namespace mpvnet namespace mpvnet
{ {
public class Command public class Command
@@ -30,10 +32,15 @@ namespace mpvnet
{ {
ParameterInfo[] parameters = i.GetParameters(); ParameterInfo[] parameters = i.GetParameters();
if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(string[])) if (parameters == null ||
parameters.Length != 1 ||
parameters[0].ParameterType != typeof(string[]))
continue; continue;
Command cmd = new Command() { Name = i.Name.Replace("_","-"), Action = (Action<string[]>)i.CreateDelegate(typeof(Action<string[]>)) }; Command cmd = new Command() {
Name = i.Name.Replace("_", "-"),
Action = (Action<string[]>)i.CreateDelegate(typeof(Action<string[]>)) };
commands.Add(cmd); commands.Add(cmd);
} }
} }
@@ -43,16 +50,36 @@ namespace mpvnet
public static void open_files(string[] args) public static void open_files(string[] args)
{ {
bool append = Control.ModifierKeys.HasFlag(Keys.Control);
bool loadFolder = true;
foreach (string arg in args)
{
if (arg == "append") append = true;
if (arg == "no-folder") loadFolder = false;
}
MainForm.Instance.Invoke(new Action(() => { MainForm.Instance.Invoke(new Action(() => {
using (var d = new OpenFileDialog() { Multiselect = true }) using (var d = new OpenFileDialog() { Multiselect = true })
if (d.ShowDialog() == DialogResult.OK) if (d.ShowDialog() == DialogResult.OK)
mp.LoadFiles(d.FileNames); mp.Load(d.FileNames, loadFolder, append);
}));
}
// deprecated in 2019
public static void add_files_to_playlist(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
using (var d = new OpenFileDialog() { Multiselect = true })
if (d.ShowDialog() == DialogResult.OK)
foreach (string file in d.FileNames)
mp.commandv("loadfile", file, "append");
})); }));
} }
public static void open_conf_folder(string[] args) public static void open_conf_folder(string[] args)
{ {
Process.Start(mp.MpvConfFolder); Process.Start(mp.ConfFolder);
} }
public static void show_input_editor(string[] args) public static void show_input_editor(string[] args)
@@ -102,7 +129,7 @@ namespace mpvnet
public static void show_history(string[] args) public static void show_history(string[] args)
{ {
var fp = mp.MpvConfFolder + "history.txt"; var fp = mp.ConfFolder + "history.txt";
if (File.Exists(fp)) if (File.Exists(fp))
Process.Start(fp); Process.Start(fp);
@@ -172,17 +199,18 @@ namespace mpvnet
mp.commandv("show-text", text, "5000"); mp.commandv("show-text", text, "5000");
string FormatTime(double value) => ((int)value).ToString("00"); string FormatTime(double value) => ((int)value).ToString("00");
} }
catch (Exception) catch (Exception e)
{ {
Msg.ShowException(e);
} }
} }
public static void execute_mpv_command(string[] args) public static void execute_mpv_command(string[] args)
{ {
MainForm.Instance.Invoke(new Action(() => { MainForm.Instance.Invoke(new Action(() => {
string command = Microsoft.VisualBasic.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegistryHelp.GetString("HKCU\\Software\\" + Application.ProductName, "RecentExecutedCommand")); string command = VB.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegHelp.GetString(App.RegPath, "RecentExecutedCommand"));
if (string.IsNullOrEmpty(command)) return; if (string.IsNullOrEmpty(command)) return;
RegistryHelp.SetObject("HKCU\\Software\\" + Application.ProductName, "RecentExecutedCommand", command); RegHelp.SetObject(App.RegPath, "RecentExecutedCommand", command);
mp.command_string(command, false); mp.command_string(command, false);
})); }));
} }
@@ -190,9 +218,13 @@ namespace mpvnet
public static void open_url(string[] args) public static void open_url(string[] args)
{ {
MainForm.Instance.Invoke(new Action(() => { MainForm.Instance.Invoke(new Action(() => {
string command = Microsoft.VisualBasic.Interaction.InputBox("Enter URL to be opened."); string clipboard = Clipboard.GetText();
if (string.IsNullOrEmpty(command)) return; if (string.IsNullOrEmpty(clipboard) || (!clipboard.Contains("://") && !File.Exists(clipboard)) || clipboard.Contains("\n"))
mp.LoadFiles(command); {
Msg.ShowError("The clipboard does not contain a valid URL or file, URLs have to contain :// and is not allowed to contain a newline character.");
return;
}
mp.Load(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
})); }));
} }

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -17,14 +18,108 @@ namespace mpvnet
{ {
public class App public class App
{ {
public static string ConfFilePath { get; } = mp.ConfFolder + "\\mpvnet.conf";
public static string RegPath { get; } = @"HKCU\Software\" + Application.ProductName;
public static string DarkMode { get; set; } = "always";
public static string ProcessInstance { get; set; } = "single";
public static string[] VideoTypes { get; } = "mkv mp4 mpg avi mov webm vob wmv flv avs 264 h264 asf webm mpeg mpv y4m avc hevc 265 h265 m2v m2ts vpy mts m4v".Split(' '); public static string[] VideoTypes { get; } = "mkv mp4 mpg avi mov webm vob wmv flv avs 264 h264 asf webm mpeg mpv y4m avc hevc 265 h265 m2v m2ts vpy mts m4v".Split(' ');
public static string[] AudioTypes { get; } = "mp3 mp2 ac3 ogg opus flac wav w64 m4a dts dtsma dtshr dtshd eac3 thd thd+ac3 mka aac mpa".Split(' '); public static string[] AudioTypes { get; } = "mp3 mp2 ac3 ogg opus flac wav w64 m4a dts dtsma dtshr dtshd eac3 thd thd+ac3 mka aac mpa".Split(' ');
public static string[] SubtitleTypes { get; } = "srt ass idx sup ttxt ssa smi".Split(' '); public static string[] SubtitleTypes { get; } = "srt ass idx sup ttxt ssa smi".Split(' ');
public static string[] UrlWhitelist { get; set; } = { "tube", "vimeo", "ard", "zdf" };
public static bool DebugMode { get; set; } = false;
public static bool IsDarkMode { public static bool IsDarkMode {
get => (DarkMode == "system" && Sys.IsDarkTheme) || DarkMode == "always";
}
public static void Init()
{
foreach (var i in Conf)
ProcessProperty(i.Key, i.Value);
if (App.DebugMode)
{
try
{
string filePath = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory) + "\\mpvnet-debug.log";
if (File.Exists(filePath)) File.Delete(filePath);
Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener(filePath));
foreach (Screen screen in Screen.AllScreens)
Trace.WriteLine(screen);
}
catch (Exception e)
{
Msg.ShowException(e);
}
}
}
public static void Exit()
{
if (Trace.Listeners.Count > 0) Trace.Listeners[0].Close();
}
static Dictionary<string, string> _Conf;
public static Dictionary<string, string> Conf {
get { get {
string darkMode = MainForm.Instance.MpvNetDarkMode; if (_Conf == null)
return (darkMode == "system" && Sys.IsDarkTheme) || darkMode == "always"; {
_Conf = new Dictionary<string, string>();
if (File.Exists(ConfFilePath))
foreach (string i in File.ReadAllLines(ConfFilePath))
if (i.Contains("=") && !i.StartsWith("#"))
_Conf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
}
return _Conf;
}
}
public static void ProcessProperty(string name, string value)
{
switch (name)
{
case "process-instance": ProcessInstance = value; break;
case "dark-mode": DarkMode = value; break;
case "debug-mode": DebugMode = value == "yes"; break;
case "url-whitelist":
UrlWhitelist = value.Split(' ', ',', ';');
break;
}
}
public static void ProcessCommandLineEarly()
{
var args = Environment.GetCommandLineArgs().Skip(1);
foreach (string i in args)
{
if (i.StartsWith("--"))
{
if (i.Contains("="))
{
string left = i.Substring(2, i.IndexOf("=") - 2);
string right = i.Substring(left.Length + 3);
mp.ProcessProperty(left, right);
ProcessProperty(left, right);
}
else
{
string switchName = i.Substring(2);
switch (switchName)
{
case "fs":
case "fullscreen":
mp.Fullscreen = true;
break;
}
}
}
} }
} }
} }
@@ -76,49 +171,49 @@ namespace mpvnet
{ {
Types = types; Types = types;
RegistryHelp.SetObject(@"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename, null, ExePath); RegHelp.SetObject(@"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename, null, ExePath);
RegistryHelp.SetObject($"HKCR\\Applications\\{ExeFilename}", "FriendlyAppName", "mpv.net media player"); RegHelp.SetObject($"HKCR\\Applications\\{ExeFilename}", "FriendlyAppName", "mpv.net media player");
RegistryHelp.SetObject($"HKCR\\Applications\\{ExeFilename}\\shell\\open\\command", null, $"\"{ExePath}\" \"%1\""); RegHelp.SetObject($"HKCR\\Applications\\{ExeFilename}\\shell\\open\\command", null, $"\"{ExePath}\" \"%1\"");
RegistryHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv\Capabilities", "ApplicationDescription", "mpv.net media player"); RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv\Capabilities", "ApplicationDescription", "mpv.net media player");
RegistryHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv\Capabilities", "ApplicationName", "mpv.net"); RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv\Capabilities", "ApplicationName", "mpv.net");
RegistryHelp.SetObject($"HKCR\\SystemFileAssociations\\video\\OpenWithList\\{ExeFilename}", null, ""); RegHelp.SetObject($"HKCR\\SystemFileAssociations\\video\\OpenWithList\\{ExeFilename}", null, "");
RegistryHelp.SetObject($"HKCR\\SystemFileAssociations\\audio\\OpenWithList\\{ExeFilename}", null, ""); RegHelp.SetObject($"HKCR\\SystemFileAssociations\\audio\\OpenWithList\\{ExeFilename}", null, "");
foreach (string ext in Types) foreach (string ext in Types)
{ {
RegistryHelp.SetObject($"HKCR\\Applications\\{ExeFilename}\\SupportedTypes", "." + ext, ""); RegHelp.SetObject($"HKCR\\Applications\\{ExeFilename}\\SupportedTypes", "." + ext, "");
RegistryHelp.SetObject($"HKCR\\" + "." + ext, null, ExeFilenameNoExt + "." + ext); RegHelp.SetObject($"HKCR\\" + "." + ext, null, ExeFilenameNoExt + "." + ext);
RegistryHelp.SetObject($"HKCR\\" + "." + ext + "\\OpenWithProgIDs", ExeFilenameNoExt + "." + ext, ""); RegHelp.SetObject($"HKCR\\" + "." + ext + "\\OpenWithProgIDs", ExeFilenameNoExt + "." + ext, "");
if (App.VideoTypes.Contains(ext)) if (App.VideoTypes.Contains(ext))
RegistryHelp.SetObject($"HKCR\\" + "." + ext, "PerceivedType", "video"); RegHelp.SetObject($"HKCR\\" + "." + ext, "PerceivedType", "video");
if (App.AudioTypes.Contains(ext)) if (App.AudioTypes.Contains(ext))
RegistryHelp.SetObject($"HKCR\\" + "." + ext, "PerceivedType", "audio"); RegHelp.SetObject($"HKCR\\" + "." + ext, "PerceivedType", "audio");
RegistryHelp.SetObject($"HKCR\\" + ExeFilenameNoExt + "." + ext + "\\shell\\open", null, "Play with " + Application.ProductName); RegHelp.SetObject($"HKCR\\" + ExeFilenameNoExt + "." + ext + "\\shell\\open", null, "Play with " + Application.ProductName);
RegistryHelp.SetObject($"HKCR\\" + ExeFilenameNoExt + "." + ext + "\\shell\\open\\command", null, $"\"{ExePath}\" \"%1\""); RegHelp.SetObject($"HKCR\\" + ExeFilenameNoExt + "." + ext + "\\shell\\open\\command", null, $"\"{ExePath}\" \"%1\"");
RegistryHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities\FileAssociations", "." + ext, ExeFilenameNoExt + "." + ext); RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities\FileAssociations", "." + ext, ExeFilenameNoExt + "." + ext);
} }
} }
public static void Unregister() public static void Unregister()
{ {
RegistryHelp.RemoveKey(@"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename); RegHelp.RemoveKey(@"HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename);
RegistryHelp.RemoveKey($"HKCR\\Applications\\{ExeFilename}"); RegHelp.RemoveKey($"HKCR\\Applications\\{ExeFilename}");
RegistryHelp.RemoveKey(@"HKLM\SOFTWARE\Clients\Media\mpv.net"); RegHelp.RemoveKey(@"HKLM\SOFTWARE\Clients\Media\mpv.net");
RegistryHelp.RemoveKey($"HKCR\\SystemFileAssociations\\video\\OpenWithList\\{ExeFilename}"); RegHelp.RemoveKey($"HKCR\\SystemFileAssociations\\video\\OpenWithList\\{ExeFilename}");
RegistryHelp.RemoveKey($"HKCR\\SystemFileAssociations\\audio\\OpenWithList\\{ExeFilename}"); RegHelp.RemoveKey($"HKCR\\SystemFileAssociations\\audio\\OpenWithList\\{ExeFilename}");
foreach (string id in Registry.ClassesRoot.GetSubKeyNames()) foreach (string id in Registry.ClassesRoot.GetSubKeyNames())
{ {
if (id.StartsWith(ExeFilenameNoExt + ".")) if (id.StartsWith(ExeFilenameNoExt + "."))
Registry.ClassesRoot.DeleteSubKeyTree(id); Registry.ClassesRoot.DeleteSubKeyTree(id);
RegistryHelp.RemoveValue($"HKCR\\Software\\Classes\\" + id + "\\OpenWithProgIDs", ExeFilenameNoExt + id); RegHelp.RemoveValue($"HKCR\\Software\\Classes\\" + id + "\\OpenWithProgIDs", ExeFilenameNoExt + id);
RegistryHelp.RemoveValue($"HKLM\\Software\\Classes\\" + id + "\\OpenWithProgIDs", ExeFilenameNoExt + id); RegHelp.RemoveValue($"HKLM\\Software\\Classes\\" + id + "\\OpenWithProgIDs", ExeFilenameNoExt + id);
} }
} }
} }
public class RegistryHelp public class RegHelp
{ {
public static void SetObject(string path, string name, object value) public static void SetObject(string path, string name, object value)
{ {
@@ -144,14 +239,18 @@ namespace mpvnet
public static void RemoveKey(string path) public static void RemoveKey(string path)
{ {
GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false); try {
GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false);
} catch { }
} }
public static void RemoveValue(string path, string name) public static void RemoveValue(string path, string name)
{ {
using (RegistryKey rk = GetRootKey(path).OpenSubKey(path.Substring(5), true)) try {
if (!(rk is null)) using (RegistryKey rk = GetRootKey(path).OpenSubKey(path.Substring(5), true))
rk.DeleteValue(name, false); if (!(rk is null))
rk.DeleteValue(name, false);
} catch {}
} }
static RegistryKey GetRootKey(string path) static RegistryKey GetRootKey(string path)
@@ -294,4 +393,15 @@ namespace mpvnet
Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10; Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10;
} }
} }
public class SingleProcess
{
public static int Message { get; } = RegisterWindowMessage("mpvnet_IPC");
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int RegisterWindowMessage(string id);
[DllImport("user32.dll")]
public static extern bool AllowSetForegroundWindow(int dwProcessId);
}
} }

View File

@@ -1,5 +1,10 @@
using System; using System;
using System.Windows.Forms; using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Diagnostics;
namespace mpvnet namespace mpvnet
{ {
@@ -10,19 +15,48 @@ namespace mpvnet
{ {
try try
{ {
string[] args = Environment.GetCommandLineArgs(); string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();
if (args.Length == 3 && args[1] == "--reg-file-assoc") if (args.Length == 2 && args[0] == "--reg-file-assoc")
{ {
if (args[2] == "audio") FileAssociation.Register(App.AudioTypes); if (args[1] == "audio") FileAssociation.Register(App.AudioTypes);
if (args[2] == "video") FileAssociation.Register(App.VideoTypes); if (args[1] == "video") FileAssociation.Register(App.VideoTypes);
if (args[2] == "unreg") FileAssociation.Unregister(); if (args[1] == "unreg") FileAssociation.Unregister();
return;
}
App.Init();
Mutex mutex = new Mutex(true, "mpvnetProcessInstance", out bool isFirst);
if ((App.ProcessInstance == "single" || App.ProcessInstance == "queue") && !isFirst)
{
List<string> files = new List<string>();
foreach (string arg in args)
if (!arg.StartsWith("--") && (arg == "-" || arg.Contains("://") || File.Exists(arg)))
files.Add(arg);
if (files.Count > 0)
RegHelp.SetObject(App.RegPath, "ShellFiles", files.ToArray());
RegHelp.SetObject(App.RegPath, "ProcessInstanceMode", App.ProcessInstance);
foreach(Process process in Process.GetProcessesByName("mpvnet"))
{
try {
SingleProcess.AllowSetForegroundWindow(process.Id);
Native.SendMessage(process.MainWindowHandle, SingleProcess.Message, IntPtr.Zero, IntPtr.Zero);
} catch {}
}
mutex.Dispose();
return; return;
} }
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm()); Application.Run(new MainForm());
mutex.Dispose();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -8,9 +8,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("mpv.net")] [assembly: AssemblyTitle("mpv.net")]
[assembly: AssemblyDescription("A modern media player")] [assembly: AssemblyDescription("A modern media player")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("Frank Skare (stax76)")]
[assembly: AssemblyProduct("mpv.net")] [assembly: AssemblyProduct("mpv.net")]
[assembly: AssemblyCopyright("Copyright © 2017-2019 stax76")] [assembly: AssemblyCopyright("Copyright © 2017-2019 Frank Skare (stax76)")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.1.0.0")] [assembly: AssemblyVersion("4.5.0.0")]
[assembly: AssemblyFileVersion("4.1.0.0")] [assembly: AssemblyFileVersion("4.5.0.0")]

View File

@@ -1,6 +1,24 @@
namespace mpvnet.Properties { //------------------------------------------------------------------------------
using System; // <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace mpvnet.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
@@ -13,7 +31,10 @@
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() { internal Resources() {
} }
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager { internal static global::System.Resources.ResourceManager ResourceManager {
get { get {
@@ -24,7 +45,11 @@
return resourceMan; return resourceMan;
} }
} }
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture { internal static global::System.Globalization.CultureInfo Culture {
get { get {
@@ -34,42 +59,103 @@
resourceCulture = value; resourceCulture = value;
} }
} }
/// <summary>
/// Looks up a localized string similar to
/// # This file defines the input (keys and mouse) bindings of mpv and mpv.net
/// # and it also defines the context menu of mpv.net. mpv.net has an input
/// # editor and an config editor as alternative to editing conf text files.
/// # The input and config editor can be found in mpv.net&apos;s context menu at:
///
/// # Settings &gt; Show Config Editor
/// # Settings &gt; Show Input Editor
///
/// # The defaults of this file can be found at:
///
/// # https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt
///
/// # th [rest of string was truncated]&quot;;.
/// </summary>
internal static string inputConf { internal static string inputConf {
get { get {
return ResourceManager.GetString("inputConf", resourceCulture); return ResourceManager.GetString("inputConf", resourceCulture);
} }
} }
internal static string inputConfHeader {
get {
return ResourceManager.GetString("inputConfHeader", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to
///# manual: https://mpv.io/manual/master/
///
///# defaults: https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt
///
///input-ar-delay = 500
///input-ar-rate = 20
///volume = 50
///hwdec = yes
///keep-open = yes
///keep-open-pause = no
///osd-playing-msg = &apos;${filename}&apos;
///screenshot-directory = &apos;~~desktop/&apos;
///input-default-bindings = no.
/// </summary>
internal static string mpvConf { internal static string mpvConf {
get { get {
return ResourceManager.GetString("mpvConf", resourceCulture); return ResourceManager.GetString("mpvConf", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized string similar to [[settings]]
///name = &quot;hwdec&quot;
///default = &quot;no&quot;
///filter = &quot;Video&quot;
///helpurl = &quot;https://mpv.io/manual/master/#options-hwdec&quot;
///help = &quot;Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:&quot;
///options = [{ name = &quot;no&quot;, help = &quot;always use software decoding&quot; },
/// { name = &quot;auto&quot;, help = &quot;enabl [rest of string was truncated]&quot;;.
/// </summary>
internal static string mpvConfToml { internal static string mpvConfToml {
get { get {
return ResourceManager.GetString("mpvConfToml", resourceCulture); return ResourceManager.GetString("mpvConfToml", resourceCulture);
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap mpvnet { internal static System.Drawing.Bitmap mpvnet {
get { get {
object obj = ResourceManager.GetObject("mpvnet", resourceCulture); object obj = ResourceManager.GetObject("mpvnet", resourceCulture);
return ((System.Drawing.Bitmap)(obj)); return ((System.Drawing.Bitmap)(obj));
} }
} }
/// <summary>
/// Looks up a localized string similar to [[settings]]
///name = &quot;dark-mode&quot;
///default = &quot;always&quot;
///filter = &quot;mpv.net&quot;
///help = &quot;Enables a dark theme.&quot;
///options = [{ name = &quot;always&quot; },
/// { name = &quot;system&quot; , help = &quot;Available on Windows 10 or higher&quot; },
/// { name = &quot;never&quot; }]
///
///[[settings]]
///name = &quot;url-whitelist&quot;
///filter = &quot;mpv.net&quot;
///type = &quot;string&quot;
///help = &quot;Whitelist to monitor the clipboard for URLs to play.\n\nDefault: tube vimeo ard zdf&quot;
///
///[[settings]]
///name = &quot;process-instance&quot;
///default = &quot;single&quot;
///filter = &quot;mpv.net&quot;
///help = &quot;Defines [rest of string was truncated]&quot;;.
/// </summary>
internal static string mpvNetConfToml { internal static string mpvNetConfToml {
get { get {
return ResourceManager.GetString("mpvNetConfToml", resourceCulture); return ResourceManager.GetString("mpvNetConfToml", resourceCulture);
} }
} }
} }
} }

View File

@@ -121,9 +121,6 @@
<data name="inputConf" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="inputConf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\inputConf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value> <value>..\Resources\inputConf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data> </data>
<data name="inputConfHeader" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\inputConfHeader.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="mpvConf" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="mpvConf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\mpvConf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> <value>..\Resources\mpvConf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data> </data>

View File

@@ -25,12 +25,14 @@
# mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys # mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys
o script-message mpv.net open-files #menu: Open > Open Files... o script-message mpv.net open-files #menu: Open > Open Files...
u script-message mpv.net open-url #menu: Open > Open URL... u script-message mpv.net open-url #menu: Open > Open URL or file path from clipboard
Ctrl+S script-message mpv.net show-media-search #menu: Open > Show media search...
_ ignore #menu: Open > - _ ignore #menu: Open > -
Alt+a script-message mpv.net load-audio #menu: Open > Load external audio files... Alt+a script-message mpv.net load-audio #menu: Open > Load external audio files...
Alt+s script-message mpv.net load-sub #menu: Open > Load external subtitle files... Alt+s script-message mpv.net load-sub #menu: Open > Load external subtitle files...
_ ignore #menu: Open > - _ ignore #menu: Open > -
_ script-message mpv.net open-files append #menu: Open > Add files to playlist...
F3 script-message mpv.net show-media-search #menu: Open > Show media search...
_ ignore #menu: Open > -
_ ignore #menu: Open > Recent _ ignore #menu: Open > Recent
_ ignore #menu: - _ ignore #menu: -
@@ -130,6 +132,7 @@
Ctrl+t set ontop yes #menu: View > On Top > Enable Ctrl+t set ontop yes #menu: View > On Top > Enable
Ctrl+T set ontop no #menu: View > On Top > Disable Ctrl+T set ontop no #menu: View > On Top > Disable
b cycle border #menu: View > Toggle Border
i script-message mpv.net show-info #menu: View > File Info i script-message mpv.net show-info #menu: View > File Info
t script-binding stats/display-stats #menu: View > Show Statistics t script-binding stats/display-stats #menu: View > Show Statistics
T script-binding stats/display-stats-toggle #menu: View > Toggle Statistics T script-binding stats/display-stats-toggle #menu: View > Toggle Statistics
@@ -137,15 +140,15 @@
F8 show-text ${playlist} 5000 #menu: View > Show Playlist F8 show-text ${playlist} 5000 #menu: View > Show Playlist
F9 show-text ${track-list} 5000 #menu: View > Show Audio/Video/Subtitle List F9 show-text ${track-list} 5000 #menu: View > Show Audio/Video/Subtitle List
Ctrl+c script-message mpv.net show-conf-editor #menu: Settings > Show Config Editor c script-message mpv.net show-conf-editor #menu: Settings > Show Config Editor
Ctrl+i script-message mpv.net show-input-editor #menu: Settings > Show Input Editor Ctrl+i script-message mpv.net show-input-editor #menu: Settings > Show Input Editor
Ctrl+f script-message mpv.net open-conf-folder #menu: Settings > Open Config Folder Ctrl+f script-message mpv.net open-conf-folder #menu: Settings > Open Config Folder
Ctrl+P script-message mpv.net show-command-palette #menu: Tools > Command Palette F1 script-message mpv.net show-command-palette #menu: Tools > Show All Commands
h script-message mpv.net show-history #menu: Tools > Show History h script-message mpv.net show-history #menu: Tools > Show History
l ab-loop #menu: Tools > Set/clear A-B loop points l ab-loop #menu: Tools > Set/clear A-B loop points
L cycle-values loop-file "inf" "no" #menu: Tools > Toggle infinite file looping L cycle-values loop-file "inf" "no" #menu: Tools > Toggle infinite file looping
Ctrl+h cycle-values hwdec "auto" "no" #menu: Tools > Cycle Hardware Decoding Ctrl+h cycle-values hwdec "auto" "no" #menu: Tools > Toggle Hardware Decoding
_ script-message mpv.net execute-mpv-command #menu: Tools > Execute mpv command... _ script-message mpv.net execute-mpv-command #menu: Tools > Execute mpv command...
_ script-message mpv.net manage-file-associations #menu: Tools > Manage File Associations... _ script-message mpv.net manage-file-associations #menu: Tools > Manage File Associations...
@@ -177,5 +180,6 @@
Wheel_Down add volume -10 Wheel_Down add volume -10
Prev playlist-prev Prev playlist-prev
Next playlist-next Next playlist-next
Ctrl+Wheel_Up no-osd seek 7
Ctrl+Wheel_Down no-osd seek -7
MBTN_LEFT_DBL cycle fullscreen MBTN_LEFT_DBL cycle fullscreen

View File

@@ -1,25 +0,0 @@
# This file defines the input (keys and mouse) bindings of mpv and mpv.net
# and it also defines the context menu of mpv.net. mpv.net has an input
# editor and an config editor as alternatives to editing conf text files.
# The input and config editor can be found in mpv.net's context menu at:
# Settings > Show Config Editor
# Settings > Show Input Editor
# The defaults of this file can be found at:
# https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt
# the defaults of mpv can be found at:
# https://github.com/mpv-player/mpv/blob/master/etc/inputConf
# mpv.net's defaults of mpv.conf contain: 'input-default-bindings = no'
# which disables mpv's input defaults. Every line in this file begins with a
# space character to make it easier to do a text search, so if you want to know
# if 'o' has already a binding you can make a text search on ' o '.
# mpv input commands: https://github.com/stax76/mpv.net/wiki/mpv-input-commands
# mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys

View File

@@ -1,7 +1,7 @@
# mpv manual: https://mpv.io/manual/master/ # manual: https://mpv.io/manual/master/
# mpv.net mpv.conf defaults: https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt # defaults: https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt
input-ar-delay = 500 input-ar-delay = 500
input-ar-rate = 20 input-ar-rate = 20
@@ -9,6 +9,6 @@ volume = 50
hwdec = yes hwdec = yes
keep-open = yes keep-open = yes
keep-open-pause = no keep-open-pause = no
osd-playing-msg = ${filename} osd-playing-msg = '${filename}'
screenshot-directory = ~~desktop/ screenshot-directory = '~~desktop/'
input-default-bindings = no input-default-bindings = no

View File

@@ -4,7 +4,7 @@ default = "no"
filter = "Video" filter = "Video"
helpurl = "https://mpv.io/manual/master/#options-hwdec" helpurl = "https://mpv.io/manual/master/#options-hwdec"
help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:" help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:"
options = [{ name = "no", help = "always use software decoding (Default)" }, options = [{ name = "no", help = "always use software decoding" },
{ name = "auto", help = "enable best hw decoder (see below)" }, { name = "auto", help = "enable best hw decoder (see below)" },
{ name = "yes", help = "exactly the same as auto" }, { name = "yes", help = "exactly the same as auto" },
{ name = "auto-copy", help = "enable best hw decoder with copy-back (see below)" }, { name = "auto-copy", help = "enable best hw decoder with copy-back (see below)" },
@@ -24,7 +24,7 @@ name = "gpu-api"
default = "auto" default = "auto"
filter = "Video" filter = "Video"
help = "Controls which type of graphics APIs will be accepted." help = "Controls which type of graphics APIs will be accepted."
options = [{ name = "auto", help = "Use any available API (Default)" }, options = [{ name = "auto", help = "Use any available API" },
{ name = "opengl", help = "Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+)" }, { name = "opengl", help = "Allow only OpenGL (requires OpenGL 2.1+ or GLES 2.0+)" },
{ name = "vulkan", help = "Allow only Vulkan (requires a valid/working spirv-compiler)" }, { name = "vulkan", help = "Allow only Vulkan (requires a valid/working spirv-compiler)" },
{ name = "d3d11", help = "Allow only gpu-context=d3d11" }] { name = "d3d11", help = "Allow only gpu-context=d3d11" }]
@@ -33,8 +33,7 @@ options = [{ name = "auto", help = "Use any available API (Default)" },
name = "gpu-context" name = "gpu-context"
default = "auto" default = "auto"
filter = "Video" filter = "Video"
help = "The value auto (the default) selects the GPU context. You can also pass help to get a complete list of compiled in backends (sorted by autoprobe order)." options = [{ name = "auto", help = "auto-select" },
options = [{ name = "auto", help = "auto-select (Default)" },
{ name = "win", help = "Win32/WGL" }, { name = "win", help = "Win32/WGL" },
{ name = "winvk", help = "VK_KHR_win32_surface" }, { name = "winvk", help = "VK_KHR_win32_surface" },
{ name = "angle", help = "Direct3D11 through the OpenGL ES translation layer ANGLE. This supports almost everything the win backend does (if the ANGLE build is new enough)." }, { name = "angle", help = "Direct3D11 through the OpenGL ES translation layer ANGLE. This supports almost everything the win backend does (if the ANGLE build is new enough)." },
@@ -45,9 +44,9 @@ options = [{ name = "auto", help = "auto-select (Default)" },
name = "vo" name = "vo"
default = "gpu" default = "gpu"
filter = "Video" filter = "Video"
help = "Video output drivers to be used.\n\nFor more information visit:"
helpurl = "https://mpv.io/manual/master/#video-output-drivers-vo" helpurl = "https://mpv.io/manual/master/#video-output-drivers-vo"
help = "Video output drivers to be used. Default = gpu.\n\nFor more information visit:" options = [{ name = "gpu", help = "General purpose, customizable, GPU-accelerated video output driver. It supports extended scaling methods, dithering, color management, custom shaders, HDR, and more." },
options = [{ name = "gpu", help = "General purpose, customizable, GPU-accelerated video output driver. It supports extended scaling methods, dithering, color management, custom shaders, HDR, and more. (Default)" },
{ name = "direct3d", help = "Video output driver that uses the Direct3D interface" }] { name = "direct3d", help = "Video output driver that uses the Direct3D interface" }]
[[settings]] [[settings]]
@@ -70,7 +69,7 @@ name = "scale"
default = "bilinear" default = "bilinear"
filter = "Video" filter = "Video"
help = "The GPU renderer filter function to use when upscaling video. There are some more filters, but most are not as useful. For a complete list, pass help as value, e.g.: mpv --scale=help" help = "The GPU renderer filter function to use when upscaling video. There are some more filters, but most are not as useful. For a complete list, pass help as value, e.g.: mpv --scale=help"
options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality). This is the default for compatibility reasons." }, options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." },
{ name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." },
{ name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" },
{ name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" },
@@ -84,7 +83,7 @@ name = "cscale"
default = "bilinear" default = "bilinear"
filter = "Video" filter = "Video"
help = "As scale, but for interpolating chroma information. If the image is not subsampled, this option is ignored entirely." help = "As scale, but for interpolating chroma information. If the image is not subsampled, this option is ignored entirely."
options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality). This is the default for compatibility reasons." }, options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." },
{ name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." },
{ name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" },
{ name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" },
@@ -98,7 +97,7 @@ name = "dscale"
default = "bilinear" default = "bilinear"
filter = "Video" filter = "Video"
help = "Like scale, but apply these filters on downscaling instead. If this option is unset, the filter implied by scale will be applied." help = "Like scale, but apply these filters on downscaling instead. If this option is unset, the filter implied by scale will be applied."
options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality). This is the default for compatibility reasons." }, options = [{ name = "bilinear", help = "Bilinear hardware texture filtering (fastest, very low quality)." },
{ name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." }, { name = "spline36", help = "Mid quality and speed. This is the default when using gpu-hq." },
{ name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" }, { name = "lanczos", help = "Lanczos scaling. Provides mid quality and speed. Generally worse than spline36, but it results in a slightly sharper image which is good for some content types. The number of taps can be controlled with scale-radius, but is best left unchanged. (This filter is an alias for sinc-windowed sinc)" },
{ name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" }, { name = "ewa_lanczos", help = "Elliptic weighted average Lanczos scaling. Also known as Jinc. Relatively slow, but very good quality. The radius can be controlled with scale-radius. Increasing the radius makes the filter sharper but adds more ringing. (This filter is an alias for jinc-windowed jinc)" },
@@ -111,7 +110,7 @@ options = [{ name = "bilinear", help = "Bilinear hardware texture filter
name = "dither-depth" name = "dither-depth"
default = "no" default = "no"
filter = "Video" filter = "Video"
help = "Set dither target depth to N. Default: no. Note that the depth of the connected video display device cannot be detected. Often, LCD panels will do dithering on their own, which conflicts with this option and leads to ugly output." help = "Set dither target depth to N. Note that the depth of the connected video display device cannot be detected. Often, LCD panels will do dithering on their own, which conflicts with this option and leads to ugly output."
options = [{ name = "no", help = "Disable any dithering done by mpv." }, options = [{ name = "no", help = "Disable any dithering done by mpv." },
{ name = "auto", help = "Automatic selection. If output bit depth cannot be detected, 8 bits per component are assumed." }, { name = "auto", help = "Automatic selection. If output bit depth cannot be detected, 8 bits per component are assumed." },
{ name = "8", help = "Dither to 8 bit output." }] { name = "8", help = "Dither to 8 bit output." }]
@@ -148,8 +147,8 @@ help = "Set the startup volume. 0 means silence, 100 means no volume reduction o
[[settings]] [[settings]]
name = "alang" name = "alang"
default = ""
filter = "Audio" filter = "Audio"
type = "string"
help = "Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also aid.\n\nExamples\n\nmpv dvd://1 alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv alang=jpn example.mkv plays a Matroska file with Japanese audio." help = "Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also aid.\n\nExamples\n\nmpv dvd://1 alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv alang=jpn example.mkv plays a Matroska file with Japanese audio."
[[settings]] [[settings]]
@@ -157,15 +156,15 @@ name = "audio-file-auto"
default = "no" default = "no"
filter = "Audio" filter = "Audio"
help = "Load additional audio files matching the video filename. The parameter specifies how external audio files are matched." help = "Load additional audio files matching the video filename. The parameter specifies how external audio files are matched."
options = [{ name = "no", help = "Don't automatically load external audio files (default)." }, options = [{ name = "no", help = "Don't automatically load external audio files." },
{ name = "exact", help = "Load the media filename with audio file extension." }, { name = "exact", help = "Load the media filename with audio file extension." },
{ name = "fuzzy", help = "Load all audio files containing media filename." }, { name = "fuzzy", help = "Load all audio files containing media filename." },
{ name = "all", help = "Load all audio files in the current and audio-file-paths directories." }] { name = "all", help = "Load all audio files in the current and audio-file-paths directories." }]
[[settings]] [[settings]]
name = "slang" name = "slang"
default = ""
filter = "Subtitle" filter = "Subtitle"
type = "string"
help = "Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also sid." help = "Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also sid."
[[settings]] [[settings]]
@@ -174,54 +173,65 @@ default = "exact"
filter = "Subtitle" filter = "Subtitle"
help = "Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default." help = "Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default."
options = [{ name = "no", help = "Don't automatically load external subtitle files." }, options = [{ name = "no", help = "Don't automatically load external subtitle files." },
{ name = "exact", help = "Load the media filename with subtitle file extension (Default)." }, { name = "exact", help = "Load the media filename with subtitle file extension." },
{ name = "fuzzy", help = "Load all subs containing media filename." }, { name = "fuzzy", help = "Load all subs containing media filename." },
{ name = "all", help = "Load all subs in the current and sub-file-paths directories." }] { name = "all", help = "Load all subs in the current and sub-file-paths directories." }]
[[settings]] [[settings]]
name = "sub-font" name = "sub-font"
default = ""
filter = "Subtitle" filter = "Subtitle"
type = "string"
help = "Specify font to use for subtitles that do not themselves specify a particular font. The default is sans-serif." help = "Specify font to use for subtitles that do not themselves specify a particular font. The default is sans-serif."
[[settings]] [[settings]]
name = "sub-font-size" name = "sub-font-size"
default = ""
filter = "Subtitle" filter = "Subtitle"
help = "Specify the sub font size. The unit is the size in scaled pixels at a window height of 720. The actual pixel size is scaled with the window height: if the window height is larger or smaller than 720, the actual size of the text increases or decreases as well. Default: 55" help = "Specify the sub font size. The unit is the size in scaled pixels at a window height of 720. The actual pixel size is scaled with the window height: if the window height is larger or smaller than 720, the actual size of the text increases or decreases as well. Default: 55"
[[settings]] [[settings]]
name = "sub-color" name = "sub-color"
default = ""
type = "color" type = "color"
filter = "Subtitle" filter = "Subtitle"
help = "Specify the color used for unstyled text subtitles.\n\nThe color is specified in the form r/g/b, where each color component is specified as number in the range 0.0 to 1.0. It's also possible to specify the transparency by using r/g/b/a, where the alpha value 0 means fully transparent, and 1.0 means opaque. If the alpha component is not given, the color is 100% opaque.\n\nPassing a single number to the option sets the sub to gray, and the form gray/a lets you specify alpha additionally.\n\nExamples\n\n1.0/0.0/0.0 set sub to opaque red\n1.0/0.0/0.0/0.75 set sub to opaque red with 75% alpha\n0.5/0.75 set sub to 50% gray with 75% alpha\n\nAlternatively, the color can be specified as a RGB hex triplet in the form #RRGGBB, where each 2-digit group expresses a color value in the range 0 (00) to 255 (FF). For example, #FF0000 is red. This is similar to web colors. Alpha is given with #AARRGGBB.\n\nExamples\n\n#FF0000 set sub to opaque red\n#C0808080 set sub to 50% gray with 75% alpha" help = "Specify the color used for unstyled text subtitles.\n\nThe color is specified in the form r/g/b, where each color component is specified as number in the range 0.0 to 1.0. It's also possible to specify the transparency by using r/g/b/a, where the alpha value 0 means fully transparent, and 1.0 means opaque. If the alpha component is not given, the color is 100% opaque.\n\nPassing a single number to the option sets the sub to gray, and the form gray/a lets you specify alpha additionally.\n\nExamples\n\n1.0/0.0/0.0 set sub to opaque red\n1.0/0.0/0.0/0.75 set sub to opaque red with 75% alpha\n0.5/0.75 set sub to 50% gray with 75% alpha\n\nAlternatively, the color can be specified as a RGB hex triplet in the form #RRGGBB, where each 2-digit group expresses a color value in the range 0 (00) to 255 (FF). For example, #FF0000 is red. This is similar to web colors. Alpha is given with #AARRGGBB.\n\nExamples\n\n#FF0000 set sub to opaque red\n#C0808080 set sub to 50% gray with 75% alpha"
[[settings]] [[settings]]
name = "sub-border-color" name = "sub-border-color"
default = ""
type = "color" type = "color"
filter = "Subtitle" filter = "Subtitle"
help = "See sub-color. Color used for the sub font border. Ignored when sub-back-color is specified (or more exactly: when that option is not set to completely transparent)." help = "See sub-color. Color used for the sub font border. Ignored when sub-back-color is specified (or more exactly: when that option is not set to completely transparent)."
[[settings]] [[settings]]
name = "sub-back-color" name = "sub-back-color"
default = ""
type = "color" type = "color"
filter = "Subtitle" filter = "Subtitle"
help = "See sub-color. Color used for sub text background. You can use sub-shadow-offset to change its size relative to the text." help = "See sub-color. Color used for sub text background. You can use sub-shadow-offset to change its size relative to the text."
[[settings]] [[settings]]
name = "screen" name = "fullscreen"
default = "" default = "no"
filter = "Screen" filter = "Screen"
help = "In multi-monitor configurations (i.e. a single desktop that spans across multiple displays), this option tells mpv which screen to display the video on. Default: default" help = "Start the player in fullscreen mode."
options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "border"
default = "yes"
filter = "Screen"
help = "Show window with decoration (titlebar, border)."
options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "screen"
filter = "Screen"
help = "<0-32> In multi-monitor configurations (i.e. a single desktop that spans across multiple displays), this option tells mpv which screen to display the video on."
[[settings]] [[settings]]
name = "osd-playing-msg" name = "osd-playing-msg"
default = ""
width = 300 width = 300
filter = "Screen" filter = "Screen"
type = "string"
help = "Show a message on OSD when playback starts. The string is expanded for properties, e.g. osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename. For more information visit:" help = "Show a message on OSD when playback starts. The string is expanded for properties, e.g. osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename. For more information visit:"
helpurl = "https://mpv.io/manual/master/#property-expansion" helpurl = "https://mpv.io/manual/master/#property-expansion"
@@ -231,17 +241,8 @@ default = "55"
filter = "Screen" filter = "Screen"
help = "Specify the OSD font size. See sub-font-size for details. Default: 55" help = "Specify the OSD font size. See sub-font-size for details. Default: 55"
[[settings]]
name = "fullscreen"
default = "no"
filter = "Screen"
help = "Start the player in fullscreen mode. Default: no"
options = [{ name = "yes" },
{ name = "no" }]
[[settings]] [[settings]]
name = "screenshot-directory" name = "screenshot-directory"
default = ""
width = 500 width = 500
type = "folder" type = "folder"
filter = "Screen" filter = "Screen"
@@ -253,11 +254,19 @@ default = "50%"
filter = "Screen" filter = "Screen"
help = "Set the initial window size in percent. Please note that this setting is only partly implemented in mpv.net, accepted are only integer values with percent sign added. Default: 50%" help = "Set the initial window size in percent. Please note that this setting is only partly implemented in mpv.net, accepted are only integer values with percent sign added. Default: 50%"
[[settings]]
name = "remember-height"
default = "yes"
filter = "Screen"
help = "mpv.net specific setting to remember the window height, otherwise the video's native resolution is used."
options = [{ name = "yes" },
{ name = "no" }]
[[settings]] [[settings]]
name = "keep-open-pause" name = "keep-open-pause"
default = "yes" default = "yes"
filter = "Playback" filter = "Playback"
help = "If set to no, instead of pausing when keep-open is active, just stop at end of file and continue playing forward when you seek backwards until end where it stops again. Default: yes" help = "If set to no, instead of pausing when keep-open is active, just stop at end of file and continue playing forward when you seek backwards until end where it stops again."
options = [{ name = "yes" }, options = [{ name = "yes" },
{ name = "no" }] { name = "no" }]
@@ -267,12 +276,11 @@ default = "no"
filter = "Playback" filter = "Playback"
help = "Do not terminate when playing or seeking beyond the end of the file, and there is not next file to be played (and loop is not used). Instead, pause the player. When trying to seek beyond end of the file, the player will attempt to seek to the last frame.\n\nNormally, this will act like set pause yes on EOF, unless the keep-open-pause=no option is set." help = "Do not terminate when playing or seeking beyond the end of the file, and there is not next file to be played (and loop is not used). Instead, pause the player. When trying to seek beyond end of the file, the player will attempt to seek to the last frame.\n\nNormally, this will act like set pause yes on EOF, unless the keep-open-pause=no option is set."
options = [{ name = "yes", help = "Don't terminate if the current file is the last playlist entry. Equivalent to keep-open without arguments."}, options = [{ name = "yes", help = "Don't terminate if the current file is the last playlist entry. Equivalent to keep-open without arguments."},
{ name = "no", help = "If the current file ends, go to the next file or terminate. (Default.)" }, { name = "no", help = "If the current file ends, go to the next file or terminate." },
{ name = "always", help = "Like yes, but also applies to files before the last playlist entry. This means playback will never automatically advance to the next file."}] { name = "always", help = "Like yes, but also applies to files before the last playlist entry. This means playback will never automatically advance to the next file."}]
[[settings]] [[settings]]
name = "loop-file" name = "loop-file"
default = ""
filter = "Playback" filter = "Playback"
help = "Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option." help = "Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option."
@@ -286,13 +294,11 @@ options = [{ name = "yes" },
[[settings]] [[settings]]
name = "input-ar-delay" name = "input-ar-delay"
default = ""
filter = "Input" filter = "Input"
help = "Delay in milliseconds before we start to autorepeat a key (0 to disable)." help = "Delay in milliseconds before we start to autorepeat a key (0 to disable)."
[[settings]] [[settings]]
name = "input-ar-rate" name = "input-ar-rate"
default = ""
filter = "Input" filter = "Input"
help = "Number of key presses to generate per second on autorepeat." help = "Number of key presses to generate per second on autorepeat."
@@ -303,19 +309,18 @@ filter = "Playback"
help = "Select when to use precise seeks that are not limited to keyframes. Such seeks require decoding video from the previous keyframe up to the target position and so can take some time depending on decoding performance. For some video formats, precise seeks are disabled. This option selects the default choice to use for seeks; it is possible to explicitly override that default in the definition of key bindings and in input commands." help = "Select when to use precise seeks that are not limited to keyframes. Such seeks require decoding video from the previous keyframe up to the target position and so can take some time depending on decoding performance. For some video formats, precise seeks are disabled. This option selects the default choice to use for seeks; it is possible to explicitly override that default in the definition of key bindings and in input commands."
options = [{ name = "yes", help = "Use precise seeks whenever possible." }, options = [{ name = "yes", help = "Use precise seeks whenever possible." },
{ name = "no", help = "Never use precise seeks." }, { name = "no", help = "Never use precise seeks." },
{ name = "absolute", help = "Use precise seeks if the seek is to an absolute position in the file, such as a chapter seek, but not for relative seeks like the default behavior of arrow keys (default)." }, { name = "absolute", help = "Use precise seeks if the seek is to an absolute position in the file, such as a chapter seek, but not for relative seeks like the default behavior of arrow keys." },
{ name = "always", help = "Same as yes (for compatibility)." }] { name = "always", help = "Same as yes (for compatibility)." }]
[[settings]] [[settings]]
name = "track-auto-selection" name = "track-auto-selection"
default = "yes" default = "yes"
filter = "Playback" filter = "Playback"
help = "Enable the default track auto-selection (default: yes). Enabling this will make the player select streams according to aid, alang, and others. If it is disabled, no tracks are selected. In addition, the player will not exit if no tracks are selected, and wait instead (this wait mode is similar to pausing, but the pause option is not set).\n\nThis is useful with lavfi-complex: you can start playback in this mode, and then set select tracks at runtime by setting the filter graph. Note that if lavfi-complex is set before playback is started, the referenced tracks are always selected." help = "Enable the default track auto-selection. Enabling this will make the player select streams according to aid, alang, and others. If it is disabled, no tracks are selected. In addition, the player will not exit if no tracks are selected, and wait instead (this wait mode is similar to pausing, but the pause option is not set).\n\nThis is useful with lavfi-complex: you can start playback in this mode, and then set select tracks at runtime by setting the filter graph. Note that if lavfi-complex is set before playback is started, the referenced tracks are always selected."
options = [{ name = "yes" }, options = [{ name = "yes" },
{ name = "no" }] { name = "no" }]
[[settings]] [[settings]]
name = "loop-playlist" name = "loop-playlist"
default = ""
filter = "Playback" filter = "Playback"
help = "<N|inf|force|no> Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. The force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions." help = "<N|inf|force|no> Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. The force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions."

View File

@@ -1,8 +1,31 @@
[[settings]] [[settings]]
name = "dark-mode" name = "dark-mode"
default = "always" default = "always"
filter = "mpv.net" filter = "General"
help = "Enables a dark theme." help = "Enables a dark theme."
options = [{ name = "always" }, options = [{ name = "always" },
{ name = "system" , help = "Available on Windows 10 or higher" }, { name = "system" , help = "Available on Windows 10 or higher" },
{ name = "never" }] { name = "never" }]
[[settings]]
name = "url-whitelist"
filter = "General"
type = "string"
help = "mpv.net specific whitelist setting to monitor the clipboard for URLs to play.\n\nDefault: tube vimeo ard zdf"
[[settings]]
name = "process-instance"
default = "single"
filter = "General"
help = "mpv.net specific setting that defines if more then one mpv.net process is allowed.\n\nTip: Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist."
options = [{ name = "multi", help = "Create a new process everytime the shell starts mpv.net" },
{ name = "single", help = "Force a single process everytime the shell starts mpv.net" },
{ name = "queue", help = "Force a single process and add files to playlist" }]
[[settings]]
name = "debug-mode"
default = "no"
filter = "General"
help = "mpv.net specific setting that writes debug info to a file located on the desktop."
options = [{ name = "yes" },
{ name = "no" }]

View File

@@ -14,26 +14,27 @@ namespace mpvnet
using (Runspace runspace = RunspaceFactory.CreateRunspace()) using (Runspace runspace = RunspaceFactory.CreateRunspace())
{ {
runspace.ApartmentState = ApartmentState.STA; runspace.ApartmentState = ApartmentState.STA;
runspace.ThreadOptions = PSThreadOptions.UseCurrentThread;
runspace.Open(); runspace.Open();
using (Pipeline pipeline = runspace.CreatePipeline()) using (Pipeline pipeline = runspace.CreatePipeline())
{ {
pipeline.Commands.AddScript( pipeline.Commands.AddScript(
@"Using namespace mpvnet; "Using namespace mpvnet\n" +
Using namespace System; "Using namespace System\n" +
[System.Reflection.Assembly]::LoadWithPartialName(""mpvnet"")"); "[System.Reflection.Assembly]::LoadWithPartialName(\"mpvnet\")\n");
pipeline.Commands.AddScript(code); pipeline.Commands.AddScript(code);
if (parameters != null)
foreach (var i in parameters)
pipeline.Commands[1].Parameters.Add(null, i);
try try
{ {
var ret = pipeline.Invoke(parameters); var ret = pipeline.Invoke();
if (ret.Count > 0) return ret[0];
if (ret.Count > 0)
return ret[0];
} }
catch (Exception ex) catch (Exception e)
{ {
try try
{ {
@@ -46,12 +47,12 @@ Using namespace System;
throw new Exception(); throw new Exception();
} }
} }
catch (Exception ex2) catch (Exception e2)
{ {
Msg.ShowError("PowerShell Setup Problem\n\nEnsure you have at least PowerShell 5.1 installed.", ex2.ToString()); Msg.ShowError("PowerShell Setup Problem\n\nEnsure you have at least PowerShell 5.1 installed.", e2.ToString());
return null; return null;
} }
Msg.ShowException(ex); Msg.ShowException(e);
} }
} }
} }
@@ -70,17 +71,11 @@ Using namespace System;
eventObject.FilePath = filePath; eventObject.FilePath = filePath;
if (eventInfo.EventHandlerType == typeof(Action)) if (eventInfo.EventHandlerType == typeof(Action))
{
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.Invoke)); mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.Invoke));
}
else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>)) else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>))
{
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeEndFileEventMode)); mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeEndFileEventMode));
}
else if (eventInfo.EventHandlerType == typeof(Action<string[]>)) else if (eventInfo.EventHandlerType == typeof(Action<string[]>))
{
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeStrings)); mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeStrings));
}
else else
throw new Exception(); throw new Exception();
@@ -91,10 +86,7 @@ Using namespace System;
return; return;
} }
} }
Task.Run(() => Task.Run(() => PowerShellScript.Execute(File.ReadAllText(filePath), null));
{
PowerShellScript.Execute(File.ReadAllText(filePath), new string[] {});
});
} }
} }
@@ -104,24 +96,16 @@ Using namespace System;
public Delegate Delegate { get; set; } public Delegate Delegate { get; set; }
public string FilePath { get; set; } public string FilePath { get; set; }
public void Invoke() public void Invoke() => Task.Run(() => { PowerShellScript.Execute(File.ReadAllText(FilePath), null); });
{
Task.Run(() => { PowerShellScript.Execute(File.ReadAllText(FilePath), new string[] { }); });
}
public void InvokeEndFileEventMode(EndFileEventMode arg) public void InvokeEndFileEventMode(EndFileEventMode arg)
{ {
Task.Run(() => Task.Run(() => PowerShellScript.Execute(File.ReadAllText(FilePath), new [] { arg.ToString() }));
{
PowerShellScript.Execute(File.ReadAllText(FilePath), new string[] { arg.ToString() });
});
} }
public void InvokeStrings(string[] args) public void InvokeStrings(string[] args)
{ {
Task.Run(() => { Task.Run(() => PowerShellScript.Execute(File.ReadAllText(FilePath), args));
PowerShellScript.Execute(File.ReadAllText(FilePath), args);
});
} }
} }
} }

View File

@@ -13,11 +13,11 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" /> <ColumnDefinition Width="100" />
<ColumnDefinition Width="6*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Controls:SearchTextBoxUserControl HintText="Find a setting" x:Name="SearchControl" Width="250" Margin="0,20,0,10" Grid.ColumnSpan="2" /> <Controls:SearchTextBoxUserControl HintText="Find a setting" x:Name="SearchControl" Width="250" Margin="0,20,0,0" Grid.ColumnSpan="2"/>
<ScrollViewer x:Name="MainScrollViewer" VerticalScrollBarVisibility="Auto" Grid.Row="1" Grid.Column="1" Margin="0,0,0,10"> <ScrollViewer x:Name="MainScrollViewer" VerticalScrollBarVisibility="Auto" Grid.Row="1" Grid.Column="2" Margin="0,0,0,10">
<StackPanel x:Name="MainStackPanel"></StackPanel> <StackPanel x:Name="MainStackPanel"></StackPanel>
</ScrollViewer> </ScrollViewer>
<StackPanel Margin="20,0,0,0" Grid.Row="1"> <StackPanel Margin="20,0,0,0" Grid.Row="1">

View File

@@ -15,8 +15,8 @@ namespace mpvnet
{ {
public partial class ConfWindow : Window public partial class ConfWindow : Window
{ {
private List<SettingBase> MpvSettingsDefinitions = Settings.LoadSettings(Properties.Resources.mpvConfToml); private List<SettingBase> SettingsDefinitions = Settings.LoadSettings(Properties.Resources.mpvConfToml);
private List<SettingBase> MpvNetSettingsDefinitions = Settings.LoadSettings(Properties.Resources.mpvNetConfToml); private List<SettingBase> NetSettingsDefinitions = Settings.LoadSettings(Properties.Resources.mpvNetConfToml);
private Dictionary<string, Dictionary<string, string>> Comments = new Dictionary<string, Dictionary<string, string>>(); private Dictionary<string, Dictionary<string, string>> Comments = new Dictionary<string, Dictionary<string, string>>();
public ObservableCollection<string> FilterStrings { get; } = new ObservableCollection<string>(); public ObservableCollection<string> FilterStrings { get; } = new ObservableCollection<string>();
@@ -26,9 +26,9 @@ namespace mpvnet
InitializeComponent(); InitializeComponent();
DataContext = this; DataContext = this;
SearchControl.SearchTextBox.TextChanged += SearchTextBox_TextChanged; SearchControl.SearchTextBox.TextChanged += SearchTextBox_TextChanged;
LoadSettings(MpvSettingsDefinitions, MpvConf); LoadSettings(SettingsDefinitions, Conf);
LoadSettings(MpvNetSettingsDefinitions, MpvNetConf); LoadSettings(NetSettingsDefinitions, NetConf);
SearchControl.Text = RegistryHelp.GetString(@"HKCU\Software\mpv.net", "config editor search"); SearchControl.Text = RegHelp.GetString(App.RegPath, "config editor search");
if (App.IsDarkMode) if (App.IsDarkMode)
{ {
@@ -49,7 +49,7 @@ namespace mpvnet
private void LoadSettings(List<SettingBase> settingsDefinitions, private void LoadSettings(List<SettingBase> settingsDefinitions,
Dictionary<string, string> confSettings) Dictionary<string, string> confSettings)
{ {
foreach (var setting in settingsDefinitions) foreach (SettingBase setting in settingsDefinitions)
{ {
if (!FilterStrings.Contains(setting.Filter)) if (!FilterStrings.Contains(setting.Filter))
FilterStrings.Add(setting.Filter); FilterStrings.Add(setting.Filter);
@@ -58,7 +58,7 @@ namespace mpvnet
{ {
if (setting.Name == pair.Key) if (setting.Name == pair.Key)
{ {
setting.Value = pair.Value; setting.Value = pair.Value.Trim('\'', '"');
setting.StartValue = pair.Value; setting.StartValue = pair.Value;
continue; continue;
} }
@@ -80,21 +80,21 @@ namespace mpvnet
} }
} }
private Dictionary<string, string> _mpvConf; private Dictionary<string, string> _Conf;
public Dictionary<string, string> MpvConf { public Dictionary<string, string> Conf {
get { get {
if (_mpvConf == null) _mpvConf = LoadConf(mp.MpvConfPath); if (_Conf == null) _Conf = LoadConf(mp.ConfPath);
return _mpvConf; return _Conf;
} }
} }
private Dictionary<string, string> _mpvNetConf; private Dictionary<string, string> _NetConf;
public Dictionary<string, string> MpvNetConf { public Dictionary<string, string> NetConf {
get { get {
if (_mpvNetConf == null) _mpvNetConf = LoadConf(mp.MpvNetConfPath); if (_NetConf == null) _NetConf = LoadConf(App.ConfFilePath);
return _mpvNetConf; return _NetConf;
} }
} }
@@ -132,26 +132,26 @@ namespace mpvnet
{ {
base.OnClosed(e); base.OnClosed(e);
WriteToDisk(); WriteToDisk();
RegistryHelp.SetObject(@"HKCU\Software\mpv.net", "config editor search", SearchControl.Text); RegHelp.SetObject(App.RegPath, "config editor search", SearchControl.Text);
} }
void WriteToDisk() void WriteToDisk()
{ {
bool isDirty = false; bool isDirty = false;
foreach (SettingBase i in MpvSettingsDefinitions) foreach (SettingBase i in SettingsDefinitions)
if (i.StartValue != i.Value) if (i.StartValue != i.Value)
isDirty = true; isDirty = true;
foreach (SettingBase i in MpvNetSettingsDefinitions) foreach (SettingBase i in NetSettingsDefinitions)
if (i.StartValue != i.Value) if (i.StartValue != i.Value)
isDirty = true; isDirty = true;
if (!isDirty) if (!isDirty)
return; return;
WriteToDisk(mp.MpvConfPath, MpvConf, MpvSettingsDefinitions); WriteToDisk(mp.ConfPath, Conf, SettingsDefinitions);
WriteToDisk(mp.MpvNetConfPath, MpvNetConf, MpvNetSettingsDefinitions); WriteToDisk(App.ConfFilePath, NetConf, NetSettingsDefinitions);
Msg.Show("Changes will be available on next mpv.net startup."); Msg.Show("Changes will be available on next mpv.net startup.");
} }
@@ -165,17 +165,22 @@ namespace mpvnet
foreach (var i in Comments[filePath]) foreach (var i in Comments[filePath])
content += $"#{i.Key} = {i.Value}\r\n"; content += $"#{i.Key} = {i.Value}\r\n";
foreach (var setting in settings) foreach (SettingBase setting in settings)
{ {
if ((setting.Value ?? "") != setting.Default) if ((setting.Value ?? "") != setting.Default)
confSettings[setting.Name] = setting.Value; if (setting.Type == "string" ||
setting.Type == "folder" ||
setting.Type == "color")
confSettings[setting.Name] = "'" + setting.Value + "'";
else
confSettings[setting.Name] = setting.Value;
if (confSettings.ContainsKey(setting.Name) && if (confSettings.ContainsKey(setting.Name) &&
(setting.Value ?? "") == setting.Default || (setting.Value ?? "") == setting.Default ||
(setting.Value ?? "") == "") (setting.Value ?? "") == "")
{
confSettings.Remove(setting.Name); confSettings.Remove(setting.Name);
}
} }
foreach (var i in confSettings) foreach (var i in confSettings)
@@ -228,7 +233,7 @@ namespace mpvnet
private void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e) private void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{ {
Process.Start(Path.GetDirectoryName(mp.MpvConfPath)); Process.Start(Path.GetDirectoryName(mp.ConfPath));
} }
private void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e) private void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e)

View File

@@ -95,12 +95,8 @@ namespace mpvnet
ListView.ScrollIntoView(ListView.SelectedItem); ListView.ScrollIntoView(ListView.SelectedItem);
} }
break; break;
case Key.Escape: case Key.Escape: Close(); break;
Close(); case Key.Enter: Execute(); break;
break;
case Key.Enter:
Execute();
break;
} }
} }
@@ -113,14 +109,11 @@ namespace mpvnet
void Execute() void Execute()
{ {
if (ListView.SelectedItem != null) if (ListView.SelectedItem != null)
mp.LoadFiles(ListView.SelectedItem as string); mp.Load(new[] { ListView.SelectedItem as string }, true, Keyboard.Modifiers == ModifierKeys.Control);
Keyboard.Focus(FilterTextBox); Keyboard.Focus(FilterTextBox);
} }
private void ListView_MouseUp(object sender, MouseButtonEventArgs e) private void ListView_MouseUp(object sender, MouseButtonEventArgs e) => Execute();
{
Execute();
}
private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e) private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{ {

View File

@@ -102,7 +102,15 @@ namespace mpvnet
string GetInputConfContent() string GetInputConfContent()
{ {
string text = Properties.Resources.inputConfHeader + "\r\n"; string text = null;
foreach (string line in Properties.Resources.inputConf.Split(new[] { "\r\n" }, StringSplitOptions.None))
{
string test = line.Trim();
if (test == "" || test.StartsWith("#")) text += test + "\r\n";
}
text = "\r\n" + text.Trim() + "\r\n\r\n";
foreach (CommandItem item in CommandItem.Items) foreach (CommandItem item in CommandItem.Items)
{ {

View File

@@ -8,7 +8,6 @@ using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Diagnostics;
namespace mpvnet namespace mpvnet
{ {
@@ -24,15 +23,6 @@ namespace mpvnet
bool IgnoreDpiChanged = true; bool IgnoreDpiChanged = true;
List<string> RecentFiles; List<string> RecentFiles;
public string MpvNetDarkMode { get; set; } = "always";
public bool MpvFullscreen { get; set; }
public float MpvAutofit { get; set; } = 0.50f;
public int MpvScreen { get; set; } = -1;
public string MpvSid { get; set; } = "";
public string MpvAid { get; set; } = "";
public string MpvVid { get; set; } = "";
public int MpvEdition { get; set; }
public MainForm() public MainForm()
{ {
InitializeComponent(); InitializeComponent();
@@ -49,25 +39,18 @@ namespace mpvnet
MinimumSize = new Size(FontHeight * 16, FontHeight * 9); MinimumSize = new Size(FontHeight * 16, FontHeight * 9);
Text += " " + Application.ProductVersion; Text += " " + Application.ProductVersion;
object recent = RegistryHelp.GetObject("HKCU\\Software\\" + Application.ProductName, "Recent"); object recent = RegHelp.GetObject(App.RegPath, "Recent");
if (recent is string[] r) if (recent is string[] r)
RecentFiles = new List<string>(r); RecentFiles = new List<string>(r);
else else
RecentFiles = new List<string>(); RecentFiles = new List<string>();
foreach (var i in mp.mpvConf) var dummy = mp.Conf;
ProcessMpvProperty(i.Key, i.Value); App.ProcessCommandLineEarly();
if (mp.Screen == -1) mp.Screen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
foreach (var i in mp.mpvNetConf) SetScreen(mp.Screen);
ProcessMpvNetProperty(i.Key, i.Value); CycleFullscreen(mp.Fullscreen);
ProcessCommandLineEarly();
if (MpvScreen == -1) MpvScreen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
SetScreen(MpvScreen);
ChangeFullscreen(MpvFullscreen);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -75,6 +58,24 @@ namespace mpvnet
} }
} }
public MenuItem FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items);
void Idle() => BeginInvoke(new Action(() => { Text = "mpv.net " + Application.ProductVersion; }));
void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
void VideoSizeChanged() => BeginInvoke(new Action(() => SetFormPosAndSize()));
void Shutdown() => BeginInvoke(new Action(() => Close()));
void PropChangeFullscreen(bool value) => BeginInvoke(new Action(() => CycleFullscreen(value)));
void ContextMenu_Opened(object sender, EventArgs e) => CursorHelp.Show();
bool IsFullscreen => WindowState == FormWindowState.Maximized;
bool IsMouseInOSC() => PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
void ContextMenu_Opening(object sender, CancelEventArgs e) void ContextMenu_Opening(object sender, CancelEventArgs e)
{ {
lock (mp.MediaTracks) lock (mp.MediaTracks)
@@ -94,7 +95,7 @@ namespace mpvnet
{ {
MenuItem mi = new MenuItem(track.Text); MenuItem mi = new MenuItem(track.Text);
mi.Action = () => { mp.commandv("set", "vid", track.ID.ToString()); }; mi.Action = () => { mp.commandv("set", "vid", track.ID.ToString()); };
mi.Checked = MpvVid == track.ID.ToString(); mi.Checked = mp.Vid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi); trackMenuItem.DropDownItems.Add(mi);
} }
@@ -105,7 +106,7 @@ namespace mpvnet
{ {
MenuItem mi = new MenuItem(track.Text); MenuItem mi = new MenuItem(track.Text);
mi.Action = () => { mp.commandv("set", "aid", track.ID.ToString()); }; mi.Action = () => { mp.commandv("set", "aid", track.ID.ToString()); };
mi.Checked = MpvAid == track.ID.ToString(); mi.Checked = mp.Aid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi); trackMenuItem.DropDownItems.Add(mi);
} }
@@ -116,7 +117,7 @@ namespace mpvnet
{ {
MenuItem mi = new MenuItem(track.Text); MenuItem mi = new MenuItem(track.Text);
mi.Action = () => { mp.commandv("set", "sid", track.ID.ToString()); }; mi.Action = () => { mp.commandv("set", "sid", track.ID.ToString()); };
mi.Checked = MpvSid == track.ID.ToString(); mi.Checked = mp.Sid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi); trackMenuItem.DropDownItems.Add(mi);
} }
@@ -124,7 +125,7 @@ namespace mpvnet
{ {
MenuItem mi = new MenuItem("S: No subtitles"); MenuItem mi = new MenuItem("S: No subtitles");
mi.Action = () => { mp.commandv("set", "sid", "no"); }; mi.Action = () => { mp.commandv("set", "sid", "no"); };
mi.Checked = MpvSid == "no"; mi.Checked = mp.Sid == "no";
trackMenuItem.DropDownItems.Add(mi); trackMenuItem.DropDownItems.Add(mi);
} }
@@ -135,7 +136,7 @@ namespace mpvnet
{ {
MenuItem mi = new MenuItem(track.Text); MenuItem mi = new MenuItem(track.Text);
mi.Action = () => { mp.commandv("set", "edition", track.ID.ToString()); }; mi.Action = () => { mp.commandv("set", "edition", track.ID.ToString()); };
mi.Checked = MpvEdition == track.ID; mi.Checked = mp.Edition == track.ID;
trackMenuItem.DropDownItems.Add(mi); trackMenuItem.DropDownItems.Add(mi);
} }
} }
@@ -164,10 +165,8 @@ namespace mpvnet
if (recent != null) if (recent != null)
{ {
recent.DropDownItems.Clear(); recent.DropDownItems.Clear();
foreach (string path in RecentFiles) foreach (string path in RecentFiles)
MenuItem.Add(recent.DropDownItems, path, () => mp.LoadFiles(path)); MenuItem.Add(recent.DropDownItems, path, () => mp.Load(new[] { path }, true, Control.ModifierKeys.HasFlag(Keys.Control)));
recent.DropDownItems.Add(new ToolStripSeparator()); recent.DropDownItems.Add(new ToolStripSeparator());
MenuItem mi = new MenuItem("Clear List"); MenuItem mi = new MenuItem("Clear List");
mi.Action = () => RecentFiles.Clear(); mi.Action = () => RecentFiles.Clear();
@@ -175,8 +174,6 @@ namespace mpvnet
} }
} }
public MenuItem FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items);
MenuItem FindMenuItem(string text, ToolStripItemCollection items) MenuItem FindMenuItem(string text, ToolStripItemCollection items)
{ {
foreach (var item in items) foreach (var item in items)
@@ -215,7 +212,7 @@ namespace mpvnet
{ {
if (IsFullscreen || mp.VideoSize.Width == 0) return; if (IsFullscreen || mp.VideoSize.Width == 0) return;
Screen screen = Screen.FromControl(this); Screen screen = Screen.FromControl(this);
int height = Convert.ToInt32(screen.Bounds.Height * MpvAutofit); int height = Convert.ToInt32(screen.Bounds.Height * mp.Autofit);
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height); int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2); Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height)); var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
@@ -225,12 +222,13 @@ namespace mpvnet
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */); Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
} }
void SetFormPositionAndSizeKeepHeight() void SetFormPosAndSize()
{ {
if (IsFullscreen || mp.VideoSize.Width == 0) return; if (IsFullscreen || mp.VideoSize.Width == 0) return;
Screen screen = Screen.FromControl(this); Screen screen = Screen.FromControl(this);
int height = ClientSize.Height; int height = mp.VideoSize.Height;
if (height > screen.Bounds.Height * 0.9) height = Convert.ToInt32(screen.Bounds.Height * MpvAutofit); if (mp.RememberHeight) height = ClientSize.Height;
if (height > screen.Bounds.Height * 0.9) height = Convert.ToInt32(screen.Bounds.Height * mp.Autofit);
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height); int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2); Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height)); var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
@@ -238,72 +236,9 @@ namespace mpvnet
int left = middlePos.X - rect.Width / 2; int left = middlePos.X - rect.Width / 2;
int top = middlePos.Y - rect.Height / 2; int top = middlePos.Y - rect.Height / 2;
Screen[] screens = Screen.AllScreens; Screen[] screens = Screen.AllScreens;
if (left < screens[0].Bounds.Left) left = screens[0].Bounds.Left;
int maxLeft = screens[0].Bounds.Left + screens.Select((sc) => sc.Bounds.Width).Sum() - rect.Width - SystemInformation.CaptionHeight;
if (left > maxLeft) left = maxLeft;
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */); Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
} }
protected void ProcessCommandLineEarly()
{
var args = Environment.GetCommandLineArgs().Skip(1);
foreach (string i in args)
{
if (i.StartsWith("--"))
{
if (i.Contains("="))
{
string left = i.Substring(2, i.IndexOf("=") - 2);
string right = i.Substring(left.Length + 3);
ProcessMpvProperty(left, right);
ProcessMpvNetProperty(left, right);
}
else
{
string switchName = i.Substring(2);
switch (switchName)
{
case "fs":
case "fullscreen":
MpvFullscreen = true;
break;
}
}
}
}
}
void ProcessMpvProperty(string name, string value)
{
switch (name)
{
case "autofit":
if (value.Length == 3 && value.EndsWith("%"))
if (int.TryParse(value.Substring(0, 2), out int result))
MpvAutofit = result / 100f;
break;
case "fs":
case "fullscreen":
MpvFullscreen = value == "yes";
break;
case "screen":
MpvScreen = Convert.ToInt32(value);
break;
}
}
void ProcessMpvNetProperty(string name, string value)
{
switch (name)
{
case "dark-mode":
MpvNetDarkMode = value;
break;
}
}
public void BuildMenu() public void BuildMenu()
{ {
string content = File.ReadAllText(mp.InputConfPath); string content = File.ReadAllText(mp.InputConfPath);
@@ -321,8 +256,7 @@ namespace mpvnet
foreach (CommandItem item in items) foreach (CommandItem item in items)
{ {
if (string.IsNullOrEmpty(item.Path)) if (string.IsNullOrEmpty(item.Path)) continue;
continue;
string path = item.Path.Replace("&", "&&"); string path = item.Path.Replace("&", "&&");
MenuItem menuItem = ContextMenu.Add(path, () => { MenuItem menuItem = ContextMenu.Add(path, () => {
try { try {
@@ -331,18 +265,15 @@ namespace mpvnet
Msg.ShowException(ex); Msg.ShowException(ex);
} }
}); });
if (menuItem != null) if (menuItem != null) menuItem.ShortcutKeyDisplayString = item.Input + " ";
menuItem.ShortcutKeyDisplayString = item.Input + " ";
} }
} }
void ContextMenu_Opened(object sender, EventArgs e) => CursorHelp.Show(); private void FileLoaded()
private void Mp_FileLoaded()
{ {
string path = mp.get_property_string("path"); string path = mp.get_property_string("path");
BeginInvoke(new Action(() => { BeginInvoke(new Action(() => {
if (File.Exists(path) || path.StartsWith("http")) if (File.Exists(path) || path.Contains("://"))
Text = Path.GetFileName(path) + " - mpv.net " + Application.ProductVersion; Text = Path.GetFileName(path) + " - mpv.net " + Application.ProductVersion;
else else
Text = "mpv.net " + Application.ProductVersion; Text = "mpv.net " + Application.ProductVersion;
@@ -352,10 +283,6 @@ namespace mpvnet
if (RecentFiles.Count > 15) RecentFiles.RemoveAt(15); if (RecentFiles.Count > 15) RecentFiles.RemoveAt(15);
} }
void Mp_Idle() => BeginInvoke(new Action(() => { Text = "mpv.net " + Application.ProductVersion; }));
void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
void Application_ThreadException(object sender, ThreadExceptionEventArgs e) void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{ {
Msg.ShowException(e.Exception); Msg.ShowException(e.Exception);
@@ -365,29 +292,12 @@ namespace mpvnet
{ {
Msg.ShowError(e.ExceptionObject.ToString()); Msg.ShowError(e.ExceptionObject.ToString());
} }
void mp_VideoSizeChanged() public void CycleFullscreen(bool enabled)
{ {
BeginInvoke(new Action(() => SetFormPositionAndSizeKeepHeight())); if (enabled)
}
void mp_Shutdown()
{
BeginInvoke(new Action(() => Close()));
}
public bool IsFullscreen => WindowState == FormWindowState.Maximized;
void mpPropChangeFullscreen(bool value)
{
BeginInvoke(new Action(() => ChangeFullscreen(value)));
}
void ChangeFullscreen(bool value)
{
if (value)
{ {
if (FormBorderStyle != FormBorderStyle.None) if (WindowState != FormWindowState.Maximized)
{ {
FormBorderStyle = FormBorderStyle.None; FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized; WindowState = FormWindowState.Maximized;
@@ -396,14 +306,19 @@ namespace mpvnet
else else
{ {
WindowState = FormWindowState.Normal; WindowState = FormWindowState.Normal;
FormBorderStyle = FormBorderStyle.Sizable;
SetFormPositionAndSizeKeepHeight(); if (mp.Border)
FormBorderStyle = FormBorderStyle.Sizable;
else
FormBorderStyle = FormBorderStyle.None;
SetFormPosAndSize();
} }
} }
protected override void WndProc(ref Message m) protected override void WndProc(ref Message m)
{ {
//Debug.WriteLine(m); //System.Diagnostics.Debug.WriteLine(m);
switch (m.Msg) switch (m.Msg)
{ {
@@ -414,16 +329,23 @@ namespace mpvnet
case 0x0104: // WM_SYSKEYDOWN case 0x0104: // WM_SYSKEYDOWN
case 0x0105: // WM_SYSKEYUP case 0x0105: // WM_SYSKEYUP
case 0x020A: // WM_MOUSEWHEEL case 0x020A: // WM_MOUSEWHEEL
if (mp.MpvWindowHandle != IntPtr.Zero) if (mp.WindowHandle != IntPtr.Zero)
Native.SendMessage(mp.MpvWindowHandle, m.Msg, m.WParam, m.LParam); Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
break;
case 0x0200: // WM_MOUSEMOVE
Point pos = PointToClient(Cursor.Position);
mp.command_string($"mouse {pos.X} {pos.Y}");
if (CursorHelp.IsPosDifferent(LastCursorPosChanged)) CursorHelp.Show();
break;
case 0x2a3: // WM_MOUSELEAVE
mp.command_string("mouse 1 1"); // osc won't always auto hide
break; break;
case 0x319: // WM_APPCOMMAND case 0x319: // WM_APPCOMMAND
if (mp.MpvWindowHandle != IntPtr.Zero) if (mp.WindowHandle != IntPtr.Zero)
Native.PostMessage(mp.MpvWindowHandle, m.Msg, m.WParam, m.LParam); Native.PostMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
break; break;
case 0x203: // Native.WM.LBUTTONDBLCLK case 0x203: // Native.WM.LBUTTONDBLCLK
if (!IsMouseInOSC()) if (!IsMouseInOSC()) mp.command_string("cycle fullscreen");
mp.command_string("cycle fullscreen");
break; break;
case 0x02E0: // WM_DPICHANGED case 0x02E0: // WM_DPICHANGED
if (IgnoreDpiChanged) break; if (IgnoreDpiChanged) break;
@@ -449,6 +371,30 @@ namespace mpvnet
m.Result = new IntPtr(1); m.Result = new IntPtr(1);
return; return;
} }
if (m.Msg == SingleProcess.Message)
{
object filesObject = RegHelp.GetObject(App.RegPath, "ShellFiles");
if (filesObject is string[] files)
{
switch (RegHelp.GetString(App.RegPath, "ProcessInstanceMode"))
{
case "single":
mp.Load(files, true, Control.ModifierKeys.HasFlag(Keys.Control));
break;
case "queue":
foreach (string file in files)
mp.commandv("loadfile", file, "append");
break;
}
}
RegHelp.RemoveValue(App.RegPath, "ShellFiles");
Activate();
return;
}
base.WndProc(ref m); base.WndProc(ref m);
} }
@@ -463,9 +409,100 @@ namespace mpvnet
{ {
base.OnDragDrop(e); base.OnDragDrop(e);
if (e.Data.GetDataPresent(DataFormats.FileDrop)) if (e.Data.GetDataPresent(DataFormats.FileDrop))
mp.LoadFiles(e.Data.GetData(DataFormats.FileDrop) as String[]); mp.Load(e.Data.GetData(DataFormats.FileDrop) as String[], true, Control.ModifierKeys.HasFlag(Keys.Control));
if (e.Data.GetDataPresent(DataFormats.Text)) if (e.Data.GetDataPresent(DataFormats.Text))
mp.LoadFiles(e.Data.GetData(DataFormats.Text).ToString()); mp.Load(new[] { e.Data.GetData(DataFormats.Text).ToString() }, true, Control.ModifierKeys.HasFlag(Keys.Control));
}
void Timer_Tick(object sender, EventArgs e)
{
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
{
LastCursorPosChanged = Control.MousePosition;
LastCursorChangedTickCount = Environment.TickCount;
}
else if (Environment.TickCount - LastCursorChangedTickCount > 1500 &&
!IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) &&
Form.ActiveForm == this && !ContextMenu.Visible)
CursorHelp.Hide();
}
void PropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value));
void PropChangeAid(string value) => mp.Aid = value;
void PropChangeSid(string value) => mp.Sid = value;
void PropChangeVid(string value) => mp.Vid = value;
void PropChangeEdition(int value) => mp.Edition = value;
void PropChangeBorder(bool enabled) {
mp.Border = enabled;
BeginInvoke(new Action(() => {
if (!IsFullscreen)
{
if (mp.Border && FormBorderStyle == FormBorderStyle.None)
FormBorderStyle = FormBorderStyle.Sizable;
if (!mp.Border && FormBorderStyle == FormBorderStyle.Sizable)
FormBorderStyle = FormBorderStyle.None;
}
}));
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
mp.Init();
mp.observe_property_bool("fullscreen", PropChangeFullscreen);
mp.observe_property_bool("ontop", PropChangeOnTop);
mp.observe_property_bool("border", PropChangeBorder);
mp.observe_property_string("sid", PropChangeSid);
mp.observe_property_string("aid", PropChangeAid);
mp.observe_property_string("vid", PropChangeVid);
mp.observe_property_int("edition", PropChangeEdition);
mp.Shutdown += Shutdown;
mp.VideoSizeChanged += VideoSizeChanged;
mp.FileLoaded += FileLoaded;
mp.Idle += Idle;
}
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
if (App.IsDarkMode) ToolStripRendererEx.ColorTheme = Color.Black;
ContextMenu = new ContextMenuStripEx(components);
ContextMenu.Opened += ContextMenu_Opened;
ContextMenu.Opening += ContextMenu_Opening;
BuildMenu();
ContextMenuStrip = ContextMenu;
IgnoreDpiChanged = false;
CheckUrlInClipboard();
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
CheckUrlInClipboard();
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
Native.SendMessage(Handle, m.Msg, m.WParam, m.LParam);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (mp.IsLogoVisible) mp.ShowLogo();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
RegHelp.SetObject(App.RegPath, "Recent", RecentFiles.ToArray());
App.Exit();
mp.commandv("quit");
mp.AutoResetEvent.WaitOne(3000);
} }
protected override void OnMouseDown(MouseEventArgs e) protected override void OnMouseDown(MouseEventArgs e)
@@ -485,108 +522,27 @@ namespace mpvnet
mp.commandv("quit"); mp.commandv("quit");
} }
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
mp.command_string($"mouse {e.X} {e.Y}");
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
CursorHelp.Show();
}
bool IsMouseInOSC() => PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
void Timer_Tick(object sender, EventArgs e)
{
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
{
LastCursorPosChanged = Control.MousePosition;
LastCursorChangedTickCount = Environment.TickCount;
}
else if (Environment.TickCount - LastCursorChangedTickCount > 1500 &&
!IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) &&
Form.ActiveForm == this && !ContextMenu.Visible)
{
CursorHelp.Hide();
}
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
mp.Init();
mp.observe_property_bool("fullscreen", mpPropChangeFullscreen);
mp.observe_property_bool("ontop", mpPropChangeOnTop);
mp.observe_property_string("sid", mpPropChangeSid);
mp.observe_property_string("aid", mpPropChangeAid);
mp.observe_property_string("vid", mpPropChangeVid);
mp.observe_property_int("edition", mpPropChangeEdition);
mp.Shutdown += mp_Shutdown;
mp.VideoSizeChanged += mp_VideoSizeChanged;
mp.FileLoaded += Mp_FileLoaded;
mp.Idle += Mp_Idle;
}
void mpPropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value));
void mpPropChangeAid(string value) => MpvAid = value;
void mpPropChangeSid(string value) => MpvSid = value;
void mpPropChangeVid(string value) => MpvVid = value;
void mpPropChangeEdition(int value) => MpvEdition = value;
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
if ((MpvNetDarkMode == "system" && Sys.IsDarkTheme) || MpvNetDarkMode == "always")
ToolStripRendererEx.ColorTheme = Color.Black;
ContextMenu = new ContextMenuStripEx(components);
ContextMenu.Opened += ContextMenu_Opened;
ContextMenu.Opening += ContextMenu_Opening;
BuildMenu();
ContextMenuStrip = ContextMenu;
IgnoreDpiChanged = false;
CheckURL();
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (mp.IsLogoVisible) mp.ShowLogo();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
RegistryHelp.SetObject("HKCU\\Software\\" + Application.ProductName, "Recent", RecentFiles.ToArray());
mp.commandv("quit");
mp.AutoResetEvent.WaitOne(3000);
}
protected override void OnLostFocus(EventArgs e) protected override void OnLostFocus(EventArgs e)
{ {
base.OnLostFocus(e); base.OnLostFocus(e);
CursorHelp.Show(); CursorHelp.Show();
} }
protected override void OnActivated(EventArgs e) void CheckUrlInClipboard()
{
base.OnActivated(e);
CheckURL();
}
void CheckURL()
{ {
string clipboard = Clipboard.GetText(); string clipboard = Clipboard.GetText();
if (clipboard.StartsWith("http") && RegistryHelp.GetString("HKCU\\Software\\" + Application.ProductName, "LastURL") != clipboard && Visible) foreach (string url in App.UrlWhitelist)
{ {
RegistryHelp.SetObject("HKCU\\Software\\" + Application.ProductName, "LastURL", clipboard); if (clipboard.Contains("://") && ! clipboard.Contains("\n") &&
! clipboard.Contains(" ") && clipboard.Contains(url.ToLower()) &&
RegHelp.GetString(App.RegPath, "LastURL") != clipboard && Visible)
{
RegHelp.SetObject(App.RegPath, "LastURL", clipboard);
if (Msg.ShowQuestion("Play URL?", clipboard) == MsgResult.OK) if (Msg.ShowQuestion("Play URL?", clipboard) == MsgResult.OK)
mp.LoadFiles(clipboard); mp.Load(new[] { clipboard }, true, Control.ModifierKeys.HasFlag(Keys.Control));
}
} }
} }
} }

View File

@@ -260,7 +260,6 @@
<None Include="App.config" /> <None Include="App.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Resources\inputConfHeader.txt" />
<Content Include="Resources\inputConf.txt" /> <Content Include="Resources\inputConf.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Drawing2D; using System.Drawing.Drawing2D;
using System.Drawing.Imaging; using System.Drawing.Imaging;
@@ -52,8 +51,8 @@ namespace mpvnet
public static event Action QueueOverflow; // MPV_EVENT_QUEUE_OVERFLOW public static event Action QueueOverflow; // MPV_EVENT_QUEUE_OVERFLOW
public static event Action Hook; // MPV_EVENT_HOOK public static event Action Hook; // MPV_EVENT_HOOK
public static IntPtr MpvHandle { get; set; } public static IntPtr Handle { get; set; }
public static IntPtr MpvWindowHandle { get; set; } public static IntPtr WindowHandle { get; set; }
public static Addon Addon { get; set; } public static Addon Addon { get; set; }
public static bool IsLogoVisible { set; get; } public static bool IsLogoVisible { set; get; }
public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>(); public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>();
@@ -65,15 +64,43 @@ namespace mpvnet
public static List<MediaTrack> MediaTracks { get; set; } = new List<MediaTrack>(); public static List<MediaTrack> MediaTracks { get; set; } = new List<MediaTrack>();
public static List<KeyValuePair<string, double>> Chapters { get; set; } = new List<KeyValuePair<string, double>>(); public static List<KeyValuePair<string, double>> Chapters { get; set; } = new List<KeyValuePair<string, double>>();
public static string InputConfPath { get; } = MpvConfFolder + "\\input.conf"; public static string InputConfPath { get; } = ConfFolder + "\\input.conf";
public static string MpvConfPath { get; } = MpvConfFolder + "\\mpv.conf"; public static string ConfPath { get; } = ConfFolder + "\\mpv.conf";
public static string MpvNetConfPath { get; } = MpvConfFolder + "\\mpvnet.conf"; public static string Sid { get; set; } = "";
public static string Aid { get; set; } = "";
public static string Vid { get; set; } = "";
static string _MpvConfFolder; public static bool Fullscreen { get; set; }
public static bool Border { get; set; } = true;
public static bool RememberHeight { get; set; } = true;
public static string MpvConfFolder { public static int Screen { get; set; } = -1;
public static int Edition { get; set; }
public static float Autofit { get; set; } = 0.5f;
public static void ProcessProperty(string name, string value)
{
switch (name)
{
case "autofit":
if (value.Length == 3 && value.EndsWith("%"))
if (int.TryParse(value.Substring(0, 2), out int result))
Autofit = result / 100f;
break;
case "fs":
case "fullscreen": Fullscreen = value == "yes"; break;
case "border": Border = value == "yes"; break;
case "screen": Screen = Convert.ToInt32(value); break;
case "remember-height": RememberHeight = value == "yes"; break;
}
}
static string _ConfFolder;
public static string ConfFolder {
get { get {
if (_MpvConfFolder == null) if (_ConfFolder == null)
{ {
string portableFolder = Application.StartupPath + "\\portable_config\\"; string portableFolder = Application.StartupPath + "\\portable_config\\";
string appdataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\"; string appdataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\";
@@ -91,75 +118,60 @@ namespace mpvnet
td.AddCommandLink("portable", portableFolder, portableFolder); td.AddCommandLink("portable", portableFolder, portableFolder);
td.AddCommandLink("startup", startupFolder, startupFolder); td.AddCommandLink("startup", startupFolder, startupFolder);
td.AllowCancel = false; td.AllowCancel = false;
_MpvConfFolder = td.Show(); _ConfFolder = td.Show();
} }
} }
else if (Directory.Exists(portableFolder)) else if (Directory.Exists(portableFolder))
_MpvConfFolder = portableFolder; _ConfFolder = portableFolder;
else if (Directory.Exists(appdataFolder)) else if (Directory.Exists(appdataFolder))
_MpvConfFolder = appdataFolder; _ConfFolder = appdataFolder;
else if (File.Exists(Application.StartupPath + "\\mpv.conf")) else if (File.Exists(Application.StartupPath + "\\mpv.conf"))
_MpvConfFolder = Application.StartupPath + "\\"; _ConfFolder = Application.StartupPath + "\\";
if (string.IsNullOrEmpty(_MpvConfFolder)) _MpvConfFolder = appdataFolder; if (string.IsNullOrEmpty(_ConfFolder)) _ConfFolder = appdataFolder;
if (!Directory.Exists(_MpvConfFolder)) Directory.CreateDirectory(_MpvConfFolder); if (!Directory.Exists(_ConfFolder)) Directory.CreateDirectory(_ConfFolder);
if (!File.Exists(_MpvConfFolder + "\\input.conf")) if (!File.Exists(_ConfFolder + "\\input.conf"))
File.WriteAllText(_MpvConfFolder + "\\input.conf", Properties.Resources.inputConf); File.WriteAllText(_ConfFolder + "\\input.conf", Properties.Resources.inputConf);
if (!File.Exists(_MpvConfFolder + "\\mpv.conf")) if (!File.Exists(_ConfFolder + "\\mpv.conf"))
File.WriteAllText(_MpvConfFolder + "\\mpv.conf", Properties.Resources.mpvConf); File.WriteAllText(_ConfFolder + "\\mpv.conf", Properties.Resources.mpvConf);
} }
return _MpvConfFolder; return _ConfFolder;
} }
} }
static Dictionary<string, string> _mpvConf; static Dictionary<string, string> _Conf;
public static Dictionary<string, string> mpvConf { public static Dictionary<string, string> Conf {
get { get {
if (_mpvConf == null) if (_Conf == null)
{ {
_mpvConf = new Dictionary<string, string>(); _Conf = new Dictionary<string, string>();
if (File.Exists(MpvConfPath)) if (File.Exists(ConfPath))
foreach (var i in File.ReadAllLines(MpvConfPath)) foreach (var i in File.ReadAllLines(ConfPath))
if (i.Contains("=") && ! i.StartsWith("#")) if (i.Contains("=") && ! i.StartsWith("#"))
_mpvConf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim(); _Conf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
foreach (var i in Conf)
ProcessProperty(i.Key, i.Value);
} }
return _mpvConf; return _Conf;
}
}
static Dictionary<string, string> _mpvNetConf;
public static Dictionary<string, string> mpvNetConf {
get {
if (_mpvNetConf == null)
{
_mpvNetConf = new Dictionary<string, string>();
if (File.Exists(MpvNetConfPath))
foreach (string i in File.ReadAllLines(MpvNetConfPath))
if (i.Contains("=") && !i.StartsWith("#"))
_mpvNetConf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
}
return _mpvNetConf;
} }
} }
public static void Init() public static void Init()
{ {
string dummy = MpvConfFolder; string dummy = ConfFolder;
LoadLibrary("mpv-1.dll"); LoadLibrary("mpv-1.dll");
MpvHandle = mpv_create(); Handle = mpv_create();
set_property_string("input-default-bindings", "yes");
set_property_string("osc", "yes"); set_property_string("osc", "yes");
set_property_string("config", "yes"); set_property_string("config", "yes");
set_property_string("wid", MainForm.Hwnd.ToString()); set_property_string("wid", MainForm.Hwnd.ToString());
set_property_string("force-window", "yes"); set_property_string("force-window", "yes");
set_property_string("input-media-keys", "yes"); set_property_string("input-media-keys", "yes");
mpv_initialize(MpvHandle); mpv_initialize(Handle);
ShowLogo(); ShowLogo();
ProcessCommandLine(); ProcessCommandLine();
Task.Run(() => { LoadScripts(); }); Task.Run(() => { LoadScripts(); });
@@ -169,23 +181,22 @@ namespace mpvnet
public static void LoadScripts() public static void LoadScripts()
{ {
string[] jsLua = { ".lua", ".js" };
string[] startupScripts = Directory.GetFiles(Application.StartupPath + "\\Scripts"); string[] startupScripts = Directory.GetFiles(Application.StartupPath + "\\Scripts");
foreach (var scriptPath in startupScripts) foreach (string scriptPath in startupScripts)
if (jsLua.Contains(Path.GetExtension(scriptPath).ToLower())) if (scriptPath.EndsWith(".lua") || scriptPath.EndsWith(".js"))
mp.commandv("load-script", $"{scriptPath}"); commandv("load-script", $"{scriptPath}");
foreach (var scriptPath in startupScripts) foreach (string scriptPath in startupScripts)
if (Path.GetExtension(scriptPath) == ".py") if (Path.GetExtension(scriptPath) == ".py")
PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath))); PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath)));
foreach (var scriptPath in startupScripts) foreach (string scriptPath in startupScripts)
if (Path.GetExtension(scriptPath) == ".ps1") if (Path.GetExtension(scriptPath) == ".ps1")
PowerShellScript.Init(scriptPath); PowerShellScript.Init(scriptPath);
if (Directory.Exists(mp.MpvConfFolder + "Scripts")) if (Directory.Exists(ConfFolder + "Scripts"))
foreach (var scriptPath in Directory.GetFiles(mp.MpvConfFolder + "Scripts")) foreach (string scriptPath in Directory.GetFiles(ConfFolder + "Scripts"))
if (Path.GetExtension(scriptPath) == ".py") if (Path.GetExtension(scriptPath) == ".py")
PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath))); PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath)));
else if (Path.GetExtension(scriptPath) == ".ps1") else if (Path.GetExtension(scriptPath) == ".ps1")
@@ -196,13 +207,13 @@ namespace mpvnet
{ {
while (true) while (true)
{ {
IntPtr ptr = mpv_wait_event(MpvHandle, -1); IntPtr ptr = mpv_wait_event(Handle, -1);
mpv_event evt = (mpv_event)Marshal.PtrToStructure(ptr, typeof(mpv_event)); mpv_event evt = (mpv_event)Marshal.PtrToStructure(ptr, typeof(mpv_event));
if (MpvWindowHandle == IntPtr.Zero) if (WindowHandle == IntPtr.Zero)
MpvWindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null); WindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null);
//Debug.WriteLine(evt.event_id.ToString()); //System.Diagnostics.Debug.WriteLine(evt.event_id.ToString());
try try
{ {
@@ -236,7 +247,7 @@ namespace mpvnet
case mpv_event_id.MPV_EVENT_FILE_LOADED: case mpv_event_id.MPV_EVENT_FILE_LOADED:
HideLogo(); HideLogo();
FileLoaded?.Invoke(); FileLoaded?.Invoke();
WriteHistory(mp.get_property_string("path")); WriteHistory(get_property_string("path"));
break; break;
case mpv_event_id.MPV_EVENT_TRACKS_CHANGED: case mpv_event_id.MPV_EVENT_TRACKS_CHANGED:
TracksChanged?.Invoke(); TracksChanged?.Invoke();
@@ -246,8 +257,7 @@ namespace mpvnet
break; break;
case mpv_event_id.MPV_EVENT_IDLE: case mpv_event_id.MPV_EVENT_IDLE:
Idle?.Invoke(); Idle?.Invoke();
if (mp.get_property_int("playlist-count") == 0) if (get_property_int("playlist-count") == 0) ShowLogo();
ShowLogo();
break; break;
case mpv_event_id.MPV_EVENT_PAUSE: case mpv_event_id.MPV_EVENT_PAUSE:
Pause?.Invoke(); Pause?.Invoke();
@@ -265,22 +275,19 @@ namespace mpvnet
var client_messageData = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message)); var client_messageData = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message));
string[] args = NativeUtf8StrArray2ManagedStrArray(client_messageData.args, client_messageData.num_args); string[] args = NativeUtf8StrArray2ManagedStrArray(client_messageData.args, client_messageData.num_args);
if (args != null && args.Length > 1 && args[0] == "mpv.net") if (args.Length > 1 && args[0] == "mpv.net")
{ {
bool found = false; bool found = false;
foreach (var i in mpvnet.Command.Commands) foreach (Command i in Command.Commands)
{ {
if (args[1] == i.Name) if (args[1] == i.Name)
{ {
found = true; found = true;
i.Action.Invoke(args.Skip(2).ToArray()); i.Action.Invoke(args.Skip(2).ToArray());
MainForm.Instance.BeginInvoke(new Action(() => {
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
Native.SendMessage(MainForm.Instance.Handle, m.Msg, m.WParam, m.LParam);
}));
} }
} }
if (!found) if (!found)
{ {
List<string> names = mpvnet.Command.Commands.Select((item) => item.Name).ToList(); List<string> names = mpvnet.Command.Commands.Select((item) => item.Name).ToList();
@@ -288,7 +295,8 @@ namespace mpvnet
Msg.ShowError($"No command '{args[1]}' found.", $"Available commands are:\n\n{string.Join("\n", names)}\n\nHow to bind these commands can be seen in the [default input bindings and menu definition](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt)."); Msg.ShowError($"No command '{args[1]}' found.", $"Available commands are:\n\n{string.Join("\n", names)}\n\nHow to bind these commands can be seen in the [default input bindings and menu definition](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt).");
} }
} }
ClientMessage?.Invoke(args); else if (args.Length > 0)
ClientMessage?.Invoke(args);
break; break;
case mpv_event_id.MPV_EVENT_VIDEO_RECONFIG: case mpv_event_id.MPV_EVENT_VIDEO_RECONFIG:
VideoReconfig?.Invoke(); VideoReconfig?.Invoke();
@@ -354,7 +362,7 @@ namespace mpvnet
{ {
if (IsLogoVisible) if (IsLogoVisible)
{ {
mp.commandv("overlay-remove", "0"); commandv("overlay-remove", "0");
IsLogoVisible = false; IsLogoVisible = false;
} }
} }
@@ -373,17 +381,11 @@ namespace mpvnet
MethodInfo mi; MethodInfo mi;
if (eventInfo.EventHandlerType == typeof(Action)) if (eventInfo.EventHandlerType == typeof(Action))
{
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.Invoke)); mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.Invoke));
}
else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>)) else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>))
{
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeEndFileEventMode)); mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeEndFileEventMode));
}
else if (eventInfo.EventHandlerType == typeof(Action<string[]>)) else if (eventInfo.EventHandlerType == typeof(Action<string[]>))
{
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeStrings)); mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeStrings));
}
else else
throw new Exception(); throw new Exception();
@@ -405,28 +407,19 @@ namespace mpvnet
public static void commandv(params string[] args) public static void commandv(params string[] args)
{ {
if (MpvHandle == IntPtr.Zero) if (Handle == IntPtr.Zero) return;
return;
IntPtr mainPtr = AllocateUtf8IntPtrArrayWithSentinel(args, out IntPtr[] byteArrayPointers); IntPtr mainPtr = AllocateUtf8IntPtrArrayWithSentinel(args, out IntPtr[] byteArrayPointers);
int err = mpv_command(MpvHandle, mainPtr); int err = mpv_command(Handle, mainPtr);
if (err < 0) throw new Exception($"{(mpv_error)err}");
if (err < 0)
throw new Exception($"{(mpv_error)err}");
foreach (var ptr in byteArrayPointers) foreach (var ptr in byteArrayPointers)
Marshal.FreeHGlobal(ptr); Marshal.FreeHGlobal(ptr);
Marshal.FreeHGlobal(mainPtr); Marshal.FreeHGlobal(mainPtr);
} }
public static void command_string(string command, bool throwException = false) public static void command_string(string command, bool throwException = false)
{ {
if (MpvHandle == IntPtr.Zero) if (Handle == IntPtr.Zero) return;
return; int err = mpv_command_string(Handle, command);
int err = mpv_command_string(MpvHandle, command);
if (err < 0 && throwException) if (err < 0 && throwException)
throw new Exception($"{(mpv_error)err}\r\n\r\n" + command); throw new Exception($"{(mpv_error)err}\r\n\r\n" + command);
} }
@@ -434,8 +427,7 @@ namespace mpvnet
public static void set_property_string(string name, string value, bool throwOnException = false) public static void set_property_string(string name, string value, bool throwOnException = false)
{ {
byte[] bytes = GetUtf8Bytes(value); byte[] bytes = GetUtf8Bytes(value);
int err = mpv_set_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes); int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes);
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
} }
@@ -444,26 +436,22 @@ namespace mpvnet
{ {
try try
{ {
int err = mpv_get_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer); int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer);
if (err < 0 && throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}");
string ret = StringFromNativeUtf8(lpBuffer); string ret = StringFromNativeUtf8(lpBuffer);
mpv_free(lpBuffer); mpv_free(lpBuffer);
return ret; return ret;
} }
catch (Exception ex) catch (Exception e)
{ {
if (throwOnException) throw ex; if (throwOnException) throw e;
return ""; return "";
} }
} }
public static int get_property_int(string name, bool throwOnException = false) public static int get_property_int(string name, bool throwOnException = false)
{ {
int err = mpv_get_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer); int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -474,7 +462,7 @@ namespace mpvnet
public static double get_property_number(string name, bool throwOnException = false) public static double get_property_number(string name, bool throwOnException = false)
{ {
double val = 0; double val = 0;
int err = mpv_get_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val); int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val);
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -484,7 +472,7 @@ namespace mpvnet
public static bool get_property_bool(string name, bool throwOnException = false) public static bool get_property_bool(string name, bool throwOnException = false)
{ {
int err = mpv_get_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_FLAG, out IntPtr lpBuffer); int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_FLAG, out IntPtr lpBuffer);
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -495,7 +483,7 @@ namespace mpvnet
public static void set_property_int(string name, int value, bool throwOnException = false) public static void set_property_int(string name, int value, bool throwOnException = false)
{ {
Int64 val = value; Int64 val = value;
int err = mpv_set_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val); int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val);
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -503,7 +491,7 @@ namespace mpvnet
public static void observe_property_int(string name, Action<int> action) public static void observe_property_int(string name, Action<int> action)
{ {
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_INT64); int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_INT64);
if (err < 0) if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -513,7 +501,7 @@ namespace mpvnet
public static void observe_property_bool(string name, Action<bool> action) public static void observe_property_bool(string name, Action<bool> action)
{ {
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG); int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG);
if (err < 0) if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -523,7 +511,7 @@ namespace mpvnet
public static void observe_property_string(string name, Action<string> action) public static void observe_property_string(string name, Action<string> action)
{ {
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_STRING); int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_STRING);
if (err < 0) if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
@@ -538,77 +526,72 @@ namespace mpvnet
foreach (string i in args) foreach (string i in args)
{ {
if (!i.StartsWith("--") && File.Exists(i) || i == "-" || i.StartsWith("http")) if (!i.StartsWith("--") && (i == "-" || i.Contains("://") || File.Exists(i)))
{ {
files.Add(i); files.Add(i);
if (i.StartsWith("http")) if (i.Contains("://")) RegHelp.SetObject(App.RegPath, "LastURL", i);
RegistryHelp.SetObject("HKCU\\Software\\" + Application.ProductName, "LastURL", i);
} }
} }
mp.LoadFiles(files.ToArray()); Load(files.ToArray(), App.ProcessInstance != "queue", Control.ModifierKeys.HasFlag(Keys.Control));
foreach (string i in args) foreach (string i in args)
{ {
if (i.StartsWith("--")) if (i.StartsWith("--"))
{ {
if (i.Contains("=")) try
{ {
string left = i.Substring(2, i.IndexOf("=") - 2); if (i.Contains("="))
string right = i.Substring(left.Length + 3); {
mp.set_property_string(left, right); string left = i.Substring(2, i.IndexOf("=") - 2);
string right = i.Substring(left.Length + 3);
set_property_string(left, right, true);
}
else
set_property_string(i.Substring(2), "yes", true);
}
catch (Exception e)
{
Msg.ShowException(e);
} }
else
mp.set_property_string(i.Substring(2), "yes");
} }
} }
} }
public static void LoadFiles(params string[] files) public static void Load(string[] files, bool loadFolder, bool append)
{ {
if (files is null || files.Length == 0) return; if (files is null || files.Length == 0) return;
HideLogo(); HideLogo();
List<string> fileList = files.ToList();
foreach (string file in files) { for (int i = 0; i < files.Length; i++)
string ext = Path.GetExtension(file).TrimStart('.').ToLower(); if (App.SubtitleTypes.Contains(Path.GetExtension(files[i]).TrimStart('.').ToLower()))
if (App.SubtitleTypes.Contains(ext)) { commandv("sub-add", files[i]);
mp.commandv("sub-add", file); else
fileList.Remove(file); if (i == 0 && !append)
} commandv("loadfile", files[i]);
} else
commandv("loadfile", files[i], "append");
if (fileList.Count == 0) return; if (string.IsNullOrEmpty(get_property_string("path")))
files = fileList.ToArray(); set_property_int("playlist-pos", 0);
int count = mp.get_property_int("playlist-count"); if (loadFolder && !append) Task.Run(() => LoadFolder()); // user reported race condition
foreach (string file in files)
mp.commandv("loadfile", file, "append");
mp.set_property_int("playlist-pos", count);
for (int i = 0; i < count; i++)
mp.commandv("playlist-remove", "0");
mp.LoadFolder(files[0]);
} }
static void LoadFolder(string path) public static void LoadFolder()
{ {
if (get_property_int("playlist-count") == 1) Thread.Sleep(50); // user reported race condition
{ string path = get_property_string("path");
if (!File.Exists(path)) return; if (!File.Exists(path) || get_property_int("playlist-count") != 1) return;
List<string> files = Directory.GetFiles(Path.GetDirectoryName(path)).ToList(); List<string> files = Directory.GetFiles(Path.GetDirectoryName(path)).ToList();
files = files.Where((file) => App.VideoTypes.Contains(Path.GetExtension(file).TrimStart('.').ToLower()) || files = files.Where((file) =>
App.AudioTypes.Contains(Path.GetExtension(file).TrimStart('.').ToLower())).ToList(); App.VideoTypes.Contains(Path.GetExtension(file).TrimStart('.').ToLower()) ||
files.Sort(new StringLogicalComparer()); App.AudioTypes.Contains(Path.GetExtension(file).TrimStart('.').ToLower())).ToList();
int index = files.IndexOf(path); files.Sort(new StringLogicalComparer());
files.Remove(path); int index = files.IndexOf(path);
foreach (string i in files) files.Remove(path);
commandv("loadfile", i, "append"); foreach (string i in files)
if (index > 0) commandv("loadfile", i, "append");
commandv("playlist-move", "0", (index + 1).ToString()); if (index > 0) commandv("playlist-move", "0", (index + 1).ToString());
}
} }
static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers) static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
@@ -629,16 +612,16 @@ namespace mpvnet
return rootPointer; return rootPointer;
} }
static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr pUnmanagedStringArray, int StringCount) static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr unmanagedStringArray, int StringCount)
{ {
IntPtr[] pIntPtrArray = new IntPtr[StringCount]; IntPtr[] intPtrArray = new IntPtr[StringCount];
string[] ManagedStringArray = new string[StringCount]; string[] stringArray = new string[StringCount];
Marshal.Copy(pUnmanagedStringArray, pIntPtrArray, 0, StringCount); Marshal.Copy(unmanagedStringArray, intPtrArray, 0, StringCount);
for (int i = 0; i < StringCount; i++) for (int i = 0; i < StringCount; i++)
ManagedStringArray[i] = StringFromNativeUtf8(pIntPtrArray[i]); stringArray[i] = StringFromNativeUtf8(intPtrArray[i]);
return ManagedStringArray; return stringArray;
} }
static string StringFromNativeUtf8(IntPtr nativeUtf8) static string StringFromNativeUtf8(IntPtr nativeUtf8)
@@ -661,7 +644,7 @@ namespace mpvnet
if (File.Exists(LastHistoryPath) && totalMinutes > 1) if (File.Exists(LastHistoryPath) && totalMinutes > 1)
{ {
string historyFilepath = mp.MpvConfFolder + "history.txt"; string historyFilepath = ConfFolder + "history.txt";
File.AppendAllText(historyFilepath, DateTime.Now.ToString().Substring(0, 16) + File.AppendAllText(historyFilepath, DateTime.Now.ToString().Substring(0, 16) +
" " + totalMinutes.ToString().PadLeft(3) + " " + " " + totalMinutes.ToString().PadLeft(3) + " " +
@@ -688,7 +671,7 @@ namespace mpvnet
Rectangle r = new Rectangle(cr.Width / 2 - iconWidth / 2, cr.Height / 2 - iconWidth / 2, iconWidth, iconWidth); Rectangle r = new Rectangle(cr.Width / 2 - iconWidth / 2, cr.Height / 2 - iconWidth / 2, iconWidth, iconWidth);
g.DrawImage(Properties.Resources.mpvnet, r); g.DrawImage(Properties.Resources.mpvnet, r);
BitmapData bd = b.LockBits(cr, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb); BitmapData bd = b.LockBits(cr, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
mp.commandv("overlay-add", "0", "0", "0", "&" + bd.Scan0.ToInt64().ToString(), "0", "bgra", bd.Width.ToString(), bd.Height.ToString(), bd.Stride.ToString()); commandv("overlay-add", "0", "0", "0", "&" + bd.Scan0.ToInt64().ToString(), "0", "bgra", bd.Width.ToString(), bd.Height.ToString(), bd.Stride.ToString());
b.UnlockBits(bd); b.UnlockBits(bd);
IsLogoVisible = true; IsLogoVisible = true;
} }
@@ -701,7 +684,7 @@ namespace mpvnet
{ {
MediaTracks.Clear(); MediaTracks.Clear();
using (MediaInfo mi = new MediaInfo(mp.get_property_string("path"))) using (MediaInfo mi = new MediaInfo(get_property_string("path")))
{ {
int count = mi.GetCount(MediaInfoStreamKind.Video); int count = mi.GetCount(MediaInfoStreamKind.Video);