Compare commits

...

10 Commits
1.0.0.0 ... 1.2

Author SHA1 Message Date
Frank Skare
3fd1285ad8 - 2019-03-17 20:56:10 +01:00
Frank Skare
91a67c29a7 - 2019-03-14 21:40:08 +01:00
Frank Skare
c8ce0b6dfc 1.1 2019-03-14 07:02:38 +01:00
Frank Skare
c075f4982b 1.1 2019-03-14 05:22:39 +01:00
Frank Skare
303355ce63 1.2 2019-03-14 05:19:57 +01:00
Frank Skare
a392c2c6da - 2019-03-13 22:22:51 +01:00
Frank Skare
72d6f44e47 - 2019-03-12 01:21:31 +01:00
Frank Skare
2e695e43bf 1.0.0.0 2019-03-11 22:05:30 +01:00
Frank Skare
207c2fc685 1.0.0.0 2019-03-11 21:59:26 +01:00
Frank Skare
3939dc7924 1.0.0.0 2019-03-11 21:33:37 +01:00
26 changed files with 456 additions and 159 deletions

View File

@@ -11,7 +11,7 @@ Public Class CSScriptAddon
Implements IAddon
Sub New()
Dim scriptDir = mpv.mpvConfFolderPath + "scripts"
Dim scriptDir = mp.mpvConfFolderPath + "scripts"
If Not Directory.Exists(scriptDir) Then Return
Dim csFiles = Directory.GetFiles(scriptDir, "*.cs")
If csFiles.Count = 0 Then Return

View File

@@ -52,11 +52,6 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>.\CSScriptLibrary.dll</HintPath>
</Reference>
<Reference Include="mpvnet, Version=0.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\mpv.net\bin\Debug\mpvnet.exe</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" />
@@ -117,5 +112,12 @@
<ItemGroup>
<Content Include="CSScriptLibrary.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\mpv.net\mpv.net.csproj">
<Project>{1751f378-8edf-4b62-be6d-304c7c287089}</Project>
<Name>mpv.net</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -8,11 +8,11 @@ Imports System.Runtime.InteropServices
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("PowerShellAddon")>
<Assembly: AssemblyTitle("CSScriptAddon")>
<Assembly: AssemblyDescription("")>
<Assembly: AssemblyCompany("")>
<Assembly: AssemblyProduct("PowerShellAddon")>
<Assembly: AssemblyCopyright("Copyright © 2017")>
<Assembly: AssemblyProduct("CSScriptAddon")>
<Assembly: AssemblyCopyright("Copyright © 2019")>
<Assembly: AssemblyTrademark("")>
<Assembly: ComVisible(False)>

View File

@@ -8,12 +8,13 @@ mpv manual: https://mpv.io/manual/master/
### Features
- mpv's OSC, IPC, Lua/JS, conf files and more
- Customizable context menu defined in the same file as the keybindings
- Addons support for using .NET languages
- C# scripts implemented with CS-Script
- Addon API for .NET languages
- Python scripting implemented with IronPython
- C# scripting implemented with CS-Script
- mpv's OSC, IPC, Lua/JS, conf files and more
![](https://github.com/stax76/mpv.net/blob/master/mpv.net/screenshot.jpg)
![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshot.png)
### Context Menu
@@ -48,8 +49,46 @@ class Script
}
```
### Python Scripting
A simple Python script located at: C:\Users\user\AppData\Roaming\mpv\scripts
```
# when seeking displays position and
# duration like so: 70:00 / 80:00
# which is different from mpv which
# uses 01:10:00 / 01:20:00
import math
def seek():
mp.commandv('show-text',
format(mp.get_property_number("time-pos")) + " / " + format(mp.get_property_number("duration")))
def format(f):
sec = round(f)
if sec < 0:
sec = 0
pos_min_floor = math.floor(sec / 60)
sec_rest = sec - pos_min_floor * 60
return add_zero(pos_min_floor) + ":" + add_zero(sec_rest)
def add_zero(val):
val = round(val)
return "" + str(int(val)) if (val > 9) else "0" + str(int(val))
mp.register_event("seek", seek) # or use: mp.Seek += seek
```
### Changes
### 1.1
- added support for Python scripting via IronPython
- show tracks and show playlist didn't work because the duration wasn't defined in the key bindings
### 1.0
- much more feature packed context menu

View File

@@ -14,8 +14,8 @@ namespace RatingAddon
public RatingAddon()
{
mpv.ClientMessage += mpv_ClientMessage;
mpv.Shutdown += mpv_Shutdown;
mp.ClientMessage += mpv_ClientMessage;
mp.Shutdown += mpv_Shutdown;
}
private void mpv_Shutdown()
@@ -51,8 +51,8 @@ namespace RatingAddon
if (args?.Length != 2 || args[0] != "rate-file" || ! int.TryParse(args[1], out rating))
return;
Dic[mpv.GetStringProp("path")] = rating;
mpv.Command("show-text", $"Rating: {rating}");
Dic[mp.GetStringProp("path")] = rating;
mp.Command("show-text", $"Rating: {rating}");
}
}
}

View File

@@ -67,10 +67,6 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="mpvnet">
<HintPath>..\mpv.net\bin\Debug\mpvnet.exe</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
@@ -85,5 +81,12 @@
<Compile Include="RatingAddon.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\mpv.net\mpv.net.csproj">
<Project>{1751f378-8edf-4b62-be6d-304c7c287089}</Project>
<Name>mpv.net</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,31 +0,0 @@
// when seeking displays position and
// duration like so: 00:00 / 120:00
// this is different from MPC which
// uses 00:00:00 / 02:00:00
function add_zero(val)
{
val = Math.round(val);
return val > 9 ? "" + val : "0" + val;
}
function format(val)
{
var sec = Math.round(val);
if (sec < 0)
sec = 0;
pos_min_floor = Math.floor(sec / 60);
sec_rest = sec - pos_min_floor * 60;
return add_zero(pos_min_floor) + ":" + add_zero(sec_rest);
}
function on_seek(_)
{
mp.commandv("show-text",
format(mp.get_property_number("time-pos")) + " / " +
format(mp.get_property_number("duration")));
}
mp.register_event("seek", on_seek);

View File

@@ -4,10 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 15.0.26730.8
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mpv.net", "mpv.net\mpv.net.csproj", "{1751F378-8EDF-4B62-BE6D-304C7C287089}"
ProjectSection(ProjectDependencies) = postProject
{55C88710-539D-4402-84C8-31694841C731} = {55C88710-539D-4402-84C8-31694841C731}
{71808A87-8B1C-4DF8-957C-D79C3B164CCA} = {71808A87-8B1C-4DF8-957C-D79C3B164CCA}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RatingAddon", "RatingAddon\RatingAddon.csproj", "{55C88710-539D-4402-84C8-31694841C731}"
EndProject

View File

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

View File

@@ -50,41 +50,41 @@ namespace mpvnet
d.Filter = Misc.GetFilter(Misc.FileTypes);
if (d.ShowDialog() == DialogResult.OK)
mpv.LoadFiles(d.FileNames);
mp.LoadFiles(d.FileNames);
}
}));
}
public static void open_config_folder(string[] args)
{
Process.Start(mpv.mpvConfFolderPath);
Process.Start(mp.mpvConfFolderPath);
}
public static void show_keys(string[] args)
{
Process.Start(mpv.InputConfPath);
Process.Start(mp.InputConfPath);
}
private static void CreateMpvConf()
{
if (!File.Exists(mpv.mpvConfPath))
if (!File.Exists(mp.mpvConfPath))
{
if (!Directory.Exists(mpv.mpvConfFolderPath))
Directory.CreateDirectory(mpv.mpvConfFolderPath);
if (!Directory.Exists(mp.mpvConfFolderPath))
Directory.CreateDirectory(mp.mpvConfFolderPath);
File.WriteAllText(mpv.mpvConfPath, "# https://mpv.io/manual/master/#configuration-files");
File.WriteAllText(mp.mpvConfPath, "# https://mpv.io/manual/master/#configuration-files");
}
}
public static void show_prefs(string[] args)
{
CreateMpvConf();
Process.Start(mpv.mpvConfPath);
Process.Start(mp.mpvConfPath);
}
public static void history(string[] args)
{
var fp = mpv.mpvConfFolderPath + "history.txt";
var fp = mp.mpvConfFolderPath + "history.txt";
if (File.Exists(fp))
Process.Start(fp);
@@ -103,7 +103,7 @@ namespace mpvnet
CreateMpvConf();
bool changed = false;
string fp = mpv.mpvConfPath;
string fp = mp.mpvConfPath;
var confLines = File.ReadAllLines(fp);
for (int i = 0; i < confLines.Length; i++)
@@ -130,21 +130,21 @@ namespace mpvnet
public static void show_info(string[] args)
{
var fi = new FileInfo(mpv.GetStringProp("path"));
var fi = new FileInfo(mp.GetStringProp("path"));
using (var mi = new MediaInfo(fi.FullName))
{
var w = mi.GetInfo(MediaInfoStreamKind.Video, "Width");
var h = mi.GetInfo(MediaInfoStreamKind.Video, "Height");
var pos = TimeSpan.FromSeconds(mpv.GetIntProp("time-pos"));
var dur = TimeSpan.FromSeconds(mpv.GetIntProp("duration"));
var pos = TimeSpan.FromSeconds(mp.GetIntProp("time-pos"));
var dur = TimeSpan.FromSeconds(mp.GetIntProp("duration"));
string mibr = mi.GetInfo(MediaInfoStreamKind.Video, "BitRate");
if (mibr == "")
mibr = "0";
var br = Convert.ToInt32(mibr) / 1000.0 / 1000.0;
var vf = mpv.GetStringProp("video-format").ToUpper();
var vf = mp.GetStringProp("video-format").ToUpper();
var fn = fi.Name;
if (fn.Length > 60)
@@ -158,7 +158,7 @@ namespace mpvnet
((int)(fi.Length / 1024 / 1024)).ToString() +
$" MB - {w} x {h}\n{vf} - {br.ToString("f1")} Mb/s" + "\n" + fn;
mpv.Command("show-text", info, "5000");
mp.Command("show-text", info, "5000");
string FormatTime(double value) => ((int)(Math.Floor(value))).ToString("00");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -52,7 +52,6 @@
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "mpv.net";
this.Load += new System.EventHandler(this.MainForm_Load);
this.ResumeLayout(false);
}

View File

@@ -4,7 +4,7 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
using static mpvnet.StaticUsing;
namespace mpvnet
@@ -16,7 +16,7 @@ namespace mpvnet
private Point LastCursorPosChanged;
private int LastCursorChangedTickCount;
private bool IsCloseRequired = true;
private bool IsClosed;
public ContextMenuStripEx CMS;
@@ -30,7 +30,7 @@ namespace mpvnet
SetFormPosSize();
Instance = this;
Hwnd = Handle;
ChangeFullscreen((mpv.mpvConv.ContainsKey("fullscreen") && mpv.mpvConv["fullscreen"] == "yes") || (mpv.mpvConv.ContainsKey("fs") && mpv.mpvConv["fs"] == "yes"));
ChangeFullscreen((mp.mpvConv.ContainsKey("fullscreen") && mp.mpvConv["fullscreen"] == "yes") || (mp.mpvConv.ContainsKey("fs") && mp.mpvConv["fs"] == "yes"));
CMS = new ContextMenuStripEx(components);
CMS.Opened += CMS_Opened;
ContextMenuStrip = CMS;
@@ -44,15 +44,15 @@ namespace mpvnet
public void BuildMenu()
{
if (!File.Exists(mpv.InputConfPath))
if (!File.Exists(mp.InputConfPath))
{
if (!Directory.Exists(mpv.mpvConfFolderPath))
Directory.CreateDirectory(mpv.mpvConfFolderPath);
if (!Directory.Exists(mp.mpvConfFolderPath))
Directory.CreateDirectory(mp.mpvConfFolderPath);
File.WriteAllText(mpv.InputConfPath, Properties.Resources.input_conf);
File.WriteAllText(mp.InputConfPath, Properties.Resources.input_conf);
}
foreach (var i in File.ReadAllText(mpv.InputConfPath).SplitLinesNoEmpty())
foreach (var i in File.ReadAllText(mp.InputConfPath).SplitLinesNoEmpty())
{
if (!i.Contains("#menu:"))
continue;
@@ -73,7 +73,7 @@ namespace mpvnet
var menuItem = CMS.Add(path, () => {
try
{
mpv.CommandString(cmd, false);
mp.CommandString(cmd, false);
}
catch (Exception e)
{
@@ -93,11 +93,11 @@ namespace mpvnet
private string LastHistory;
private void mpv_PlaybackRestart()
private void mp_PlaybackRestart()
{
var fn = mpv.GetStringProp("filename");
var fn = mp.GetStringProp("filename");
BeginInvoke(new Action(() => { Text = fn + " - mpv.net " + Application.ProductVersion; }));
var fp = mpv.mpvConfFolderPath + "history.txt";
var fp = mp.mpvConfFolderPath + "history.txt";
if (LastHistory != fn && File.Exists(fp))
{
@@ -121,14 +121,14 @@ namespace mpvnet
MsgError(e.ToString());
}
private void Mpv_VideoSizeChanged()
private void mp_VideoSizeChanged()
{
BeginInvoke(new Action(() => SetFormPosSize()));
}
private void Mpv_AfterShutdown()
private void mp_Shutdown()
{
if (IsCloseRequired)
if (!IsClosed)
Invoke(new Action(() => Close()));
}
@@ -137,7 +137,7 @@ namespace mpvnet
get => WindowState == FormWindowState.Maximized;
}
void MpvChangeFullscreen(bool value)
void mp_ChangeFullscreen(bool value)
{
BeginInvoke(new Action(() => ChangeFullscreen(value)));
}
@@ -166,19 +166,19 @@ namespace mpvnet
case 0x0100: // WM_KEYDOWN
case 0x0101: // WM_KEYUP
case 0x020A: // WM_MOUSEWHEEL
if (mpv.MpvWindowHandle != IntPtr.Zero)
Native.SendMessage(mpv.MpvWindowHandle, m.Msg, m.WParam, m.LParam);
if (mp.MpvWindowHandle != IntPtr.Zero)
Native.SendMessage(mp.MpvWindowHandle, m.Msg, m.WParam, m.LParam);
break;
case 0x203: // Native.WM.LBUTTONDBLCLK
if (!IsMouseInOSC())
mpv.CommandString("cycle fullscreen");
mp.CommandString("cycle fullscreen");
break;
case 0x0214: // WM_SIZING
var rc = Marshal.PtrToStructure<Native.RECT>(m.LParam);
var r = rc;
NativeHelp.SubtractWindowBorders(Handle, ref r);
int c_w = r.Right - r.Left, c_h = r.Bottom - r.Top;
float aspect = mpv.VideoSize.Width / (float)mpv.VideoSize.Height;
float aspect = mp.VideoSize.Width / (float)mp.VideoSize.Height;
int d_w = (int)(c_h * aspect - c_w);
int d_h = (int)(c_w / aspect - c_h);
int[] d_corners = { d_w, d_h, -d_w, -d_h };
@@ -198,10 +198,10 @@ namespace mpvnet
void SetFormPosSize()
{
if (IsFullscreen || mpv.VideoSize.Width == 0) return;
if (IsFullscreen || mp.VideoSize.Width == 0) return;
var wa = Screen.GetWorkingArea(this);
int h = (int)(wa.Height * 0.6);
int w = (int)(h * mpv.VideoSize.Width / (float)mpv.VideoSize.Height);
int w = (int)(h * mp.VideoSize.Width / (float)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var r = new Native.RECT(new Rectangle(0, 0, w, h));
NativeHelp.AddWindowBorders(Handle, ref r);
@@ -227,7 +227,7 @@ namespace mpvnet
base.OnDragDrop(e);
if (e.Data.GetDataPresent(DataFormats.FileDrop))
mpv.LoadFiles(e.Data.GetData(DataFormats.FileDrop) as String[]);
mp.LoadFiles(e.Data.GetData(DataFormats.FileDrop) as String[]);
}
protected override void OnMouseDown(MouseEventArgs e)
@@ -249,7 +249,7 @@ namespace mpvnet
var p2 = PointToScreen(e.Location);
if (Math.Abs(p1.X - p2.X) < 10 && Math.Abs(p1.Y - p2.Y) < 10)
mpv.Command("quit");
mp.Command("quit");
}
protected override void OnMouseMove(MouseEventArgs e)
@@ -257,19 +257,12 @@ namespace mpvnet
base.OnMouseMove(e);
// send mouse command to make OSC show
mpv.CommandString($"mouse {e.X} {e.Y}");
mp.CommandString($"mouse {e.X} {e.Y}");
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
CursorHelp.Show();
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
IsCloseRequired = false;
mpv.Command("quit");
}
bool IsMouseInOSC()
{
return PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
@@ -290,13 +283,27 @@ namespace mpvnet
}
}
private void MainForm_Load(object sender, EventArgs ea)
protected override void OnLoad(EventArgs e)
{
mpv.Init();
mpv.ObserveBoolProp("fullscreen", MpvChangeFullscreen);
mpv.AfterShutdown += Mpv_AfterShutdown;
mpv.VideoSizeChanged += Mpv_VideoSizeChanged;
mpv.PlaybackRestart += mpv_PlaybackRestart;
base.OnLoad(e);
mp.Init();
mp.ObserveBoolProp("fullscreen", mp_ChangeFullscreen);
mp.Shutdown += mp_Shutdown;
mp.VideoSizeChanged += mp_VideoSizeChanged;
mp.PlaybackRestart += mp_PlaybackRestart;
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
IsClosed = true;
mp.Command("quit");
for (int i = 0; i < 99; i++)
{
if (mp.IsShutdownComplete) break;
Thread.Sleep(50);
}
}
}
}

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.1.0.0")]

33
mpv.net/PyScript.cs Normal file
View File

@@ -0,0 +1,33 @@
using System;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
using static mpvnet.StaticUsing;
namespace mpvnet
{
public class PyScript
{
ScriptEngine engine;
ScriptScope scope;
public PyScript(string code)
{
try
{
engine = Python.CreateEngine();
scope = engine.CreateScope();
scope.ImportModule("clr");
engine.Execute("import clr", scope);
engine.Execute("clr.AddReference(\"mpvnet\")", scope);
engine.Execute("from mpvnet import *", scope);
engine.Execute(code, scope);
}
catch (Exception ex)
{
MsgError(ex.ToString());
}
}
}
}

View File

@@ -104,7 +104,7 @@
k script-message mpv.net show-keys #menu: K ; Settings > Show Keys
c script-message mpv.net open-config-folder #menu: C ; Settings > Open Config Folder
i show-progress ; script-message mpv.net show-info #menu: I ; Tools | Info
i script-message mpv.net show-info #menu: I ; Tools | Info
t script-binding stats/display-stats #menu: T ; Tools > Show Statistics
T script-binding stats/display-stats-toggle #menu: Shift+T ; Tools > Toggle Statistics
_ ignore #menu: _ ; Tools > -
@@ -113,8 +113,8 @@
L cycle-values loop-file "inf" "no" #menu: Shift+L ; Tools > Toggle Infinite Looping
DEL script-binding osc/visibility #menu: Delete ; Tools > Toggle OSC Visibility
Ctrl+H cycle-values hwdec "auto" "no" #menu: Ctrl+H ; Tools > Cycle Hardware Decoding
F8 show_text ${playlist} #menu: F8 ; Tools > Show Playlist
F9 show_text ${track-list} #menu: F9 ; Tools > Show Audio/Video/Subtitle List
F8 show-text ${playlist} 5000 #menu: F8 ; Tools > Show Playlist
F9 show-text ${track-list} 5000 #menu: F9 ; Tools > Show Audio/Video/Subtitle List
_ script-message mpv.net shell-execute https://mpv.io/manual/stable/ #menu: _ ; Tools > Web > Show mpv manual
_ script-message mpv.net shell-execute https://github.com/mpv-player/mpv/blob/master/etc/input.conf #menu: _ ; Tools > Web > Show mpv default keys

View File

@@ -29,6 +29,9 @@ namespace mpvnet
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref IntPtr data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref double data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref byte[] data);
@@ -102,7 +105,8 @@ namespace mpvnet
MPV_EVENT_PLAYBACK_RESTART = 21,
MPV_EVENT_PROPERTY_CHANGE = 22,
MPV_EVENT_CHAPTER_CHANGE = 23,
MPV_EVENT_QUEUE_OVERFLOW = 24
MPV_EVENT_QUEUE_OVERFLOW = 24,
MPV_EVENT_HOOK = 25
}
public enum mpv_format
@@ -131,6 +135,15 @@ namespace mpvnet
MPV_LOG_LEVEL_TRACE = 70,
}
public enum mpv_end_file_reason
{
MPV_END_FILE_REASON_EOF = 0,
MPV_END_FILE_REASON_STOP = 2,
MPV_END_FILE_REASON_QUIT = 3,
MPV_END_FILE_REASON_ERROR = 4,
MPV_END_FILE_REASON_REDIRECT = 5
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message
{
@@ -159,9 +172,16 @@ namespace mpvnet
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_property
{
[MarshalAs(UnmanagedType.LPUTF8Str)] public string name;
public string name;
public mpv_format format;
public IntPtr data;
}
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_end_file
{
public int reason;
public int error;
}
}
}

View File

@@ -1,37 +1,67 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static mpvnet.libmpv;
using static mpvnet.Native;
using static mpvnet.StaticUsing;
using PyRT = IronPython.Runtime;
namespace mpvnet
{
public delegate void MpvBoolPropChangeHandler(string propName, bool value);
public class mpv
public class mp
{
public static event Action<string[]> ClientMessage;
public static event Action Shutdown;
public static event Action AfterShutdown;
public static event Action PlaybackRestart;
public static event Action VideoSizeChanged;
// Lua/JS evens libmpv events
// MPV_EVENT_NONE
public static event Action Shutdown; // shutdown MPV_EVENT_SHUTDOWN
public static event Action LogMessage; // log-message MPV_EVENT_LOG_MESSAGE
public static event Action GetPropertyReply; // get-property-reply MPV_EVENT_GET_PROPERTY_REPLY
public static event Action SetPropertyReply; // set-property-reply MPV_EVENT_SET_PROPERTY_REPLY
public static event Action CommandReply; // command-reply MPV_EVENT_COMMAND_REPLY
public static event Action StartFile; // start-file MPV_EVENT_START_FILE
public static event Action<EndFileEventMode> EndFile; // end-file MPV_EVENT_END_FILE
public static event Action FileLoaded; // file-loaded MPV_EVENT_FILE_LOADED
public static event Action TracksChanged; // MPV_EVENT_TRACKS_CHANGED
public static event Action TrackSwitched; // MPV_EVENT_TRACK_SWITCHED
public static event Action Idle; // idle MPV_EVENT_IDLE
public static event Action Pause; // MPV_EVENT_PAUSE
public static event Action Unpause; // MPV_EVENT_UNPAUSE
public static event Action Tick; // tick MPV_EVENT_TICK
public static event Action ScriptInputDispatch; // MPV_EVENT_SCRIPT_INPUT_DISPATCH
public static event Action<string[]> ClientMessage; // client-message MPV_EVENT_CLIENT_MESSAGE
public static event Action VideoReconfig; // video-reconfig MPV_EVENT_VIDEO_RECONFIG
public static event Action AudioReconfig; // audio-reconfig MPV_EVENT_AUDIO_RECONFIG
public static event Action MetadataUpdate; // MPV_EVENT_METADATA_UPDATE
public static event Action Seek; // seek MPV_EVENT_SEEK
public static event Action PlaybackRestart; // playback-restart MPV_EVENT_PLAYBACK_RESTART
// MPV_EVENT_PROPERTY_CHANGE
public static event Action ChapterChange; // MPV_EVENT_CHAPTER_CHANGE
public static event Action QueueOverflow; // MPV_EVENT_QUEUE_OVERFLOW
public static event Action Hook; // MPV_EVENT_HOOK
public static IntPtr MpvHandle;
public static IntPtr MpvWindowHandle;
public static Addon Addon;
public static List<Action<bool>> BoolPropChangeActions = new List<Action<bool>>();
public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions = new List<KeyValuePair<string, Action<bool>>>();
public static Size VideoSize = new Size(1920, 1080);
public static string mpvConfFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\";
public static string InputConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\input.conf";
public static string mpvConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpv.conf";
public static List<PyScript> PyScripts { get; } = new List<PyScript>();
public static bool IsShutdownComplete { get; set; }
private static Dictionary<string, string> _mpvConv;
@@ -76,17 +106,34 @@ namespace mpvnet
SetStringProp("force-window", "yes");
mpv_initialize(MpvHandle);
ProcessCommandLine();
Task.Run(() => { LoadScripts(); });
Task.Run(() => { Addon = new Addon(); });
Task.Run(() => { EventLoop(); });
}
public static void LoadScripts()
{
string[] jsLua = { ".lua", ".js" };
string[] startupScripts = Directory.GetFiles(Application.StartupPath + "\\Scripts");
foreach (var scriptPath in startupScripts)
if (jsLua.Contains(Path.GetExtension(scriptPath).ToLower()))
mp.Command("load-script", $"{scriptPath}");
foreach (var scriptPath in startupScripts)
if (Path.GetExtension(scriptPath) == ".py")
PyScripts.Add(new PyScript(File.ReadAllText(scriptPath)));
foreach(var scriptPath in Directory.GetFiles(mp.mpvConfFolderPath + "scripts", "*.py"))
PyScripts.Add(new PyScript(File.ReadAllText(scriptPath)));
}
public static void EventLoop()
{
while (true)
{
IntPtr ptr = mpv_wait_event(MpvHandle, -1);
mpv_event evt = (mpv_event)Marshal.PtrToStructure(ptr, typeof(mpv_event));
Debug.WriteLine(evt.event_id);
if (MpvWindowHandle == IntPtr.Zero)
MpvWindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null);
@@ -95,22 +142,51 @@ namespace mpvnet
{
case mpv_event_id.MPV_EVENT_SHUTDOWN:
Shutdown?.Invoke();
AfterShutdown?.Invoke();
IsShutdownComplete = true;
return;
case mpv_event_id.MPV_EVENT_LOG_MESSAGE:
LogMessage?.Invoke();
break;
case mpv_event_id.MPV_EVENT_GET_PROPERTY_REPLY:
GetPropertyReply?.Invoke();
break;
case mpv_event_id.MPV_EVENT_SET_PROPERTY_REPLY:
SetPropertyReply?.Invoke();
break;
case mpv_event_id.MPV_EVENT_COMMAND_REPLY:
CommandReply?.Invoke();
break;
case mpv_event_id.MPV_EVENT_START_FILE:
StartFile?.Invoke();
break;
case mpv_event_id.MPV_EVENT_END_FILE:
var end_fileData = (mpv_event_end_file)Marshal.PtrToStructure(evt.data, typeof(mpv_event_end_file));
EndFile?.Invoke((EndFileEventMode)end_fileData.reason);
break;
case mpv_event_id.MPV_EVENT_FILE_LOADED:
FileLoaded?.Invoke();
LoadFolder();
break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
PlaybackRestart?.Invoke();
Size s = new Size(GetIntProp("dwidth", false), GetIntProp("dheight", false));
if (VideoSize != s && s != Size.Empty)
{
VideoSize = s;
VideoSizeChanged?.Invoke();
}
case mpv_event_id.MPV_EVENT_TRACKS_CHANGED:
TracksChanged?.Invoke();
break;
case mpv_event_id.MPV_EVENT_TRACK_SWITCHED:
TrackSwitched?.Invoke();
break;
case mpv_event_id.MPV_EVENT_IDLE:
Idle?.Invoke();
break;
case mpv_event_id.MPV_EVENT_PAUSE:
Pause?.Invoke();
break;
case mpv_event_id.MPV_EVENT_UNPAUSE:
Unpause?.Invoke();
break;
case mpv_event_id.MPV_EVENT_TICK:
Tick?.Invoke();
break;
case mpv_event_id.MPV_EVENT_SCRIPT_INPUT_DISPATCH:
ScriptInputDispatch?.Invoke();
break;
case mpv_event_id.MPV_EVENT_CLIENT_MESSAGE:
if (ClientMessage != null)
@@ -129,22 +205,122 @@ namespace mpvnet
{
MsgError(ex.GetType().Name + "\r\n\r\n" + ex.ToString());
}
ClientMessage?.Invoke(args);
}
break;
case mpv_event_id.MPV_EVENT_VIDEO_RECONFIG:
VideoReconfig?.Invoke();
break;
case mpv_event_id.MPV_EVENT_AUDIO_RECONFIG:
AudioReconfig?.Invoke();
break;
case mpv_event_id.MPV_EVENT_METADATA_UPDATE:
MetadataUpdate?.Invoke();
break;
case mpv_event_id.MPV_EVENT_SEEK:
Seek?.Invoke();
break;
case mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
var eventData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
var event_propertyData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
if (eventData.format == mpv_format.MPV_FORMAT_FLAG)
foreach (var action in BoolPropChangeActions)
action.Invoke(Marshal.PtrToStructure<int>(eventData.data) == 1);
if (event_propertyData.format == mpv_format.MPV_FORMAT_FLAG)
foreach (var i in BoolPropChangeActions)
if (i.Key== event_propertyData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(event_propertyData.data) == 1);
break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
PlaybackRestart?.Invoke();
Size s = new Size(GetIntProp("dwidth", false), GetIntProp("dheight", false));
if (VideoSize != s && s != Size.Empty)
{
VideoSize = s;
VideoSizeChanged?.Invoke();
}
break;
case mpv_event_id.MPV_EVENT_CHAPTER_CHANGE:
ChapterChange?.Invoke();
break;
case mpv_event_id.MPV_EVENT_QUEUE_OVERFLOW:
QueueOverflow?.Invoke();
break;
case mpv_event_id.MPV_EVENT_HOOK:
Hook?.Invoke();
break;
}
}
}
public class EventObject
{
public PyRT.PythonFunction PythonFunction { get; set; }
public EventInfo EventInfo { get; set; }
public Delegate Delegate { get; set; }
public void Invoke()
{
PyRT.Operations.PythonCalls.Call(PythonFunction);
}
public void InvokeEndFileEventMode(EndFileEventMode arg)
{
PyRT.Operations.PythonCalls.Call(PythonFunction, new[] { arg });
}
public void InvokeStrings(string[] arg)
{
PyRT.Operations.PythonCalls.Call(PythonFunction, new[] { arg });
}
}
private static List<EventObject> EventObjects = new List<EventObject>();
public static void register_event(string name, PyRT.PythonFunction pyFunc)
{
foreach (var eventInfo in typeof(mp).GetEvents())
{
if (eventInfo.Name.ToLower() == name.Replace("-", ""))
{
EventObject eventObject = new EventObject();
EventObjects.Add(eventObject);
eventObject.PythonFunction = pyFunc;
MethodInfo mi;
if (eventInfo.EventHandlerType == typeof(Action))
{
mi = eventObject.GetType().GetMethod(nameof(EventObject.Invoke));
}
else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>))
{
mi = eventObject.GetType().GetMethod(nameof(EventObject.InvokeEndFileEventMode));
}
else if (eventInfo.EventHandlerType == typeof(Action<string[]>))
{
mi = eventObject.GetType().GetMethod(nameof(EventObject.InvokeStrings));
}
else
throw new Exception();
eventObject.EventInfo = eventInfo;
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventObject, mi);
eventObject.Delegate = handler;
eventInfo.AddEventHandler(eventObject, handler);
}
}
}
public static void unregister_event(PyRT.PythonFunction pyFunc)
{
foreach (var eventObjects in EventObjects)
if (eventObjects.PythonFunction == pyFunc)
eventObjects.EventInfo.RemoveEventHandler(eventObjects, eventObjects.Delegate);
}
public static void commandv(params string[] args)
{
Command(args);
}
public static void Command(params string[] args)
{
if (MpvHandle == IntPtr.Zero)
@@ -208,6 +384,22 @@ namespace mpvnet
return lpBuffer.ToInt32();
}
public static double get_property_number(string name)
{
return GetDoubleProp(name);
}
public static double GetDoubleProp(string name, bool throwException = true)
{
double val = 0;
int err = mpv_get_property(MpvHandle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val);
if (err < 0 && throwException)
throw new Exception($"{name}: {(mpv_error)err}");
else
return val;
}
public static void SetIntProp(string name, int value)
{
Int64 val = value;
@@ -219,16 +411,20 @@ namespace mpvnet
public static void ObserveBoolProp(string name, Action<bool> action)
{
BoolPropChangeActions.Add(action);
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
BoolPropChangeActions.Add(new KeyValuePair<string, Action<bool>>(name, action));
}
public static void UnobserveBoolProp(string name, Action<bool> action)
{
BoolPropChangeActions.Remove(action);
foreach (var i in BoolPropChangeActions.ToArray())
if (i.Value == action)
BoolPropChangeActions.Remove(i);
int err = mpv_unobserve_property(MpvHandle, (ulong)action.GetHashCode());
if (err < 0)
@@ -241,9 +437,9 @@ namespace mpvnet
foreach (string i in args)
if (!i.StartsWith("--") && File.Exists(i))
mpv.Command("loadfile", i, "append");
mp.Command("loadfile", i, "append");
mpv.SetStringProp("playlist-pos", "0", false);
mp.SetStringProp("playlist-pos", "0", false);
foreach (string i in args)
{
@@ -253,27 +449,27 @@ namespace mpvnet
{
string left = i.Substring(2, i.IndexOf("=") - 2);
string right = i.Substring(left.Length + 3);
mpv.SetStringProp(left, right);
mp.SetStringProp(left, right);
}
else
mpv.SetStringProp(i.Substring(2), "yes");
mp.SetStringProp(i.Substring(2), "yes");
}
}
}
public static void LoadFiles(string[] files)
{
int count = mpv.GetIntProp("playlist-count");
int count = mp.GetIntProp("playlist-count");
foreach (string file in files)
mpv.Command("loadfile", file, "append");
mp.Command("loadfile", file, "append");
mpv.SetIntProp("playlist-pos", count);
mp.SetIntProp("playlist-pos", count);
for (int i = 0; i < count; i++)
mpv.Command("playlist-remove", "0");
mp.Command("playlist-remove", "0");
mpv.LoadFolder();
mp.LoadFolder();
}
private static bool WasFolderLoaded;
@@ -344,4 +540,14 @@ namespace mpvnet
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
}
public enum EndFileEventMode
{
Eof,
Stop,
Quit,
Error,
Redirect,
Unknown
}
}

View File

@@ -94,6 +94,29 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="IKVM.Reflection, Version=7.2.4630.5, Culture=neutral, PublicKeyToken=13235d27fcbfff58, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>IronPython\IKVM.Reflection.dll</HintPath>
</Reference>
<Reference Include="IronPython, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>IronPython\IronPython.dll</HintPath>
</Reference>
<Reference Include="IronPython.Modules, Version=2.7.9.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>IronPython\IronPython.Modules.dll</HintPath>
</Reference>
<Reference Include="IronPythonAddon">
<HintPath>IronPython\IronPythonAddon.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Dynamic, Version=1.2.2.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>IronPython\Microsoft.Dynamic.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Scripting, Version=1.2.2.0, Culture=neutral, PublicKeyToken=7f709c5b713576e1, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>IronPython\Microsoft.Scripting.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
@@ -109,6 +132,7 @@
<Compile Include="Menu.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="PyScript.cs" />
<Compile Include="StringExtensions.cs" />
<Compile Include="libmpv.cs" />
<Compile Include="MainForm.cs">
@@ -118,7 +142,7 @@
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Misc.cs" />
<Compile Include="mpv.cs" />
<Compile Include="mp.cs" />
<Compile Include="Command.cs" />
<Compile Include="Native.cs" />
<Compile Include="NativeHelp.cs" />
@@ -127,6 +151,7 @@
<Compile Include="UI.cs" />
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>

View File

@@ -3,10 +3,8 @@ $exePath = $scriptDir + "\mpv.net\bin\Debug\mpvnet.exe"
$version = [Diagnostics.FileVersionInfo]::GetVersionInfo($exePath).FileVersion
$desktopDir = [Environment]::GetFolderPath("Desktop")
$targetDir = $desktopDir + "\mpv.net-" + $version
if (Test-Path $targetDir) { rd $targetDir -recurse }
Copy-Item $scriptDir\mpv.net\bin\Debug $targetDir -recurse
$addonDir = $targetDir + "\Addons"
Remove-Item $addonDir -Recurse -Include *mpvnet.exe, *mpvnet.exe.config, *mpvnet.pdb
$7zPath = "C:\Program Files\7-Zip\7z.exe"
$args = "a -t7z -mx9 $targetDir.7z -r $targetDir\*"
Start-Process -FilePath $7zPath -ArgumentList $args

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB