Compare commits

..

80 Commits

Author SHA1 Message Date
Frank Skare
7d31237d5f fix release script 2020-05-11 17:14:23 +02:00
Frank Skare
3705e2abbf 5.4.8.0 2020-05-11 17:08:01 +02:00
Frank Skare
7d73529c83 fix beep sound when closed from taskbar 2020-05-11 12:16:40 +02:00
Frank Skare
2e59d49ad1 2 bugs fixed 2020-05-11 03:51:52 +02:00
Frank Skare
f61eef8961 5.4.7.4 Beta 2020-05-10 07:01:59 +02:00
Frank Skare
efb4c085d5 fix release script 2020-05-08 15:56:54 +02:00
Frank Skare
4a045d6a91 setting media-keys added 2020-05-08 15:32:47 +02:00
Frank Skare
da1a78cfa3 5.4.7.2 Beta 2020-05-08 09:22:19 +02:00
Frank Skare
5b3697fbde built-in WM_APPCOMMAND support 2020-05-08 08:26:44 +02:00
Frank Skare
726d759589 misc 2020-05-07 13:32:23 +02:00
Frank Skare
ed614332f7 misc 2020-05-07 13:21:59 +02:00
Frank Skare
9d46064dca 5.4.7.1 Beta 2020-05-07 11:47:09 +02:00
Frank Skare
407b49b3ad 5.4.7.0 2020-05-06 18:51:38 +02:00
Frank Skare
0e92d4ec0c 5.4.7.0 2020-05-06 18:43:20 +02:00
Frank Skare
4f96835e19 support avisynth portable 2020-05-06 11:11:09 +02:00
Frank Skare
56d954d94e manual 2020-05-04 16:30:39 +02:00
Frank Skare
a8aeb1e3e9 manual 2020-05-04 16:28:59 +02:00
Frank Skare
415fd266a2 log error fix 2020-05-04 15:15:40 +02:00
Frank Skare
51f9e17380 5.4.6.0 2020-05-04 05:50:29 +02:00
Frank Skare
28045ad33e 5.4.5.1 Beta 2020-05-03 11:10:56 +02:00
Frank Skare
e1bd44cd00 5.4.5.0 2020-05-01 22:02:03 +02:00
Frank Skare
27d6026213 misc 2020-04-27 15:41:16 +02:00
Frank Skare
c67d5f10f8 manual 2020-04-25 23:59:05 +02:00
Frank Skare
5f73be07e8 URL fix 2020-04-25 15:59:51 +02:00
Frank Skare
0cad09d2bb 5.4.4.6 Beta 2020-04-25 15:56:34 +02:00
Frank Skare
54b2612bb8 5.4.4.6 Beta 2020-04-25 15:36:56 +02:00
Frank Skare
82b4d19282 5.4.4.5 Beta 2020-04-23 22:55:45 +02:00
Frank Skare
23a587c819 manual 2020-04-23 12:55:02 +02:00
Frank Skare
d69eecd494 readme 2020-04-23 12:27:43 +02:00
Frank Skare
0d4cc59f9a manual 2020-04-23 12:15:12 +02:00
Frank Skare
a394536a8c readme and manual 2020-04-23 11:35:44 +02:00
Frank Skare
ebcd20ceb5 manual 2020-04-22 19:41:50 +02:00
Frank Skare
964c2f4dd2 manual 2020-04-22 19:39:17 +02:00
Frank Skare
e985047a54 manual 2020-04-22 19:33:58 +02:00
Frank Skare
bafc09a948 manual 2020-04-22 19:24:34 +02:00
Frank Skare
14a172b78b scripting docs 2020-04-22 19:17:12 +02:00
Frank Skare
2eb324bf41 manual 2020-04-22 08:25:31 +02:00
Frank Skare
afe9ad5128 misc 2020-04-22 08:15:28 +02:00
Frank Skare
cc657d4ec2 misc 2020-04-22 08:14:55 +02:00
Frank Skare
1270dd474e 5.4.4.4 Beta 2020-04-22 08:04:26 +02:00
stax76
e9ea70838d Merge pull request #152 from Restia666Ashdoll/patch-1
Update Manual.md - Replace External Application Button with Open With
2020-04-22 05:54:49 +02:00
Frank Skare
e7fde60150 full implementation for window-minimized and window-maximized 2020-04-22 05:52:11 +02:00
Restia666Ashdoll
2028113d58 Update Manual.md 2020-04-21 18:41:57 +05:30
Frank Skare
4d55423fc7 scripts moved from wiki to folder 2020-04-21 04:31:10 +02:00
Frank Skare
d82874409d rename scripts folder 2020-04-21 04:02:25 +02:00
Frank Skare
930ba2b0e7 crash fixed when PowerShell 5.1 is not available 2020-04-21 03:39:31 +02:00
Frank Skare
4743f365a1 readme 2020-04-19 20:43:09 +02:00
Frank Skare
194ff5d62b 5.4.4.3 Beta 2020-04-19 20:40:26 +02:00
Frank Skare
5a726a01e0 window maximized issue 2020-04-19 20:14:46 +02:00
Frank Skare
fee13353ec seek-show-position script removed 2020-04-19 18:51:54 +02:00
Frank Skare
cd3543fff4 misc 2020-04-19 03:16:00 +02:00
Frank Skare
c54f240346 docs 2020-04-15 20:28:30 +02:00
Frank Skare
7f55ad908a start-size supports always 2020-04-15 14:25:45 +02:00
Frank Skare
d586a82e37 changelog 2020-04-15 04:03:06 +02:00
Frank Skare
722e06a02b window-maximized support added 2020-04-13 03:42:36 +02:00
Frank Skare
2f76d2b775 Merge branch 'master' of https://github.com/stax76/mpv.net 2020-04-12 21:54:44 +02:00
Frank Skare
7519977124 misc 2020-04-12 21:54:41 +02:00
stax76
2e65b9ceb8 Update issue templates 2020-04-12 21:53:26 +02:00
stax76
04bffca206 Update issue templates 2020-04-12 21:52:11 +02:00
Frank Skare
ff5a164c69 cleanup MediaInfo class 2020-03-22 00:55:33 +01:00
Frank Skare
26b391a8d9 5.4.4.2 2020-03-21 08:28:37 +01:00
Frank Skare
32eed33c82 misc 2020-03-21 06:43:54 +01:00
Frank Skare
639893f2e7 misc 2020-03-20 17:47:46 +01:00
Frank Skare
42b0cc1a64 5.4.4.2 2020-03-20 01:22:26 +01:00
Frank Skare
6f7fa6c9d6 readme fix 2020-03-17 19:41:33 +01:00
Frank Skare
e6fb775dcd 5.4.4.1 2020-03-17 19:36:07 +01:00
Frank Skare
ece15ca9bb fix issue enlarging or shrinking the window size via keyboard 2020-03-17 00:18:09 +01:00
Frank Skare
6b3c2788df new: blue color in dark theme is now less intense 2020-03-16 03:50:18 +01:00
Frank Skare
067bdd20d7 minor changes 2020-03-16 03:31:26 +01:00
Frank Skare
97ce08c4c7 script-message mpv.net playlist-first 2020-03-07 14:12:02 +01:00
Frank Skare
fbb9cbdc33 docs 2020-03-03 23:43:49 +01:00
Frank Skare
815adca198 Merge branch 'master' of https://github.com/stax76/mpv.net 2020-03-03 23:03:03 +01:00
Frank Skare
ce3d5792ad readme update 2020-03-03 23:02:40 +01:00
stax76
74fc0b3ed7 Merge pull request #126 from Restia666Ashdoll/patch-1
Add AppGet to README
2019-12-05 13:51:57 +01:00
Restia666Ashdoll
604a1e8499 Update README.md 2019-12-05 16:00:42 +05:30
stax76
56ade0a0d1 Merge pull request #118 from Restia666Ashdoll/patch-1
External Application Button is also on Firefox
2019-12-02 01:25:01 +01:00
Frank Skare
6294a96aea fix update routine 2019-12-02 01:22:12 +01:00
Restia666Ashdoll
9a95953a04 Update Manual.md 2019-11-18 21:05:57 +05:30
Frank Skare
8d9c0c9092 readme update 2019-11-17 16:02:16 +01:00
Frank Skare
e399ca672f update readme 2019-11-17 15:52:56 +01:00
80 changed files with 4164 additions and 2747 deletions

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: stax76
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: stax76
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,4 +1,164 @@
5.4.8.1 Beta (not yet released)
============
5.4.8.0
=======
- fix learn window of input editor (much work)
- fix black one pixel bar on right side
- fix beep sound when closed from taskbar
5.4.7.4 Beta (not yet released)
============
- the media key issue should now be fixed!
- libmpv and youtube-dl update
5.4.7.3 Beta
============
- new setting media-keys added, can be found in the config editor in the input tab.
5.4.7.2 Beta
============
- another attempt to fix a app command issue. WM_APPCOMMAND (mpv calls this media keys)
based input is no longer directly passed to mpv but rather handled in mpv.net directly
translating the native commands to mpv keys and sent via keypress input command to mpv
- another attempt to fix a crash caused by powershell 5.1 not being installed
5.4.7.1 Beta
============
- log error fix, mpv.net does now ignore all log messages because libmpv already
prints the log messages to the the terminal
- the release script now also outputs x86 beta versions so x86 users can test betas
5.4.7.0
=======
- log error fix
- workaround to support AviSynth portable (ffmpeg blocks loading AviSynth from path env var)
- attempt to fix not reproducible volume input issue
- attempt to fix exception caused by powershell being not available on Win 7
5.4.6.0
=======
- youtube-dl update
- auto update routine fix
5.4.5.1 Beta
============
- extensions no longer need to end with *Extension.dll but rather
the file name and the directory name must be identical
- text encoding exception fix
- the PowerShell script host is more feature complete, easier to use
and more efficient, there were however many PowerShell and C# breaking
changes requrired to make the core more robust and efficient
- Python 2 script host removed, Python 3 support is planned for summer
5.4.5.0
=======
stable release, no changes since the last beta
5.4.4.6 Beta
============
- using start-size=video the window size was not remembered
after fullscreen mode was left.
- using start-size=video the window can now use the entire
working area using autofit-larger=100
5.4.4.5 Beta
============
- overhaul of the [webpage](README.md) and the [manual](Manual.md).
- change that possibly could fix a multimedia keyboard volume issue.
5.4.4.4 Beta
============
- with `border=no` the OSC top bar window buttons min, max and close are fully supported.
- anamorphic videos are shown without black bars, the window is resized according to the ascpect ratio.
- PowerShell 5.1 was made optional.
- full implementation for `window-minimized` and `window-maximized`,
scripts that depend on this like pause-when-minimize.lua are now fully supported.
- fix cycling from maximized to fullscreen and back.
5.4.4.3 Beta
============
- update MediaInfo 20.03
- update libmpv 2020-04-12, it supports vpy playback, read manual or ask in forum
- mpv property `window-maximized` support added, cycling it from input.conf
is not recommended, use native Windows shortcuts Win+Up, Win+Down
- the mpv.net `start-size` option supports `always` to always remember the window height
- if the window was maximized before fullscreen was entered, it's now set to
maximized after fullscreen was left
- with `border=no` the OSC did not auto hide after the mouse curser left the window
- the script that modified the seek OSD was removed, it still can be found in the mpv wiki
- certain videos were showed with black bars
### 5.4.4.2
- update: libmpv shinchiro 0.32.0-258-g281f5c63c1
- update: youtube-dl
- new: d3d11va-zero-copy setting added to conf editor
- new: hdr-compute-peak setting added to conf editor
- new: flag cli switches support now `--no-flag` in addition to `--flag=no`
https://mpv.io/manual/master/#usage
- new: cli switches can also start with single `-` instead of double `--`
https://mpv.io/manual/master/#legacy-option-syntax
- new: PowerShell script host was completely rewritten, events can be assigned
by using `Register-ObjectEvent`, the scripting wiki page was updated
https://github.com/stax76/mpv.net/wiki/Scripting#powershell
- new: Context Menu > View > Show Profiles
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L147
- new: Context Menu > View > Show Properties
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L148
- new: Context Menu > View > Show Commands
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L149
- new: config editor tab is now remembered
- new: osd-duration setting added to config editor and default mpv.conf
- new: external console replaced with internal console, in case mpv.conf is missing it's
generated with correct Hight DPI font size scale settings.
`script-opts=console-scale=<dpiscale>`
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L150
https://mpv.io/manual/master/#console
- new: blue color in dark theme is now less intense
https://github.com/stax76/mpv.net/blob/master/Manual.md#color-theme
- new: menu item 'View > Show Progress' (p key) to show progress bar
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L146
- new: `script-message mpv.net playlist-first`, unlike mpv does not
restart if the first file is already active
https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L44
- new: if mpv.net is started from the terminal and an error happens then the error
is printed to the terminal instead of shown with a message box
- fix: update routine did only work when mpv.net was located in 'Program Files'
- fix: fatal errors were ignored and only seen in the terminal, now a message box is shown
- fix: when start-size=video was used then enlarging or shrinking the window size was broken
### 5.4.4.0
- new: forecolors in the dark theme are slightly darker now
@@ -9,7 +169,7 @@
- new: update check, it must be enabled first in the conf editor under General
- new: update feature, requires PowerShell 5 and curl,
an up to date Windows 10 system has both included.
Main menu (input.conf) must be reset or updated manually ([defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt))
Main menu (input.conf) must be reset or updated manually ([defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt))
- update: libmpv shinchiro 2019-11-10
@@ -48,7 +208,7 @@
context menu item in explorer with multi selection support use my
[Open with++](https://github.com/stax76/OpenWithPlusPlus#add-to-mpvnet-playlist) shell extension, as far as I know multi selection
can not be done using the Registry but only via shell extension
- window-size mpv property support added ([default bindings](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt#L137))
- window-size mpv property support added ([default bindings](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L137))
- the config editor keeps profiles and comments in mpv.conf intact!
- the options in the config editor are better organized
@@ -122,7 +282,6 @@
### 5.1
- 'Tools > Execute mpv command' was replaced with [mpv-repl](https://github.com/rossy/mpv-repl)
- many [wiki pages](https://github.com/stax76/mpv.net/wiki) were improved
- the logo/icon had a very small cosmetic change
- the help in the context menu was improved,
@@ -246,7 +405,7 @@
pressed, the files are opened as selected, the order is random though
because Explorer starts multiple mpv.net processes concurrently
- libmpv was updated to shinchiro 2019-06-30
- the [mpv.conf defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt) were changed to show a larger OSC
- the [mpv.conf defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpv.conf.txt) were changed to show a larger OSC
- in case a file is opened that has a aspect ratio smaller then 1.2 then
the window size will use a aspect ratio of 1.8
- new JavaScript script osc-visibility.js included in the distribution
@@ -285,7 +444,7 @@
- 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))
key binding (b) was added as well ([Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L135))
### 4.3.1
@@ -300,7 +459,7 @@
- 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)
it appends files to the playlist [(Default binding)](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L33)
- a setting was added to force using a single mpv.net process instance
### 4.1
@@ -342,7 +501,7 @@
- there was a bug that caused underscores beeing removed from input like MBTN_LEFT_DBL
- the search clear button in the input editor had a render issue in dark mode
- new search feature added to search and play media files, requires
[Everything](https://www.voidtools.com) to be installed. [Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt#L29)
[Everything](https://www.voidtools.com) to be installed. [Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L29)
### 3.5
@@ -355,6 +514,6 @@
### 3.4
- new feature added to manage file associations from within the app. It can be found in the menu at: Tools > Manage... [Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt#L149)
- new feature added to manage file associations from within the app. It can be found in the menu at: Tools > Manage... [Default Binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L149)
- new zip download option added
- new x86 download option added

View File

@@ -1,7 +1,7 @@
MIT License
Copyright (c) 2002-2017 Frank Skare (stax76)
Copyright (C) 2017-2020 Frank Skare (stax76)
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and ssociated documentation

596
Manual.md

File diff suppressed because it is too large Load Diff

319
README.md
View File

@@ -1,283 +1,148 @@
![](https://raw.githubusercontent.com/stax76/mpv.net/master/img/mpvnet.png)
![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed/stax76/mpv.net) ![GitHub closed issues](https://img.shields.io/github/issues-closed/stax76/mpv.net) ![GitHub All Releases](https://img.shields.io/github/downloads/stax76/mpv.net/total) ![GitHub tag (latest by date)](https://img.shields.io/github/tag-date/stax76/mpv.net) ![GitHub stars](https://img.shields.io/github/stars/stax76/mpv.net) [![PayPal donate button](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.me/stax76)
![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed/stax76/mpv.net) ![GitHub closed issues](https://img.shields.io/github/issues-closed/stax76/mpv.net) ![GitHub All Releases](https://img.shields.io/github/downloads/stax76/mpv.net/total) ![GitHub tag (latest by date)](https://img.shields.io/github/tag-date/stax76/mpv.net) ![GitHub stars](https://img.shields.io/github/stars/stax76/mpv.net)
# 🎞 mpv.net
🎞 mpv.net
==========
mpv.net is a modern media player for Windows based on the popular [mpv](https://mpv.io) player.
mpv.net is a modern media player for Windows that works just like [mpv](https://mpv.io).
#### Graphical User Interface
Modern GUI that supports customizable color themes.
Modern GUI with customizable color themes.
#### Command Line Interface
mpv.net has the [CLI of mpv](https://mpv.io/manual/master/#options).
mpv.net supports mpvs property based command line switches.
#### High quality video output
libmpv has an OpenGL based video output that is capable of many features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more.
Video output that is capable of many features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more.
#### On Screen Controller
The OSC of libmpv offers play controls with a modern flat design. ([Screenshot](#main-window-screenshot))
Play controls with a modern flat design.
#### GPU video decoding
libmpv leverages the FFmpeg hwaccel APIs to support DXVA2 video decoding acceleration.
Leverages the FFmpeg hwaccel APIs to support DXVA2 video decoding acceleration.
#### Active development
mpv.net is under active development. Want a feature? Post a [patch](https://github.com/stax76/mpv.net/pulls) or [request it](https://github.com/stax76/mpv.net/issues)!
mpv.net is under active development.
#### 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 is like vlc not based on DirectShow or Media Foundation.
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.
Table of contents
-----------------
- [Features](#features)
- [Screenshots](#screenshots)
- [Installation](#installation)
- [Manual](#manual)
- [Context Menu](#context-menu)
- [Settings](#settings)
- [Scripting](#scripting)
- [Extensions](#extensions)
- [Architecture](#architecture)
- [Support](#support)
- [Links](#links)
- [Changelog](#changelog)
### Features
- Very high degree of mpv compatibility, almost all mpv features are available
- Great usability due to everything in the application being searchable
- Open source built with modern tools
- 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), [Defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt))
- Searchable input (key/mouse) binding editor ([Screenshot](#input-editor-screenshot), [Defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt))
- Configuration files that are easy to read and edit ([Manual](https://mpv.io/manual/master/#configuration-files))
- Searchable command palette to quickly find commands and keys ([Screenshot](#command-palette-screenshot))
- Modern graphical user interface with customizable color themes ([Screenshot](#config-editor-screenshot))
- [Extension API for .NET languages (C#, VB.NET and F#)](https://github.com/stax76/mpv.net/wiki/Extensions)
- Scripting API for Python, C#, Lua, JavaScript and PowerShell ([Wiki](https://github.com/stax76/mpv.net/wiki/Scripting))
- Language agnostic JSON IPC to control the player with a external programs
- On Screen Controler (OSC, play control buttons) ([Screenshot](#main-window-screenshot))
- [Command Line Interface](https://mpv.io/manual/master/#options)
- If started from a PowerShell terminal mpv.net will attach to the terminal and print status and debug output ([Screenshot](#terminal-and-repl-screenshot))
- [OSD REPL](https://github.com/rossy/mpv-repl)
- 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
- Search feature powered by [Everything](https://www.voidtools.com) to find and play media ([Screenshot](#media-search-screenshot))
- Extension to start mpv.net from Google Chrome ([Manual](Manual.md#chrome-extension))
- Extremely fast seek performance
- Very fast startup performance, video is usally ready to play in less then a second
- Usable as video player, audio player and image viewer with a wide range of supported formats
- All decoders are built-in, no external codecs have to be installed
- Setup and portable download options, setup is recommended but not required
- Build-in media streaming via youtube-dl
- x64 and x86 Support (64-bit and 32-bit)
- File associations can be created by the setup and from the player
- External audio and subtitle files can either be loaded manually or automatically
- Screenshot feature with many options
- File history feature to log time and filename
- A-B loop feature
- Watch later feature to save the position
- Files can be enqueued from File Explorer ([Manual](Manual.md#open-with))
- Update check and update routine ([Manual](Manual.md#help--check-for-updates))
- [Download](#download)
- [Manual](#manual)
- [Screenshots](#screenshots)
### Screenshots
#### Main Window Screenshot
Features
--------
- High degree of mpv compatibility, almost all mpv features are available
- Customizable context menu defined in the same file as the key bindings
- Config dialog
- Shorcut key editor
- Many features like the config editor and shortcut key editor are fully searchable
- Configuration files that are easy to read and edit
- Command palette to quickly find commands and keys
- Modern graphical user interface with customizable color themes
- Extension API for .NET languages (C#, VB.NET and F#)
- Scripting API for C#, Lua, JavaScript and PowerShell
- JSON IPC to control the player with a external programs
- On Screen Controler (OSC, play control buttons) with modern flat design
- Command Line Interface
- Started from a terminal status and debug output is printed on the terminal
- DXVA2 video decoding acceleration
- 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
- File search feature powered by the popular search and index tool Everything
- Browser extensions to start mpv.net from the browser
- Fast seek performance
- Fast startup performance
- Usable as video player, audio player and image viewer with a wide range of supported formats
- Built-in decoders, no external codecs have to be installed
- Setup as x64, x86, installer, portable, Chocolatey and Scoop
- Build-in media streaming via youtube-dl
- File associations can be created by the setup and from the player
- External audio and subtitle files can be loaded manually or automatically
- Screenshot feature
- File history feature to log time and filename
- Watch later feature to save the position
- Files can be enqueued from File Explorer
- Update check and update routine
## [Support](Manual.md#support)
[Support section of the manual.](Manual.md#support)
## [Download](Manual.md#download)
[Download section of the manual.](Manual.md#download)
## [Manual](Manual.md)
[The mpv.net documentation.](Manual.md)
Screenshots
-----------
#### Main Window
![Main Window](https://raw.githubusercontent.com/stax76/mpv.net/master/img/Main.png)
#### Context Menu Screenshot
Context menu defined in the input.conf file with dark mode support.
#### Context Menu
Context menu using dark mode.
![Context Menu](https://raw.githubusercontent.com/stax76/mpv.net/master/img/Menu.png)
#### Config Editor Screenshot
A searchable config editor as alternative to edit the mpv.conf file manually.
#### Config Editor
Searchable config editor as alternative to edit the conf file manually.
![](https://raw.githubusercontent.com/stax76/mpv.net/master/img/ConfEditor.png)
#### Terminal and REPL Screenshot
mpv.net attached to a PowerShell terminal showing the [OSD REPL](https://github.com/rossy/mpv-repl).
#### Terminal
OSD console and status printed on the terminal.
![](https://raw.githubusercontent.com/stax76/mpv.net/master/img/Terminal.png)
#### Input Editor Screenshot
A searchable key and mouse binding editor.
#### Input Editor
Searchable key and mouse binding editor.
![Input Editor](https://raw.githubusercontent.com/stax76/mpv.net/master/img/InputEditor.png)
#### Command Palette Screenshot
Forgot where a command in the menu is located or what shortcut key it has?
Just press F1 and find it easily in the searchable command palette.
#### Command Palette
Command Palette to find commands and shortcut keys easily.
![Command Palette](https://raw.githubusercontent.com/stax76/mpv.net/master/img/CommandPalette.png)
#### Media Search Screenshot
Media search feature powered by [Everything](https://www.voidtools.com) to find and play media.
![Media Search](https://raw.githubusercontent.com/stax76/mpv.net/master/img/MediaSearch.png)
### Installation
mpv.net requires minimum .NET Framework 4.8 and Windows 7. For optimal results a modern graphics card is recommended.
Stable releases are compiled from the source and can be downloaded from the releases tab:
<https://github.com/stax76/mpv.net/releases>
Scoop can be used to install and update it:
```
scoop bucket add extras
scoop install mpv.net
```
Alternatively, Chocolatey can also be used:
`choco install mpvnet.install`
### Manual
[Manual](Manual.md)
### Context Menu
The context menu can be customized via input.conf file located in the config directory:
```Text
C:\Users\%username%\AppData\Roaming\mpv\input.conf
```
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>
input.conf defines mpv's key and mouse bindings and mpv.net uses comments to define the context menu.
### Settings
When mpv.net finds no config folder on startup it will ask for a location.
If a folder named portable_config next to the mpvnet.exe exists,
all config will be loaded from this folder only.
```Text
<startup>\portable_config\
```
mpv specific settings are stored in the file mpv.conf, if no mpv.conf file exists
mpv.net generates it with the following defaults:
<https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt>
mpv.net specific settings are stored in the file mpvnet.conf
The input (key/mouse) bindings and the context menu definitions are stored in the
input.conf file, 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>
mpv.net supports almost all mpv settings and features,
[limitations are listed in the wiki](https://github.com/stax76/mpv.net/wiki/Limitations).
The config folder can be opened from the context menu.
### Scripting
[Scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting)
### Extensions
[Extensions wiki page](https://github.com/stax76/mpv.net/wiki/Extensions)
### Architecture
Coding mpv.net was great fun because libmpv is such a awesome
library with a very clever design, I'm having a great experience
with libmpv.
The player does not contain any feature that was more work than 1-2 days or
was difficult to build, the hard parts are totally covered by libmpv.
mpv.net is written in C# 7 and runs on .NET 4.7, I've not yet decided
if I will port it to C# 8 and .NET 5 once available.
The Extension implementation is based on the [Managed Extensibility Framework](https://docs.microsoft.com/en-us/dotnet/framework/mef/).
There are no specific extension or scripting interfaces but instead everyting
is accessible for .NET compatible languages (C#, VB.NET, F#, Python, PowerShell),
this decision was made to keep the code simple and lightweight.
Python scripting is implemented with IronPython which uses Python 2.7.
The main window is WinForms based and uses less than 750 lines of code,
all other windows are WPF based and use even less code.
The config editor adds it's controls dynamically and uses [TOML](https://en.wikipedia.org/wiki/TOML) to define it's
content, there are only two simple types, StringSetting and OptionSetting.
mpv.net was started 2017 and consists of about 6000 lines of code and markup.
IDE, Editor: Visual Studio, Visual Studio Code.
Due to mpv.net being my first WPF app and mpv.net never meant to be a large
application best practices and design pattern are not always applied.
Third party components:
- [libmpv, the heard and soul of mpv.net](https://mpv.io/)
- [MediaInfo, no media related project could do without](https://mediaarea.net/en/MediaInfo)
- [Tommy, a single file TOML parser](https://github.com/dezhidki/Tommy)
- [IronPython, bringing Python to dotnet](https://ironpython.net/)
- [CS-Script, scripting with C#](http://www.csscript.net/)
- [Everything, a blazing fast file search service](https://www.voidtools.com)
### Support
[Support thread in Doom9 forum](https://forum.doom9.org/showthread.php?t=174841)
[Support thread in VideoHelp forum](https://forum.videohelp.com/threads/392514-mpv-net-a-extendable-media-player-for-windows)
[Issue tracker](https://github.com/stax76/mpv.net/issues), feel free to use for anything mpv.net related
[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/).
If you want to support the development of mpv.net or express your appreciation you can do so with a donation:
<https://www.paypal.me/stax76>
### Links
- mpv.net wiki: <https://github.com/stax76/mpv.net/wiki>
- mpv.net default key bindings: <https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt>
- mpv.net download: <https://github.com/stax76/mpv.net/releases>
- mpv.net bugs and requests: <https://github.com/stax76/mpv.net/issues>
- mpv website: <https://mpv.io/>
- mpv manual: <https://mpv.io/manual/master/>
- mpv wiki: <https://github.com/mpv-player/mpv/wiki>
- mpv apps: <https://github.com/mpv-player/mpv/wiki/Applications-using-mpv>
- mpv user scripts: <https://github.com/mpv-player/mpv/wiki/User-Scripts>
- mpv default key bindings: <https://github.com/mpv-player/mpv/blob/master/etc/input.conf>
- mpv download: <https://mpv.io/installation/>
- mpv bugs and requests: <https://mpv.io/bug-reports/>
### Changelog
[Changelog](Changelog.md)

View File

@@ -2,65 +2,96 @@
$ErrorActionPreference = 'Stop'
$desktopDir = [Environment]::GetFolderPath('Desktop')
$exePath = (Get-Location).Path + '\mpv.net\bin\x64\mpvnet.exe'
$version = [Reflection.Assembly]::LoadFile($exePath).GetName().Version
$msbuild = 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe'
$iscc = 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe'
$exePath = $PSScriptRoot + '\mpv.net\bin\x64\mpvnet.exe'
$versionInfo = [Diagnostics.FileVersionInfo]::GetVersionInfo($exePath)
$vsDir = 'C:\Program Files (x86)\Microsoft Visual Studio\2019'
$msBuild = $vsDir + '\Community\MSBuild\Current\Bin\MSBuild.exe'
$inno = 'C:\Program Files (x86)\Inno Setup 6\ISCC.exe'
$7z = 'C:\Program Files\7-Zip\7z.exe'
if ($version.Revision -ne 0)
$cloudDirectories = 'C:\Users\frank\OneDrive\Public\mpv.net\',
'C:\Users\frank\Dropbox\Public\mpv.net\'
function UploadBeta($sourceFile)
{
& $msbuild mpv.net.sln /p:Configuration=Debug /p:Platform=x64
foreach ($cloudDirectory in $cloudDirectories)
{
if (-not (Test-Path $cloudDirectory))
{
throw $cloudDirectory
}
$targetFile = $cloudDirectory + (Split-Path $sourceFile -Leaf)
if (Test-Path $targetFile)
{
throw $targetFile
}
Copy-Item $sourceFile $targetFile
}
}
if ($versionInfo.FilePrivatePart -eq 0)
{
& $msBuild mpv.net.sln -t:Rebuild -p:Configuration=Debug -p:Platform=x64
if ($LastExitCode) { throw $LastExitCode }
$targetDir = "$desktopDir\mpv.net-portable-x64-$version-beta"
& $msBuild mpv.net.sln -t:Rebuild -p:Configuration=Debug -p:Platform=x86
if ($LastExitCode) { throw $LastExitCode }
& $inno /Darch=x64 setup.iss
if ($LastExitCode) { throw $LastExitCode }
& $inno /Darch=x86 setup.iss
if ($LastExitCode) { throw $LastExitCode }
$targetDir = $desktopDir + "\mpv.net-portable-x64-$($versionInfo.FileVersion)"
Copy-Item .\mpv.net\bin\x64 $targetDir -Recurse -Exclude System.Management.Automation.xml
& $7z a -t7z -mx9 "$targetDir.7z" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
& $7z a -tzip -mx9 "$targetDir.zip" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
$targetDirectories = 'C:\Users\frank\OneDrive\StaxRip\TestBuilds\',
'C:\Users\frank\Dropbox\public\StaxRip\Builds\'
$targetDir = $desktopDir + "\mpv.net-portable-x86-$($versionInfo.FileVersion)"
Copy-Item .\mpv.net\bin\x86 $targetDir -Recurse -Exclude System.Management.Automation.xml
& $7z a -t7z -mx9 "$targetDir.7z" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
& $7z a -tzip -mx9 "$targetDir.zip" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
foreach ($dir in $targetDirectories)
{
if (Test-Path $dir)
{
Copy-Item ($targetDir + '.7z') ($dir + (Split-Path $targetDir -Leaf) + '.7z')
Invoke-Item $dir
}
}
Set-Clipboard ($versionInfo.FileVersion + "`n`nChangelog:`n`n" +
'https://github.com/stax76/mpv.net/blob/master/Changelog.md' + "`n`nDownload:`n`n" +
'https://github.com/stax76/mpv.net/blob/master/Manual.md#stable')
}
else
{
& $msbuild mpv.net.sln /p:Configuration=Debug /p:Platform=x64
& $msBuild mpv.net.sln -t:Rebuild -p:Configuration=Debug -p:Platform=x64
if ($LastExitCode) { throw $LastExitCode }
& $msbuild mpv.net.sln /p:Configuration=Debug /p:Platform=x86
& $msBuild mpv.net.sln -t:Rebuild -p:Configuration=Debug -p:Platform=x86
if ($LastExitCode) { throw $LastExitCode }
& $iscc /Darch=x64 setup.iss
if ($LastExitCode) { throw $LastExitCode }
& $iscc /Darch=x86 setup.iss
if ($LastExitCode) { throw $LastExitCode }
$targetDir = $desktopDir + "\mpv.net-portable-x64-$version"
$targetDir = "$desktopDir\mpv.net-portable-x64-$($versionInfo.FileVersion)-beta"
Copy-Item .\mpv.net\bin\x64 $targetDir -Recurse -Exclude System.Management.Automation.xml
& $7z a -t7z -mx9 "$targetDir.7z" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
UploadBeta "$targetDir.7z"
& $7z a -tzip -mx9 "$targetDir.zip" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
$targetDir = $desktopDir + "\mpv.net-portable-x86-$version"
$targetDir = $desktopDir + "\mpv.net-portable-x86-$($versionInfo.FileVersion)-beta"
Copy-Item .\mpv.net\bin\x86 $targetDir -Recurse -Exclude System.Management.Automation.xml
& $7z a -t7z -mx9 "$targetDir.7z" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
UploadBeta "$targetDir.7z"
& $7z a -tzip -mx9 "$targetDir.zip" -r "$targetDir\*"
if ($LastExitCode) { throw $LastExitCode }
foreach ($cloudDirectory in $cloudDirectories)
{
Invoke-Item $cloudDirectory
}
Set-Clipboard ($versionInfo.FileVersion + " Beta`n`nChangelog:`n`n" +
'https://github.com/stax76/mpv.net/blob/master/Changelog.md' + "`n`nDownload:`n`n" +
'https://github.com/stax76/mpv.net/blob/master/Manual.md#beta')
}
Write-Host 'successfully finished' -ForegroundColor Green

View File

@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RatingExtension")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyDescription("RatingExtension")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("Frank Skare (stax76)")]
[assembly: AssemblyProduct("RatingExtension")]
[assembly: AssemblyCopyright("Copyright © 2017-2019 Frank Skare (stax76)")]
[assembly: AssemblyCopyright("Copyright (C) 2017-2020 Frank Skare (stax76)")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View File

@@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.IO;
using mpvnet;
using static mpvnet.Core;
namespace RatingExtension // the assembly name must end with 'Extension'
{
@@ -19,8 +20,8 @@ namespace RatingExtension // the assembly name must end with 'Extension'
public RatingExtension() // plugin initialization
{
mp.ClientMessage += ClientMessage; //handles keys defined in input.conf
mp.Shutdown += Shutdown; // handles MPV_EVENT_SHUTDOWN
core.ClientMessage += ClientMessage; //handles keys defined in input.conf
core.Shutdown += Shutdown; // handles MPV_EVENT_SHUTDOWN
}
// handles MPV_EVENT_SHUTDOWN
@@ -53,10 +54,10 @@ namespace RatingExtension // the assembly name must end with 'Extension'
if (int.TryParse(args[1], out int rating))
{
string path = mp.get_property_string("path");
string path = core.get_property_string("path");
if (!File.Exists(path)) return;
Dic[path] = rating;
mp.commandv("show-text", $"Rating: {rating}");
core.commandv("show-text", $"Rating: {rating}");
}
else if (args[1] == "about")
Msg.Show("Rating Extension", "This extension writes a rating to the filename of rated videos when mpv.net shuts down.\n\nThe input.conf defaults contain key bindings for this extension to set ratings.");

View File

@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RatingExtension")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyDescription("RatingExtension")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyCompany("Frank Skare (stax76)")]
[assembly: AssemblyProduct("RatingExtension")]
[assembly: AssemblyCopyright("Copyright © 2017-2019 Frank Skare (stax76)")]
[assembly: AssemblyCopyright("Copyright (C) 2017-2020 Frank Skare (stax76)")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View File

@@ -1,3 +1,4 @@
// This extension implements the C# scripting feature of mpv.net which
// is based on CS-Script (https://www.cs-script.net).
@@ -12,8 +13,10 @@ using System.IO;
using mpvnet;
using CSScriptLibrary;
using static mpvnet.Core;
namespace ScriptingExtension // the file name of extensions must end with 'Extension'
// the file name of extensions must end with 'Extension'
namespace ScriptingExtension
{
[Export(typeof(IExtension))]
public class ScriptingExtension : IExtension
@@ -23,24 +26,29 @@ namespace ScriptingExtension // the file name of extensions must end with 'Exten
public ScriptingExtension()
{
//Script = new Script();
List<string> scriptFiles = new List<string>();
List<string> files = new List<string>();
if (Directory.Exists(mp.ConfigFolder + "scripts"))
scriptFiles.AddRange(Directory.GetFiles(mp.ConfigFolder + "scripts", "*.cs"));
if (Directory.Exists(core.ConfigFolder + "scripts-cs"))
files.AddRange(Directory.GetFiles(core.ConfigFolder + "scripts-cs", "*.cs"));
if (Directory.Exists(Folder.Startup + "scripts"))
foreach (string path in Directory.GetFiles(Folder.Startup + "scripts", "*.cs"))
scriptFiles.AddRange(Directory.GetFiles(Folder.Startup + "scripts", "*.cs"));
files.AddRange(Directory.GetFiles(Folder.Startup + "scripts", "*.cs"));
if (files.Count == 0)
return;
if (scriptFiles.Count == 0) return;
CSScriptLibrary.CSScript.EvaluatorConfig.Engine = EvaluatorEngine.CodeDom;
foreach (var i in scriptFiles)
foreach (string file in files)
{
try {
CSScriptLibrary.CSScript.Evaluator.LoadCode(File.ReadAllText(i));
} catch (Exception e) {
Msg.ShowException(e);
try
{
CSScriptLibrary.CSScript.Evaluator.LoadCode(File.ReadAllText(file));
}
catch (Exception e)
{
App.ShowException(e);
}
}
}

View File

@@ -1,15 +1,17 @@
using System.IO;

using System.IO;
using mpvnet;
using static mpvnet.Core;
class Script
{
public Script()
{
mp.Shutdown += Shutdown;
core.Shutdown += Shutdown;
}
private void Shutdown()
void Shutdown()
{
foreach (string file in Directory.GetFiles(@"C:\Users\frank\Desktop\aaa"))
File.Delete(file);

View File

@@ -1,4 +1,5 @@
using System;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -113,7 +114,7 @@ namespace DynamicGUI
public OptionSetting OptionSetting { get; set; }
private string _Text;
string _Text;
public string Text
{
@@ -144,7 +145,7 @@ namespace DynamicGUI
public class HyperlinkEx : Hyperlink
{
private void HyperLinkEx_RequestNavigate(object sender, RequestNavigateEventArgs e)
void HyperLinkEx_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(e.Uri.AbsoluteUri);
}

View File

@@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DynamicGUI"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
d:DesignHeight="450"
@@ -17,8 +17,8 @@
Margin="0,10"
BorderThickness="0"
IsReadOnly="True"
Foreground="{x:Static UI:Theme.Heading}"
Background="{x:Static UI:Theme.Background}" />
Foreground="{x:Static mpvnet:Theme.Heading}"
Background="{x:Static mpvnet:Theme.Background}" />
<ItemsControl x:Name="ItemsControl">
<ItemsControl.ItemTemplate>
@@ -32,7 +32,7 @@
FontSize="16"
FontWeight="Normal"
VerticalAlignment="Top"
Foreground="{x:Static UI:Theme.Foreground}"/>
Foreground="{x:Static mpvnet:Theme.Foreground}"/>
<TextBox x:Name="ItemHelpTextBox"
TextWrapping="WrapWithOverflow"
@@ -41,8 +41,8 @@
Margin="10,0,0,0" BorderThickness="0"
IsReadOnly="True" Padding="7,0,0,0"
MinHeight="0"
Foreground="{x:Static UI:Theme.Foreground2}"
Background="{x:Static UI:Theme.Background}"/>
Foreground="{x:Static mpvnet:Theme.Foreground2}"
Background="{x:Static mpvnet:Theme.Background}"/>
</WrapPanel>
</DataTemplate>
@@ -54,8 +54,8 @@
BorderThickness="0"
IsReadOnly="True"
Margin="0,10,0,0"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"/>
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"/>
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
</TextBlock>

View File

@@ -1,35 +1,42 @@
using System.Windows;

using System.Windows;
using System.Windows.Controls;
namespace DynamicGUI
{
public partial class OptionSettingControl : UserControl, ISettingControl
{
private OptionSetting OptionSetting;
OptionSetting OptionSetting;
public OptionSettingControl(OptionSetting optionSetting)
{
OptionSetting = optionSetting;
InitializeComponent();
TitleTextBox.Text = optionSetting.Name;
if (string.IsNullOrEmpty(optionSetting.Help))
HelpTextBox.Visibility = Visibility.Collapsed;
HelpTextBox.Text = optionSetting.Help;
ItemsControl.ItemsSource = optionSetting.Options;
if (string.IsNullOrEmpty(optionSetting.URL))
LinkTextBlock.Visibility = Visibility.Collapsed;
Link.SetURL(optionSetting.URL);
}
private string _SearchableText;
string _SearchableText;
public string SearchableText {
get {
if (_SearchableText is null)
{
_SearchableText = TitleTextBox.Text + HelpTextBox.Text;
foreach (var i in OptionSetting.Options)
_SearchableText += i.Text + i.Help + i.Name;
_SearchableText = _SearchableText.ToLower();
}
return _SearchableText;

View File

@@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DynamicGUI"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
@@ -16,8 +16,8 @@
Margin="0,10"
BorderThickness="0"
IsReadOnly="True"
Foreground="{x:Static UI:Theme.Heading}"
Background="{x:Static UI:Theme.Background}"/>
Foreground="{x:Static mpvnet:Theme.Heading}"
Background="{x:Static mpvnet:Theme.Background}"/>
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
@@ -29,9 +29,9 @@
Width="150"
Height="20"
HorizontalAlignment="Left"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
CaretBrush="{x:Static UI:Theme.Foreground}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
CaretBrush="{x:Static mpvnet:Theme.Foreground}"
TextChanged="ValueTextBox_TextChanged"/>
<Button x:Name="Button"
@@ -47,8 +47,8 @@
TextWrapping="WrapWithOverflow"
BorderThickness="0"
IsReadOnly="True"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"/>
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"/>
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>

View File

@@ -1,5 +1,5 @@
using System;
using System.Diagnostics;

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
@@ -10,7 +10,7 @@ namespace DynamicGUI
{
public partial class StringSettingControl : UserControl, ISettingControl
{
private StringSetting StringSetting;
StringSetting StringSetting;
public StringSettingControl(StringSetting stringSetting)
{
@@ -19,21 +19,26 @@ namespace DynamicGUI
TitleTextBox.Text = stringSetting.Name;
HelpTextBox.Text = stringSetting.Help;
ValueTextBox.Text = StringSetting.Value;
if (StringSetting.Width > 0)
ValueTextBox.Width = StringSetting.Width;
if (StringSetting.Type != "folder" && StringSetting.Type != "color")
Button.Visibility = Visibility.Hidden;
Link.SetURL(StringSetting.URL);
if (string.IsNullOrEmpty(stringSetting.URL))
LinkTextBlock.Visibility = Visibility.Collapsed;
}
private string _SearchableText;
string _SearchableText;
public string SearchableText {
get {
if (_SearchableText is null)
_SearchableText = (TitleTextBox.Text + HelpTextBox.Text +ValueTextBox.Text).ToLower();
return _SearchableText;
}
}
@@ -47,7 +52,7 @@ namespace DynamicGUI
set => StringSetting.Value = value;
}
private void Button_Click(object sender, RoutedEventArgs e)
void Button_Click(object sender, RoutedEventArgs e)
{
switch (StringSetting.Type)
{
@@ -56,6 +61,7 @@ namespace DynamicGUI
{
d.Description = "Choose a folder.";
d.SelectedPath = ValueTextBox.Text;
if (d.ShowDialog() == WinForms.DialogResult.OK)
ValueTextBox.Text = d.SelectedPath;
}
@@ -65,7 +71,8 @@ namespace DynamicGUI
{
dialog.FullOpen = true;
try {
try
{
if (!string.IsNullOrEmpty(ValueTextBox.Text))
{
Color col = GetColor(ValueTextBox.Text);
@@ -80,7 +87,7 @@ namespace DynamicGUI
}
}
private void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update();
void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update();
Color GetColor(string value)
{
@@ -104,7 +111,10 @@ namespace DynamicGUI
if (StringSetting.Type == "color")
{
Color c = Colors.Transparent;
if (ValueTextBox.Text != "") try { c = GetColor(ValueTextBox.Text); } catch {}
if (ValueTextBox.Text != "")
try { c = GetColor(ValueTextBox.Text); } catch {}
ValueTextBox.Background = new SolidColorBrush(c);
}
}

Binary file not shown.

View File

@@ -1,9 +1,12 @@
using System;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using UI;
using System.Threading.Tasks;
using static mpvnet.Core;
namespace mpvnet
{
@@ -15,15 +18,14 @@ namespace mpvnet
public static string[] SubtitleTypes { get; } = { "srt", "ass", "idx", "sup", "ttxt", "ssa", "smi" };
public static string RegPath { get; } = @"HKCU\Software\" + Application.ProductName;
public static string ConfPath { get => mp.ConfigFolder + "mpvnet.conf"; }
public static string ConfPath { get => core.ConfigFolder + "mpvnet.conf"; }
public static string ProcessInstance { get; set; } = "single";
public static string DarkMode { get; set; } = "always";
public static string DarkTheme { get; set; } = "dark";
public static string LightTheme { get; set; } = "light";
public static string StartSize { get; set; } = "previous";
public static bool RememberHeight { get; set; } = true;
public static bool RememberPosition { get; set; }
public static bool Maximized { get; set; }
public static bool DebugMode { get; set; }
public static bool IsStartedFromTerminal { get; } = Environment.GetEnvironmentVariable("_started_from_console") == "yes";
public static bool RememberVolume { get; set; } = true;
@@ -34,7 +36,7 @@ namespace mpvnet
public static int StartThreshold { get; set; } = 1500;
public static int RecentCount { get; set; } = 15;
public static float MinimumAspectRatio { get; set; } = 1.3f;
public static float MinimumAspectRatio { get; set; } = 1.2f;
public static Extension Extension { get; set; }
@@ -44,8 +46,8 @@ namespace mpvnet
public static void Init()
{
string dummy = mp.ConfigFolder;
var dummy2 = mp.Conf;
string dummy = core.ConfigFolder;
var dummy2 = core.Conf;
foreach (var i in Conf)
ProcessProperty(i.Key, i.Value, true);
@@ -54,7 +56,7 @@ namespace mpvnet
{
try
{
string filePath = mp.ConfigFolder + "mpvnet-debug.log";
string filePath = core.ConfigFolder + "mpvnet-debug.log";
if (File.Exists(filePath))
File.Delete(filePath);
@@ -73,33 +75,77 @@ namespace mpvnet
string themeContent = null;
if (File.Exists(mp.ConfigFolder + "theme.conf"))
themeContent = File.ReadAllText(mp.ConfigFolder + "theme.conf");
if (File.Exists(core.ConfigFolder + "theme.conf"))
themeContent = File.ReadAllText(core.ConfigFolder + "theme.conf");
Theme.Init(
themeContent,
Properties.Resources.theme,
IsDarkMode ? DarkTheme : LightTheme);
mp.Shutdown += Shutdown;
mp.Initialized += Initialized;
core.Shutdown += Shutdown;
core.Initialized += Initialized;
}
private static void Initialized()
public static void RunAction(Action action)
{
if (RememberVolume)
Task.Run(() => {
try
{
mp.set_property_int("volume", RegistryHelp.GetInt(App.RegPath, "Volume", 70));
mp.set_property_string("mute", RegistryHelp.GetString(App.RegPath, "Mute", "no"));
action.Invoke();
Debug.WriteLine(Environment.TickCount);
}
catch (Exception e)
{
ShowException(e);
}
});
}
public static void ShowException(object obj)
{
if (obj is Exception e)
{
if (App.IsStartedFromTerminal)
ConsoleHelp.WriteError(e.ToString());
else
Msg.ShowException(e);
}
else
{
if (App.IsStartedFromTerminal)
ConsoleHelp.WriteError(obj.ToString());
else
Msg.ShowError(obj.ToString());
}
}
private static void Shutdown()
public static void ShowError(string title, string msg)
{
if (App.IsStartedFromTerminal)
{
ConsoleHelp.WriteError(title);
ConsoleHelp.WriteError(msg);
}
else
Msg.ShowError(title, msg);
}
static void Initialized()
{
if (RememberVolume)
{
RegistryHelp.SetValue(App.RegPath, "Volume", mp.get_property_int("volume"));
RegistryHelp.SetValue(App.RegPath, "Mute", mp.get_property_string("mute"));
core.set_property_int("volume", RegistryHelp.GetInt(App.RegPath, "Volume", 70));
core.set_property_string("mute", RegistryHelp.GetString(App.RegPath, "Mute", "no"));
}
}
static void Shutdown()
{
if (RememberVolume)
{
RegistryHelp.SetValue(App.RegPath, "Volume", core.get_property_int("volume"));
RegistryHelp.SetValue(App.RegPath, "Mute", core.get_property_string("mute"));
}
}
@@ -125,13 +171,12 @@ namespace mpvnet
switch (name)
{
case "remember-position": RememberPosition = value == "yes"; return true;
case "maximized": Maximized = value == "yes"; return true;
case "debug-mode": DebugMode = value == "yes"; return true;
case "remember-volume": RememberVolume = value == "yes"; return true;
case "queue": Queue = value == "yes"; return true;
case "auto-load-folder": AutoLoadFolder = value == "yes"; return true;
case "update-check": UpdateCheck = value == "yes"; return true;
case "start-size": RememberHeight = value == "previous"; return true;
case "start-size": StartSize = value; return true;
case "process-instance": ProcessInstance = value; return true;
case "dark-mode": DarkMode = value; return true;
case "start-threshold": StartThreshold = value.Int(); return true;

View File

@@ -9,9 +9,12 @@ using System.Windows.Interop;
using VB = Microsoft.VisualBasic;
using static NewLine;
using static mpvnet.Core;
namespace mpvnet
{
public class Command
public class Commands
{
public static void Execute(string id, string[] args)
{
@@ -33,21 +36,20 @@ namespace mpvnet
case "show-about": ShowDialog(typeof(AboutWindow)); break;
case "show-conf-editor": ShowDialog(typeof(ConfWindow)); break;
case "show-input-editor": ShowDialog(typeof(InputWindow)); break;
case "open-conf-folder": Process.Start(mp.ConfigFolder); break;
case "open-conf-folder": Process.Start(core.ConfigFolder); break;
case "shell-execute": Process.Start(args[0]); break;
case "show-info": ShowInfo(); break;
case "playlist-first": PlaylistFirst(); break;
case "playlist-last": PlaylistLast(); break;
case "show-profiles": ShowProfiles(); break;
case "show-properties": ShowProperties(); break;
case "show-commands": ShowCommands(); break;
case "add-files-to-playlist": OpenFiles("append"); break; // deprecated 2019
default: Msg.ShowError($"No command '{id}' found."); break;
}
MainForm.Instance.BeginInvoke(new Action(() => {
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
Native.SendMessage(MainForm.Instance.Handle, m.Msg, m.WParam, m.LParam);
}));
}
public static void InvokeOnMainThread(Action action) => MainForm.Instance.Invoke(action);
public static void InvokeOnMainThread(Action action) => MainForm.Instance.BeginInvoke(action);
public static void ShowDialog(Type winType)
{
@@ -72,7 +74,7 @@ namespace mpvnet
InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog() { Multiselect = true })
if (d.ShowDialog() == DialogResult.OK)
mp.Load(d.FileNames, loadFolder, append);
core.LoadFiles(d.FileNames, loadFolder, append);
}));
}
@@ -88,31 +90,46 @@ namespace mpvnet
{
if (Directory.Exists(d.SelectedPath + "\\BDMV"))
{
mp.set_property_string("bluray-device", d.SelectedPath);
mp.Load(new[] { @"bd://" }, false, false);
core.set_property_string("bluray-device", d.SelectedPath);
core.LoadFiles(new[] { @"bd://" }, false, false);
}
else
{
mp.set_property_string("dvd-device", d.SelectedPath);
mp.Load(new[] { @"dvd://" }, false, false);
core.set_property_string("dvd-device", d.SelectedPath);
core.LoadFiles(new[] { @"dvd://" }, false, false);
}
}
}
}));
}
public static void PlaylistLast() => mp.set_property_int("playlist-pos", mp.get_property_int("playlist-count") - 1);
public static void PlaylistFirst()
{
int pos = core.get_property_int("playlist-pos");
if (pos != 0)
core.set_property_int("playlist-pos", 0);
}
public static void PlaylistLast()
{
int pos = core.get_property_int("playlist-pos");
int count = core.get_property_int("playlist-count");
if (pos < count - 1)
core.set_property_int("playlist-pos", count - 1);
}
public static void ShowHistory()
{
if (File.Exists(mp.ConfigFolder + "history.txt"))
Process.Start(mp.ConfigFolder + "history.txt");
if (File.Exists(core.ConfigFolder + "history.txt"))
Process.Start(core.ConfigFolder + "history.txt");
else
{
if (Msg.ShowQuestion("Create history.txt file in config folder?",
"mpv.net will write the date, time and filename of opened files to it.") == MsgResult.OK)
File.WriteAllText(mp.ConfigFolder + "history.txt", "");
File.WriteAllText(core.ConfigFolder + "history.txt", "");
}
}
@@ -122,13 +139,13 @@ namespace mpvnet
{
string performer, title, album, genre, date, duration, text = "";
long fileSize = 0;
string path = mp.get_property_string("path");
string path = core.get_property_string("path");
if (path.Contains("://"))
path = mp.get_property_string("media-title");
path = core.get_property_string("media-title");
int width = mp.get_property_int("video-params/w");
int height = mp.get_property_int("video-params/h");
int width = core.get_property_int("video-params/w");
int height = core.get_property_int("video-params/h");
if (File.Exists(path))
{
@@ -154,7 +171,7 @@ namespace mpvnet
text += "Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n";
text += "Type: " + path.ShortExt().ToUpper();
mp.commandv("show-text", text, "5000");
core.commandv("show-text", text, "5000");
return;
}
}
@@ -168,16 +185,16 @@ namespace mpvnet
"Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n" +
"Type: " + path.ShortExt().ToUpper();
mp.commandv("show-text", text, "5000");
core.commandv("show-text", text, "5000");
return;
}
}
}
TimeSpan position = TimeSpan.FromSeconds(mp.get_property_number("time-pos"));
TimeSpan duration2 = TimeSpan.FromSeconds(mp.get_property_number("duration"));
string videoFormat = mp.get_property_string("video-format").ToUpper();
string audioCodec = mp.get_property_string("audio-codec-name").ToUpper();
TimeSpan position = TimeSpan.FromSeconds(core.get_property_number("time-pos"));
TimeSpan duration2 = TimeSpan.FromSeconds(core.get_property_number("duration"));
string videoFormat = core.get_property_string("video-format").ToUpper();
string audioCodec = core.get_property_string("audio-codec-name").ToUpper();
text = path.FileName() + "\n" +
FormatTime(position.TotalMinutes) + ":" +
@@ -191,12 +208,12 @@ namespace mpvnet
text += $"{videoFormat}\n{audioCodec}";
mp.commandv("show-text", text, "5000");
core.commandv("show-text", text, "5000");
string FormatTime(double value) => ((int)value).ToString("00");
}
catch (Exception e)
{
Msg.ShowException(e);
App.ShowException(e);
}
}
@@ -204,9 +221,12 @@ namespace mpvnet
{
InvokeOnMainThread(new Action(() => {
string command = VB.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegistryHelp.GetString(App.RegPath, "RecentExecutedCommand"));
if (string.IsNullOrEmpty(command)) return;
if (string.IsNullOrEmpty(command))
return;
RegistryHelp.SetValue(App.RegPath, "RecentExecutedCommand", command);
mp.command(command, false);
core.command(command, false);
}));
}
@@ -214,12 +234,14 @@ namespace mpvnet
{
InvokeOnMainThread(new Action(() => {
string clipboard = System.Windows.Forms.Clipboard.GetText();
if (string.IsNullOrEmpty(clipboard) || (!clipboard.Contains("://") && !File.Exists(clipboard)) || clipboard.Contains("\n"))
{
Msg.ShowError("The clipboard does not contain a valid URL or file, URLs have to contain :// and are not allowed to contain a newline character.");
App.ShowError("No URL found", "The clipboard does not contain a valid URL or file.");
return;
}
mp.Load(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
core.LoadFiles(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
}));
}
@@ -228,12 +250,16 @@ namespace mpvnet
InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog())
{
string path = mp.get_property_string("path");
if (File.Exists(path)) d.InitialDirectory = Path.GetDirectoryName(path);
string path = core.get_property_string("path");
if (File.Exists(path))
d.InitialDirectory = Path.GetDirectoryName(path);
d.Multiselect = true;
if (d.ShowDialog() == DialogResult.OK)
foreach (string i in d.FileNames)
mp.commandv("sub-add", i);
foreach (string filename in d.FileNames)
core.commandv("sub-add", filename);
}
}));
}
@@ -243,33 +269,98 @@ namespace mpvnet
InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog())
{
string path = mp.get_property_string("path");
string path = core.get_property_string("path");
if (File.Exists(path))
d.InitialDirectory = Path.GetDirectoryName(path);
d.Multiselect = true;
if (d.ShowDialog() == DialogResult.OK)
foreach (string i in d.FileNames)
mp.commandv("audio-add", i);
core.commandv("audio-add", i);
}
}));
}
public static void CycleAudio()
{
string path = mp.get_property_string("path");
if (!File.Exists(path)) return;
string path = core.get_property_string("path");
if (!File.Exists(path))
return;
using (MediaInfo mi = new MediaInfo(path))
{
MediaTrack[] audTracks = mp.MediaTracks.Where(track => track.Type == "a").ToArray();
if (audTracks.Length < 2) return;
int aid = mp.get_property_int("aid");
MediaTrack[] audTracks = core.MediaTracks.Where(track => track.Type == "a").ToArray();
if (audTracks.Length < 2)
return;
int aid = core.get_property_int("aid");
aid += 1;
if (aid > audTracks.Length) aid = 1;
mp.commandv("set", "aid", aid.ToString());
mp.commandv("show-text", audTracks[aid - 1].Text.Substring(3), "5000");
}
if (aid > audTracks.Length)
aid = 1;
core.commandv("set", "aid", aid.ToString());
core.commandv("show-text", audTracks[aid - 1].Text.Substring(3), "5000");
}
}
static void ShowProfiles()
{
string code = @"
foreach ($item in ($json | ConvertFrom-Json | foreach { $_ } | sort name))
{
$item.name
''
foreach ($option in $item.options)
{
' ' + $option.key + ' = ' + $option.value
}
''
}";
string json = core.get_property_string("profile-list");
string file = Path.GetTempPath() + @"\mpv profile-list.txt";
File.WriteAllText(file, BR + PowerShell.InvokeAndReturnString(code, "json", json));
Process.Start(file);
}
static void ShowCommands()
{
string code = @"
foreach ($item in ($json | ConvertFrom-Json | foreach { $_ } | sort name))
{
''
$item.name
foreach ($arg in $item.args)
{
$value = $arg.name + ' <' + $arg.type.ToLower() + '>'
if ($arg.optional -eq $true)
{
$value = '[' + $value + ']'
}
' ' + $value
}
}";
string json = core.get_property_string("command-list");
string file = Path.GetTempPath() + @"\mpv command-list.txt";
File.WriteAllText(file, PowerShell.InvokeAndReturnString(code, "json", json) + BR);
Process.Start(file);
}
static void ShowProperties()
{
string file = Path.GetTempPath() + @"\mpv property-list.txt";
var props = core.get_property_string("property-list").Split(',').OrderBy(prop => prop);
File.WriteAllText(file, BR + string.Join(BR, props) + BR);
Process.Start(file);
}
}
}

View File

@@ -1,10 +1,13 @@
using System;

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.IO;
using System.Linq;
using static mpvnet.Core;
namespace mpvnet
{
public class Extension
@@ -25,20 +28,23 @@ namespace mpvnet
{
string[] knownExtensions = { "RatingExtension", "ScriptingExtension" };
foreach (string path in Directory.GetDirectories(dir))
foreach (string extDir in Directory.GetDirectories(dir))
{
if (knownExtensions.Contains(Path.GetFileName(path)))
catalog.Catalogs.Add(new DirectoryCatalog(path, "*Extension.dll"));
if (knownExtensions.Contains(Path.GetFileName(extDir)))
catalog.Catalogs.Add(new DirectoryCatalog(extDir, Path.GetFileName(extDir) + ".dll"));
else
Msg.ShowError("Failed to load extension", path + "\n\nOnly extensions that ship with mpv.net are allowed in <startup>\\extensions\n\nUser extensions have to use <config folder>\\extensions\n\nNever copy or install a new mpv.net version over a old mpv.net version.");
ConsoleHelp.WriteError("Failed to load extension:\n\n" + extDir +
"\n\nOnly extensions that ship with mpv.net are allowed in <startup>\\extensions" +
"\n\nUser extensions have to use <config folder>\\extensions" +
"\n\nNever copy or install a new mpv.net version over a old mpv.net version.");
}
}
dir = mp.ConfigFolder + "extensions";
dir = core.ConfigFolder + "extensions";
if (Directory.Exists(dir))
foreach (string i in Directory.GetDirectories(dir))
catalog.Catalogs.Add(new DirectoryCatalog(i, "*Extension.dll"));
foreach (string extDir in Directory.GetDirectories(dir))
catalog.Catalogs.Add(new DirectoryCatalog(extDir, Path.GetFileName(extDir) + ".dll"));
if (catalog.Catalogs.Count > 0)
{
@@ -48,7 +54,7 @@ namespace mpvnet
}
catch (Exception ex)
{
Msg.ShowException(ex);
App.ShowException(ex);
}
}
}

View File

@@ -1,14 +1,111 @@

using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
class ConsoleHelp
namespace mpvnet
{
public static void WriteError(object obj)
public static class ConsoleHelp
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(obj);
public static int Padding { get; set; }
public static void WriteError(object obj, string module = "mpv.net")
{
Write(obj, module, ConsoleColor.Red, false);
}
public static void Write(object obj, string module = "mpv.net")
{
Write(obj, module, ConsoleColor.Black, true);
}
public static void Write(object obj, string module, ConsoleColor color)
{
Write(obj, module, color, false);
}
public static void Write(object obj, string module, ConsoleColor color, bool useDefaultColor)
{
if (obj == null)
return;
string value = obj.ToString();
if (!string.IsNullOrEmpty(module))
module = "[" + module + "] ";
if (useDefaultColor)
Console.ResetColor();
else
Console.ForegroundColor = color;
value = module + value;
if (Padding > 0 && value.Length < Padding)
value = value.PadRight(Padding);
Console.WriteLine(value);
Console.ResetColor();
Trace.WriteLine(obj);
}
}
public class CursorHelp
{
static bool IsVisible = true;
public static void Show()
{
if (!IsVisible)
{
Cursor.Show();
IsVisible = true;
}
}
public static void Hide()
{
if (IsVisible)
{
Cursor.Hide();
IsVisible = false;
}
}
public static bool IsPosDifferent(Point screenPos)
{
return
Math.Abs(screenPos.X - Control.MousePosition.X) > 10 ||
Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10;
}
}
public class mpvHelp
{
public static string WM_APPCOMMAND_to_mpv_key(int value)
{
switch (value)
{
case 5: return "SEARCH"; // BROWSER_SEARCH
case 6: return "FAVORITES"; // BROWSER_FAVORITES
case 7: return "HOMEPAGE"; // BROWSER_HOME
case 15: return "MAIL"; // LAUNCH_MAIL
case 33: return "PRINT"; // PRINT
case 11: return "NEXT"; // MEDIA_NEXTTRACK
case 12: return "PREV"; // MEDIA_PREVIOUSTRACK
case 13: return "STOP"; // MEDIA_STOP
case 14: return "PLAYPAUSE"; // MEDIA_PLAY_PAUSE
case 46: return "PLAY"; // MEDIA_PLAY
case 47: return "PAUSE"; // MEDIA_PAUSE
case 48: return "RECORD"; // MEDIA_RECORD
case 49: return "FORWARD"; // MEDIA_FAST_FORWARD
case 50: return "REWIND"; // MEDIA_REWIND
case 51: return "CHANNEL_UP"; // MEDIA_CHANNEL_UP
case 52: return "CHANNEL_DOWN"; // MEDIA_CHANNEL_DOWN
}
return null;
}
}
}

View File

@@ -1,10 +1,8 @@

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
@@ -14,6 +12,8 @@ using System.Windows.Forms;
using Microsoft.Win32;
using static mpvnet.Core;
namespace mpvnet
{
public class Sys
@@ -119,12 +119,12 @@ namespace mpvnet
public CommandItem(SerializationInfo info, StreamingContext context) { }
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private string _Input = "";
string _Input = "";
public string Input {
get => _Input;
@@ -175,73 +175,22 @@ namespace mpvnet
if (item.Command.ToLower() == "ignore")
item.Command = "";
MigrateCommands(item);
items.Add(item);
}
}
return items;
}
private static ObservableCollection<CommandItem> _Items;
static ObservableCollection<CommandItem> _Items;
public static ObservableCollection<CommandItem> Items {
get {
if (_Items is null)
_Items = GetItems(File.ReadAllText(mp.InputConfPath));
_Items = GetItems(File.ReadAllText(core.InputConfPath));
return _Items;
}
}
// last change 2019
public static void MigrateCommands(CommandItem item)
{
switch (item.Command)
{
case "script-message mpv.net show-prefs":
item.Command = "script-message mpv.net show-conf-editor";
break;
case "script-message mpv.net show-keys":
item.Command = "script-message mpv.net show-input-editor";
break;
case "script-message mpv.net history":
item.Command = "script-message mpv.net show-history";
break;
case "script-message mpv.net open-config-folder":
item.Command = "script-message open-conf-folder";
break;
}
}
}
public class CursorHelp
{
static bool IsVisible = true;
public static void Show()
{
if (!IsVisible)
{
Cursor.Show();
IsVisible = true;
}
}
public static void Hide()
{
if (IsVisible)
{
Cursor.Hide();
IsVisible = false;
}
}
public static bool IsPosDifferent(Point screenPos)
{
return
Math.Abs(screenPos.X - Control.MousePosition.X) > 10 ||
Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10;
}
}
public class Folder

8
mpv.net/Misc/NewLine.cs Normal file
View File

@@ -0,0 +1,8 @@

using System;
public static class NewLine
{
public static string BR = Environment.NewLine;
public static string BR2 = Environment.NewLine + Environment.NewLine;
}

244
mpv.net/Misc/PowerShell.cs Normal file
View File

@@ -0,0 +1,244 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;
using System.Threading.Tasks;
using static mpvnet.Core;
using static NewLine;
namespace mpvnet
{
public class PowerShell
{
public Runspace Runspace { get; set; }
public Pipeline Pipeline { get; set; }
public string Module { get; set; }
public bool Print { get; set; }
public List<string> Scripts { get; } = new List<string>();
public List<KeyValuePair<string, object>> Variables = new List<KeyValuePair<string, object>>();
public string[] Parameters { get; }
public event Action<string, object[]> Event;
public event Action<string, object> PropertyChanged;
public List<KeyValuePair<string, ScriptBlock>> EventHandlers = new List<KeyValuePair<string, ScriptBlock>>();
public List<KeyValuePair<string, ScriptBlock>> PropChangedHandlers = new List<KeyValuePair<string, ScriptBlock>>();
public static List<PowerShell> Instances { get; } = new List<PowerShell>();
public object Invoke() => Invoke(null, null);
public object Invoke(string variable, object obj)
{
try
{
Runspace = RunspaceFactory.CreateRunspace();
Runspace.ApartmentState = ApartmentState.STA;
Runspace.Open();
Pipeline = Runspace.CreatePipeline();
foreach (string script in Scripts)
Pipeline.Commands.AddScript(script);
if (Parameters != null)
foreach (string param in Parameters)
foreach (Command command in Pipeline.Commands)
command.Parameters.Add(null, param);
Runspace.SessionStateProxy.SetVariable("mp", this);
foreach (var i in Variables)
Runspace.SessionStateProxy.SetVariable(i.Key, i.Value);
if (!string.IsNullOrEmpty(variable))
Runspace.SessionStateProxy.SetVariable(variable, obj);
if (Print)
{
Pipeline.Output.DataReady += Output_DataReady;
Pipeline.Error.DataReady += Error_DataReady;
}
return Pipeline.Invoke();
}
catch (RuntimeException e)
{
string message = e.Message + BR + BR + e.ErrorRecord.ScriptStackTrace.Replace(
" <ScriptBlock>, <No file>", "") + BR + BR + Module + BR;
throw new PowerShellException(message);
}
catch (Exception e)
{
throw e;
}
}
public static string InvokeAndReturnString(string code, string varName, object varValue)
{
PowerShell ps = new PowerShell() { Print = false };
ps.Scripts.Add(code);
string ret = string.Join(Environment.NewLine, (ps.Invoke(varName, varValue)
as IEnumerable<object>).Select(item => item.ToString())).ToString();
ps.Runspace.Dispose();
return ret;
}
public void Output_DataReady(object sender, EventArgs e)
{
var output = sender as PipelineReader<PSObject>;
while (output.Count > 0)
ConsoleHelp.Write(output.Read(), Module);
}
public void Error_DataReady(object sender, EventArgs e)
{
var output = sender as PipelineReader<Object>;
while (output.Count > 0)
ConsoleHelp.WriteError(output.Read(), Module);
}
public void RedirectStreams(PSEventJob job)
{
if (Print)
{
job.Output.DataAdded += Output_DataAdded;
job.Error.DataAdded += Error_DataAdded;
}
}
public void commandv(params string[] args) => core.commandv(args);
public void command(string command) => core.command(command);
public bool get_property_bool(string name) => core.get_property_bool(name);
public void set_property_bool(string name, bool value) => core.set_property_bool(name, value);
public int get_property_int(string name) => core.get_property_int(name);
public void set_property_int(string name, int value) => core.set_property_int(name, value);
public double get_property_number(string name) => core.get_property_number(name);
public void set_property_number(string name, double value) => core.set_property_number(name, value);
public string get_property_string(string name) => core.get_property_string(name);
public void set_property_string(string name, string value) => core.set_property_string(name, value);
public void observe_property(string name, string type, ScriptBlock sb)
{
PropChangedHandlers.Add(new KeyValuePair<string, ScriptBlock>(name, sb));
switch (type)
{
case "bool": case "boolean":
core.observe_property_bool(name, (value) => Task.Run(() => PropertyChanged.Invoke(name, value)));
break;
case "string":
core.observe_property_string(name, (value) => Task.Run(() => PropertyChanged.Invoke(name, value)));
break;
case "int": case "integer":
core.observe_property_int(name, (value) => Task.Run(() => PropertyChanged.Invoke(name, value)));
break;
case "float": case "double":
core.observe_property_double(name, (value) => Task.Run(() => PropertyChanged.Invoke(name, value)));
break;
case "nil": case "none": case "native":
core.observe_property(name, () => Task.Run(() => PropertyChanged.Invoke(name, null)));
break;
default:
App.ShowError("Invalid Type", "Valid types are: bool or boolean, string, int or integer, float or double, nil or none or native");
break;
}
}
public void register_event(string name, ScriptBlock sb)
{
EventHandlers.Add(new KeyValuePair<string, ScriptBlock>(name, sb));
switch (name)
{
case "log-message":
core.LogMessageAsync += (level, msg) => Event.Invoke("log-message", new object[] { level, msg });
break;
case "end-file":
core.EndFileAsync += (reason) => Event.Invoke("end-file", new object[] { reason });
break;
case "client-message":
core.ClientMessageAsync += (args) => Event.Invoke("client-message", args);
break;
case "shutdown":
core.Shutdown += () => Event.Invoke("shutdown", null);
break;
case "get-property-reply":
core.GetPropertyReplyAsync += () => Event.Invoke("get-property-reply", null);
break;
case "set-property-reply":
core.SetPropertyReplyAsync += () => Event.Invoke("set-property-reply", null);
break;
case "command-reply":
core.CommandReplyAsync += () => Event.Invoke("command-reply", null);
break;
case "start-file":
core.StartFileAsync += () => Event.Invoke("start-file", null);
break;
case "file-loaded":
core.FileLoadedAsync += () => Event.Invoke("file-loaded", null);
break;
case "idle":
core.IdleAsync += () => Event.Invoke("idle", null);
break;
case "video-reconfig":
core.VideoReconfigAsync += () => Event.Invoke("video-reconfig", null);
break;
case "audio-reconfig":
core.AudioReconfigAsync += () => Event.Invoke("audio-reconfig", null);
break;
case "seek":
core.SeekAsync += () => Event.Invoke("seek", null);
break;
case "playback-restart":
core.PlaybackRestartAsync += () => Event.Invoke("playback-restart", null);
break;
}
}
void Output_DataAdded(object sender, DataAddedEventArgs e)
{
var output = sender as PSDataCollection<PSObject>;
ConsoleHelp.Write(output[e.Index], Module);
}
void Error_DataAdded(object sender, DataAddedEventArgs e)
{
var error = sender as PSDataCollection<ErrorRecord>;
ConsoleHelp.WriteError(error[e.Index], Module);
}
}
public class PowerShellException : Exception
{
public PowerShellException(string message) : base(message)
{
}
}
}

View File

@@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using static mpvnet.Core;
namespace mpvnet
{
static class Program
@@ -19,9 +21,9 @@ namespace mpvnet
Application.SetCompatibleTextRenderingDefault(false);
if (App.IsStartedFromTerminal)
Native.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
WinAPI.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
if (mp.ConfigFolder == "")
if (core.ConfigFolder == "")
return;
string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();
@@ -61,15 +63,15 @@ namespace mpvnet
{
if (proc.MainWindowHandle != IntPtr.Zero)
{
Native.AllowSetForegroundWindow(proc.Id);
var data = new Native.COPYDATASTRUCT();
WinAPI.AllowSetForegroundWindow(proc.Id);
var data = new WinAPI.COPYDATASTRUCT();
data.lpData = string.Join("\n", files.ToArray());
data.cbData = data.lpData.Length * 2 + 1;
Native.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
WinAPI.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
mutex.Dispose();
if (App.IsStartedFromTerminal)
Native.FreeConsole();
WinAPI.FreeConsole();
return;
}
@@ -85,7 +87,7 @@ namespace mpvnet
Application.Run(new MainForm());
if (App.IsStartedFromTerminal)
Native.FreeConsole();
WinAPI.FreeConsole();
mutex.Dispose();
}

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Windows.Media;
namespace UI
namespace mpvnet
{
public class Theme
{

View File

@@ -6,6 +6,8 @@ using System.Reflection;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using static mpvnet.Core;
namespace mpvnet
{
class UpdateCheck
@@ -33,15 +35,16 @@ namespace mpvnet
Version onlineVersion = Version.Parse(match.Groups[1].Value);
Version currentVersion = Assembly.GetEntryAssembly().GetName().Version;
if (onlineVersion == currentVersion)
if (onlineVersion <= currentVersion)
{
if (showUpToDateMessage)
Msg.Show($"{Application.ProductName} is up to date.");
return;
}
if (RegistryHelp.GetString(RegistryHelp.ApplicationKey, "UpdateCheckVersion")
!= onlineVersion.ToString() && Msg.ShowQuestion(
if ((RegistryHelp.GetString(RegistryHelp.ApplicationKey, "UpdateCheckVersion")
!= onlineVersion.ToString() || showUpToDateMessage) && Msg.ShowQuestion(
$"New version {onlineVersion} is available, update now?") == MsgResult.OK)
{
string arch = IntPtr.Size == 8 ? "64" : "86";
@@ -49,8 +52,10 @@ namespace mpvnet
using (Process proc = new Process())
{
proc.StartInfo.UseShellExecute = true;
proc.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
proc.StartInfo.FileName = "PowerShell";
proc.StartInfo.Arguments = $"-NoLogo -NoExit -File \"{Folder.Startup + "Update.ps1"}\" \"{url}\" \"{Application.StartupPath}\"";
proc.StartInfo.Arguments = $"-NoLogo -NoExit -File \"{Folder.Startup + "Update.ps1"}\" \"{url}\" \"{Application.StartupPath.TrimEnd('\\')}\"";
if (Folder.Startup.Contains("Program Files"))
proc.StartInfo.Verb = "runas";
@@ -58,7 +63,7 @@ namespace mpvnet
proc.Start();
}
mp.command("quit");
core.command("quit");
}
RegistryHelp.SetValue(RegistryHelp.ApplicationKey, "UpdateCheckVersion", onlineVersion.ToString());

View File

@@ -1,45 +1,44 @@
using System;

using System;
using System.Runtime.InteropServices;
public class MediaInfo : IDisposable
{
IntPtr Handle;
static bool Loaded;
public MediaInfo(string sourcepath)
public MediaInfo(string file)
{
if (!Loaded)
{
if (Native.LoadLibrary("MediaInfo.dll") == IntPtr.Zero)
throw new Exception("Failed to load MediaInfo.dll.");
if ((Handle = MediaInfo_New()) == IntPtr.Zero)
throw new Exception("Failed to call MediaInfo_New");
Loaded = true;
if (MediaInfo_Open(Handle, file) == 0)
throw new Exception("Error MediaInfo_Open");
}
Handle = MediaInfo_New();
MediaInfo_Open(Handle, sourcepath);
public string GetInfo(MediaInfoStreamKind kind, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, kind, 0,
parameter, MediaInfoKind.Text, MediaInfoKind.Name));
}
public string GetInfo(MediaInfoStreamKind streamKind, string parameter)
public int GetCount(MediaInfoStreamKind kind) => MediaInfo_Count_Get(Handle, kind, -1);
public string GetVideo(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, streamKind, 0, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
}
public int GetCount(MediaInfoStreamKind streamKind) => MediaInfo_Count_Get(Handle, streamKind, -1);
public string GetVideo(int streamNumber, string parameter)
public string GetAudio(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Audio,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
}
public string GetAudio(int streamNumber, string parameter)
public string GetText(int stream, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Audio, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
}
public string GetText(int streamNumber, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Text, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Text,
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
}
bool Disposed;
@@ -48,13 +47,20 @@ public class MediaInfo : IDisposable
{
if (!Disposed)
{
Disposed = true;
if (Handle != IntPtr.Zero)
{
MediaInfo_Close(Handle);
MediaInfo_Delete(Handle);
}
Disposed = true;
}
}
~MediaInfo() { Dispose(); }
~MediaInfo()
{
Dispose();
}
[DllImport("MediaInfo.dll")]
static extern IntPtr MediaInfo_New();
@@ -63,7 +69,7 @@ public class MediaInfo : IDisposable
static extern int MediaInfo_Open(IntPtr handle, string path);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern IntPtr MediaInfo_Option(IntPtr handle, string optionString, string value);
static extern IntPtr MediaInfo_Option(IntPtr handle, string option, string value);
[DllImport("MediaInfo.dll")]
static extern IntPtr MediaInfo_Inform(IntPtr handle, int reserved);
@@ -75,17 +81,11 @@ public class MediaInfo : IDisposable
static extern void MediaInfo_Delete(IntPtr handle);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern IntPtr MediaInfo_Get(IntPtr handle,
MediaInfoStreamKind streamKind,
int streamNumber,
string parameter,
MediaInfoInfoKind kindOfInfo,
MediaInfoInfoKind kindOfSearch);
static extern IntPtr MediaInfo_Get(IntPtr handle, MediaInfoStreamKind kind,
int stream, string parameter, MediaInfoKind infoKind, MediaInfoKind searchKind);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern int MediaInfo_Count_Get(IntPtr handle,
MediaInfoStreamKind streamKind,
int streamNumber);
static extern int MediaInfo_Count_Get(IntPtr handle, MediaInfoStreamKind streamKind, int stream);
}
public enum MediaInfoStreamKind
@@ -100,7 +100,7 @@ public enum MediaInfoStreamKind
Max,
}
public enum MediaInfoInfoKind
public enum MediaInfoKind
{
Name,
Text,

View File

@@ -1,8 +1,9 @@
using System;

using System;
using System.Drawing;
using System.Runtime.InteropServices;
public class Native
public class WinAPI
{
[DllImport("kernel32.dll")]
public static extern bool AttachConsole(int dwProcessId);
@@ -38,13 +39,13 @@ public class Native
public static extern bool AdjustWindowRect(ref RECT lpRect, uint dwStyle, bool bMenu);
[DllImport("user32.dll")]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, int uFlags);
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
private static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);
static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);
public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex)
{
@@ -54,6 +55,9 @@ public class Native
return GetWindowLong32(hWnd, nIndex);
}
[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{

View File

@@ -1,4 +1,5 @@
using System;

using System;
namespace mpvnet
{
@@ -20,9 +21,9 @@ namespace mpvnet
}
}
public static void SubtractWindowBorders(IntPtr hwnd, ref Native.RECT rc)
public static void SubtractWindowBorders(IntPtr hwnd, ref WinAPI.RECT rc)
{
var b = new Native.RECT(0, 0, 0, 0);
var b = new WinAPI.RECT(0, 0, 0, 0);
AddWindowBorders(hwnd, ref b);
rc.Left -= b.Left;
rc.Top -= b.Top;
@@ -30,9 +31,9 @@ namespace mpvnet
rc.Bottom -= b.Bottom;
}
public static void AddWindowBorders(IntPtr hwnd, ref Native.RECT rc)
public static void AddWindowBorders(IntPtr hwnd, ref WinAPI.RECT rc)
{
Native.AdjustWindowRect(ref rc, (uint)Native.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
WinAPI.AdjustWindowRect(ref rc, (uint)WinAPI.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
}
}
}

View File

@@ -12,7 +12,7 @@ using System.Windows.Forms;
public class Msg
{
private static string ShownMessages;
static string ShownMessages;
public static string SupportURL { get; set; }
@@ -67,7 +67,7 @@ public class Msg
td.MainInstruction = exception.GetType().Name;
td.Content = exception.Message;
td.MainIcon = MsgIcon.Error;
td.ExpandedInformation = exception.ToString();
td.ExpandedInformation = exception.StackTrace;
td.Footer = "[Copy Message](copymsg)";
if (!string.IsNullOrEmpty(Msg.SupportURL))
@@ -78,8 +78,7 @@ public class Msg
}
catch (Exception e)
{
MessageBox.Show(e.GetType().Name + "\n\n" + e.Message + "\n\n" + e,
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
MessageBox.Show(e.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@@ -96,20 +95,20 @@ public class Msg
Msg.ShownMessages += mainInstruction + content;
}
public static MsgResult ShowQuestion(string mainInstruction,
MsgButtons buttons = MsgButtons.OkCancel)
public static MsgResult ShowQuestion(
string mainInstruction, MsgButtons buttons = MsgButtons.OkCancel)
{
return Msg.Show(mainInstruction, null, MsgIcon.None, buttons, MsgResult.None);
}
public static MsgResult ShowQuestion(string mainInstruction,
string content,
MsgButtons buttons = MsgButtons.OkCancel)
public static MsgResult ShowQuestion(
string mainInstruction, string content, MsgButtons buttons = MsgButtons.OkCancel)
{
return Msg.Show(mainInstruction, content, MsgIcon.None, buttons, MsgResult.None);
}
public static MsgResult Show(string mainInstruction,
public static MsgResult Show(
string mainInstruction,
string content,
MsgIcon icon,
MsgButtons buttons,
@@ -155,14 +154,20 @@ public class Msg
public class TaskDialog<T> : TaskDialogNative, IDisposable
{
private Dictionary<int, T> IdValueDic;
private Dictionary<int, string> IdTextDic;
private List<int> CommandLinkShieldList;
private IntPtr ButtonArray;
private IntPtr RadioButtonArray;
private List<TaskDialogNative.TASKDIALOG_BUTTON> Buttons;
private List<TaskDialogNative.TASKDIALOG_BUTTON> RadioButtons;
private TaskDialogNative.TASKDIALOGCONFIG Config;
Dictionary<int, T> IdValueDic;
Dictionary<int, string> IdTextDic;
List<int> CommandLinkShieldList;
IntPtr ButtonArray;
IntPtr RadioButtonArray;
T SelectedValueValue;
string SelectedTextValue;
int TimeoutValue;
int ExitTickCount;
bool Disposed;
List<TaskDialogNative.TASKDIALOG_BUTTON> Buttons;
List<TaskDialogNative.TASKDIALOG_BUTTON> RadioButtons;
TaskDialogNative.TASKDIALOGCONFIG Config;
const int TDE_CONTENT = 0;
const int TDE_EXPANDED_INFORMATION = 1;
const int TDE_FOOTER = 2;
@@ -193,11 +198,6 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
const int TDM_UPDATE_ELEMENT_TEXT = 1138;
const int TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE = 1139;
const int TDM_UPDATE_ICON = 1140;
private T SelectedValueValue;
private string SelectedTextValue;
private int TimeoutValue;
private int ExitTickCount;
private bool disposed;
public TaskDialog()
{
@@ -285,13 +285,14 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
set => Config.MainIcon = new TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION((int)value);
}
private int _SelectedID;
int _SelectedID;
public int SelectedID {
get => _SelectedID;
set {
foreach (var i in IdValueDic)
if (i.Key == value) _SelectedID = value;
if (i.Key == value)
_SelectedID = value;
}
}
@@ -299,6 +300,7 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
get {
if (IdValueDic.ContainsKey(SelectedID))
return IdValueDic[SelectedID];
return SelectedValueValue;
}
set => SelectedValueValue = value;
@@ -308,13 +310,15 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
get {
if (IdTextDic.ContainsKey(SelectedID))
return IdTextDic[SelectedID];
return SelectedTextValue;
}
set => SelectedTextValue = value;
}
public bool CheckBoxChecked {
get => (Config.dwFlags & TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED) == TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED;
get => (Config.dwFlags & TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED)
== TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED;
set {
if (value)
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED;
@@ -355,10 +359,11 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
value = regex.Replace(value, "<a href=\"$2\">$1</a>");
}
}
return value;
}
public void AddCommandLink(string text, T value)
public void AddCommand(string text, T value)
{
int n = 1000 + IdValueDic.Count + 1;
IdValueDic[n] = value;
@@ -367,12 +372,17 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS;
}
public void AddCommandLink(string text, string description, T value, bool setShield = false)
public void AddCommand(string text, string description, T value, bool setShield = false)
{
int n = 1000 + IdValueDic.Count + 1;
IdValueDic[n] = value;
if (setShield) CommandLinkShieldList.Add(n);
if (!string.IsNullOrEmpty(description)) text += "\n" + description;
if (setShield)
CommandLinkShieldList.Add(n);
if (!string.IsNullOrEmpty(description))
text += "\n" + description;
Buttons.Add(new TaskDialogNative.TASKDIALOG_BUTTON(n, text));
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS;
}
@@ -388,10 +398,17 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
{
MarshalDialogControlStructs();
TaskDialogNative.TASKDIALOGCONFIG config = Config;
int errorCode = TaskDialogNative.TaskDialogIndirect(config, out int dummy1, out int dummy2, out bool isChecked);
if (errorCode < 0) Marshal.ThrowExceptionForHR(errorCode);
int hr = TaskDialogNative.TaskDialogIndirect(
config, out int dummy1, out int dummy2, out bool isChecked);
if (hr < 0)
Marshal.ThrowExceptionForHR(hr);
CheckBoxChecked = isChecked;
if (SelectedValue is MsgResult) SelectedValue = (T)(object)SelectedID;
if (SelectedValue is MsgResult)
SelectedValue = (T)(object)SelectedID;
return SelectedValue;
}
@@ -412,8 +429,10 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
break;
case 3: //TDN_HYPERLINK_CLICKED
string stringUni = Marshal.PtrToStringUni(lParam);
if (stringUni.StartsWith("mailto") || stringUni.StartsWith("http"))
Process.Start(stringUni);
if (stringUni == "copymsg")
{
Thread thread = new Thread((ThreadStart)(() => {
@@ -475,8 +494,10 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
protected void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
if (Disposed)
return;
Disposed = true;
if (ButtonArray != IntPtr.Zero)
{
@@ -493,36 +514,27 @@ public class TaskDialog<T> : TaskDialogNative, IDisposable
}
public delegate int PFTASKDIALOGCALLBACK(
IntPtr hwnd,
uint msg,
IntPtr wParam,
IntPtr lParam,
IntPtr lpRefData);
IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam, IntPtr lpRefData);
public class TaskDialogNative
{
[DllImport("comctl32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int TaskDialogIndirect(
[In] TaskDialogNative.TASKDIALOGCONFIG pTaskConfig,
TaskDialogNative.TASKDIALOGCONFIG pTaskConfig,
out int pnButton,
out int pnRadioButton,
[MarshalAs(UnmanagedType.Bool)] out bool pVerificationFlagChecked);
out bool pVerificationFlagChecked);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern uint GetWindowModuleFileName(
IntPtr hwnd,
StringBuilder lpszFileName,
uint cchFileNameMax);
IntPtr hwnd, StringBuilder lpszFileName, uint cchFileNameMax);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(
IntPtr handle,
int message,
IntPtr wParam,
IntPtr lParam);
IntPtr handle, int message, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
public class TASKDIALOGCONFIG
@@ -532,12 +544,9 @@ public class TaskDialogNative
public IntPtr hInstance;
public TaskDialogNative.TASKDIALOG_FLAGS dwFlags;
public MsgButtons dwCommonButtons;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszWindowTitle;
public TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION MainIcon;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszMainInstruction;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszContent;
public uint cButtons;
public IntPtr pButtons;
@@ -545,16 +554,11 @@ public class TaskDialogNative
public uint cRadioButtons;
public IntPtr pRadioButtons;
public int nDefaultRadioButton;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszVerificationText;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszExpandedInformation;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszExpandedControlText;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszCollapsedControlText;
public TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION FooterIcon;
[MarshalAs(UnmanagedType.LPWStr)]
public string pszFooter;
public PFTASKDIALOGCALLBACK pfCallback;
public IntPtr lpCallbackData;

View File

@@ -1,18 +1,18 @@
using System;

using System;
using System.Runtime.InteropServices;
public class Taskbar
{
private ITaskbarList3 Instance = (ITaskbarList3)new TaskBarCommunication();
public IntPtr Handle { get; set; }
public Taskbar(IntPtr handle) => Handle = handle;
[ComImportAttribute]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
private interface ITaskbarList3
ITaskbarList3 Instance = (ITaskbarList3)new TaskBarCommunication();
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF")]
interface ITaskbarList3
{
// ITaskbarList
[PreserveSig] void HrInit();
@@ -27,10 +27,12 @@ public class Taskbar
[PreserveSig] void SetProgressState(IntPtr hwnd, TaskbarStates state);
}
[ComImportAttribute]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")]
private class TaskBarCommunication { }
[ComImport]
[ClassInterface(ClassInterfaceType.None)]
[Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
class TaskBarCommunication
{
}
public void SetState(TaskbarStates taskbarState)
{

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Frank Skare (stax76)")]
[assembly: AssemblyProduct("mpv.net")]
[assembly: AssemblyCopyright("Copyright (c) 2017-2019 Frank Skare (stax76)")]
[assembly: AssemblyCopyright("Copyright (C) 2017-2020 Frank Skare (stax76)")]
[assembly: AssemblyTrademark("")]
[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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("5.4.4.0")]
[assembly: AssemblyFileVersion("5.4.4.0")]
[assembly: AssemblyVersion("5.4.8.0")]
[assembly: AssemblyFileVersion("5.4.8.0")]

View File

@@ -61,7 +61,8 @@ namespace mpvnet.Properties {
}
/// <summary>
/// Looks up a localized string similar to [[settings]]
/// Looks up a localized string similar to
///[[settings]]
///name = &quot;hwdec&quot;
///file = &quot;mpv&quot;
///default = &quot;no&quot;
@@ -69,18 +70,18 @@ namespace mpvnet.Properties {
///url = &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;, hel [rest of string was truncated]&quot;;.
/// { name = &quot;auto&quot;, h [rest of string was truncated]&quot;;.
/// </summary>
internal static string confToml {
internal static string editor_toml {
get {
return ResourceManager.GetString("confToml", resourceCulture);
return ResourceManager.GetString("editor_toml", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to
/// # This file defines the key and mouse bindings and the context menu
/// # of mpv.net. A input and config editor can be found in mpv.net&apos;s
/// # of mpv.net. A input and config editor can be found in mpv.nets
/// # context menu under &apos;Settings&apos;. The defaults of this file can be found at:
///
/// # https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt
@@ -90,34 +91,34 @@ namespace mpvnet.Properties {
/// # https://github.com/mpv-player/mpv/blob/master/etc/input.conf
///
/// # mpv.net&apos;s defaults of mpv.conf contain: &apos;input-default-bindings = no&apos;
/// # which disables mpv&apos;s [rest of string was truncated]&quot;;.
/// # which disables mpv&apos;s i [rest of string was truncated]&quot;;.
/// </summary>
internal static string inputConf {
internal static string input_conf {
get {
return ResourceManager.GetString("inputConf", resourceCulture);
return ResourceManager.GetString("input_conf", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to input-default-bindings = no
/// Looks up a localized string similar to
///input-default-bindings = no
///input-ar-delay = 500
///input-ar-rate = 20
///keep-open = yes
///keep-open-pause = no
///osd-duration = 2000
///osd-playing-msg = &apos;${filename}&apos;
///script-opts = osc-scalewindowed=1.5
///script-opts = osc-scalewindowed=1.5,osc-hidetimeout=2000,console-scale=1
///screenshot-directory = &apos;~~desktop/&apos;
///cscale = spline36
///dscale = spline36
///scale = spline36
///hwdec = yes
///
///[protocol.https]
///osd-playing-msg = &apos;${media-title}&apos;.
///osd-playing-msg = &apos;${media-title}&apos;
///.
/// </summary>
internal static string mpvConf {
internal static string mpv_conf {
get {
return ResourceManager.GetString("mpvConf", resourceCulture);
return ResourceManager.GetString("mpv_conf", resourceCulture);
}
}
@@ -135,17 +136,18 @@ namespace mpvnet.Properties {
/// Looks up a localized string similar to
///[dark]
///
///heading = #1E90FF
///foreground = #FFFFFF
///foreground2 = #C0C0C0
///heading = #3C8CC8
///foreground = #DDDDDD
///foreground2 = #AAAAAA
///background = #323232
///
///menu-foreground = #FFFFFF
///menu-foreground = #DDDDDD
///menu-background = #323232
///menu-highlight = #505050
///menu-border = #FFFFFF
///menu-checked = #5A5A5A
///
///
///[light]
///
///heading = #0068B2

View File

@@ -118,18 +118,18 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="confToml" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ConfToml.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
<data name="editor_toml" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\editor.toml.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<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>
</data>
<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>
<data name="input_conf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\input.conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
</data>
<data name="mpvnet" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\mpvnet.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="mpv_conf" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\mpv.conf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>
<data name="theme" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\theme.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
</data>

View File

@@ -152,6 +152,25 @@ help = "Enable the debanding algorithm. This greatly reduces the amount of visib
options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "d3d11va-zero-copy"
file = "mpv"
default = "no"
filter = "Video"
help = "By default, when using hardware decoding with --gpu-api=d3d11, the video image will be copied (GPU-to-GPU) from the decoder surface to a shader resource. Set this option to avoid that copy by sampling directly from the decoder image. This may increase performance and reduce power usage, but can cause the image to be sampled incorrectly on the bottom and right edges due to padding, and may invoke driver bugs, since Direct3D 11 technically does not allow sampling from a decoder surface (though most drivers support it.)"
options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "hdr-compute-peak"
file = "mpv"
default = "auto"
filter = "Video"
help = "Compute the HDR peak and frame average brightness per-frame instead of relying on tagged metadata. These values are averaged over local regions as well as over several frames to prevent the value from jittering around too much. This option basically gives you dynamic, per-scene tone mapping. Requires compute shaders, which is a fairly recent OpenGL feature, and will probably also perform horribly on some drivers, so enable at your own risk. The special value auto (default) will enable HDR peak computation automatically if compute shaders and SSBOs are supported."
options = [{ name = "auto" },
{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "volume"
file = "mpv"
@@ -276,6 +295,12 @@ file = "mpv"
filter = "Screen"
help = "Specify the OSD font size. See sub-font-size for details. Default: 55"
[[settings]]
name = "osd-duration"
file = "mpv"
filter = "Screen"
help = "Set the duration of the OSD messages in ms. Default: 1000"
[[settings]]
name = "osd-scale-by-window"
file = "mpv"
@@ -289,28 +314,29 @@ options = [{ name = "yes" },
name = "autofit"
file = "mpv"
filter = "Screen"
help = "<int> Initial window height in percent. Default: 50"
help = "<int> Initial window height in percent. Default: 60"
[[settings]]
name = "autofit-smaller"
file = "mpv"
filter = "Screen"
help = "<int> Minimum window height in percent. Default: 40"
help = "<int> Minimum window height in percent. Default: 30"
[[settings]]
name = "autofit-larger"
file = "mpv"
filter = "Screen"
help = "<int> Maximum window height in percent. Default: 75"
help = "<int> Maximum window height in percent. Default: 80"
[[settings]]
name = "start-size"
file = "mpvnet"
default = "previous"
filter = "Screen"
help = "Setting to remember the window height in the current session, otherwise the video's native resolution is used. (mpv.net specific setting)"
options = [{ name = "video", help = "Window size is set to native video resolution" },
{ name = "previous", help = "Window size is remembered but only from the current session" }]
help = "Setting to remember the window height. (mpv.net specific setting)"
options = [{ name = "video", help = "Window size is set to video resolution" },
{ name = "previous", help = "Window size is remembered but only in the current session" },
{ name = "always", help = "Window size is always remembered"}]
[[settings]]
name = "start-threshold"
@@ -322,7 +348,7 @@ help = "Threshold in milliseconds to wait for libmpv returning the video resolut
name = "minimum-aspect-ratio"
file = "mpvnet"
filter = "Screen"
help = "<float> Minimum aspect ratio for the window. Default: 1.3 (mpv.net specific setting)"
help = "<float> Minimum aspect ratio, if the AR is smaller than the defined value then the window AR is set to 16/9. This avoids a square window for Music with cover art. Default: 1.2 (mpv.net specific setting)"
[[settings]]
name = "remember-position"
@@ -334,11 +360,11 @@ options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "maximized"
file = "mpvnet"
name = "window-maximized"
file = "mpv"
default = "no"
filter = "Screen"
help = "Start with a maximized window. (mpv.net specific setting)"
help = "Start with a maximized window."
options = [{ name = "yes" },
{ name = "no" }]

View File

@@ -1,24 +1,31 @@
# This file defines the key and mouse bindings and the context menu
# of mpv.net. A input and config editor can be found in mpv.net's
# context menu under 'Settings'. The defaults of this file can be found at:
# This file defines the key and mouse bindings and the context menu of mpv.net.
# https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt
# A convenient input and config editor can be found in the context menu of mpv.net
# under 'Settings'.
# The defaults of mpv can be found at:
# The mpv.conf defaults of mpv.net contain: 'input-default-bindings = no'
# which disables the input defaults of mpv. 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 do a text search on ' o '.
# input test mode:
# mpvnet --input-test
# input key list:
# mpv --input-keylist
# mpv.net input.conf defaults:
# https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt
# mpv input.conf defaults:
# https://github.com/mpv-player/mpv/blob/master/etc/input.conf
# 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://mpv.io/manual/master/#list-of-input-commands
# mpv input commands: https://mpv.io/manual/master/#list-of-input-commands
# mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys
# run mpv.net in input test mode with: mpvnet --input-test
# mpv input options:
# https://mpv.io/manual/master/#input
o script-message mpv.net open-files #menu: Open > Open Files...
u script-message mpv.net open-url #menu: Open > Open URL or file path from clipboard
@@ -41,7 +48,7 @@
F11 playlist-prev #menu: Navigate > Previous File
F12 playlist-next #menu: Navigate > Next File
_ ignore #menu: Navigate > -
Home set playlist-pos 0 #menu: Navigate > First File
Home script-message mpv.net playlist-first #menu: Navigate > First File
End script-message mpv.net playlist-last #menu: Navigate > Last File
_ ignore #menu: Navigate > -
PGUP add chapter 1 #menu: Navigate > Next Chapter
@@ -50,14 +57,14 @@
. frame-step #menu: Navigate > Jump Next Frame
, frame-back-step #menu: Navigate > Jump Previous Frame
_ ignore #menu: Navigate > -
Right no-osd seek 7 #menu: Navigate > Jump 7 sec forward
Left no-osd seek -7 #menu: Navigate > Jump 7 sec backward
Right seek 7 #menu: Navigate > Jump 7 sec forward
Left seek -7 #menu: Navigate > Jump 7 sec backward
_ ignore #menu: Navigate > -
Up no-osd seek 40 #menu: Navigate > Jump 40 sec forward
Down no-osd seek -40 #menu: Navigate > Jump 40 sec backward
Up seek 40 #menu: Navigate > Jump 40 sec forward
Down seek -40 #menu: Navigate > Jump 40 sec backward
_ ignore #menu: Navigate > -
Ctrl+Right no-osd seek 300 #menu: Navigate > Jump 5 min forward
Ctrl+Left no-osd seek -300 #menu: Navigate > Jump 5 min backward
Ctrl+Right seek 300 #menu: Navigate > Jump 5 min forward
Ctrl+Left seek -300 #menu: Navigate > Jump 5 min backward
_ ignore #menu: Navigate > -
_ ignore #menu: Navigate > Chapters
@@ -143,6 +150,11 @@
Del script-binding osc/visibility #menu: View > Toggle OSC Visibility
F8 script-binding show-playlist #menu: View > Show Playlist
F9 show-text ${track-list} 5000 #menu: View > Show Audio/Video/Subtitle List
p show-progress #menu: View > Show Progress
Ctrl+p script-message mpv.net show-profiles #menu: View > Show Profiles
Shift+p script-message mpv.net show-properties #menu: View > Show Properties
Shift+c script-message mpv.net show-commands #menu: View > Show Commands
` script-binding console/enable #menu: View > Show Console
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
@@ -150,12 +162,11 @@
F1 script-message mpv.net show-command-palette #menu: Tools > Show All Commands
h script-message mpv.net show-history #menu: Tools > Show History
Ctrl+r script-message-to repl type "" #menu: Tools > Show REPL
l ab-loop #menu: Tools > Set/clear A-B loop points
L cycle-values loop-file "inf" "no" #menu: Tools > Toggle infinite file looping
_ cycle shuffle #menu: Tools > Toggle Shuffle
Ctrl+h cycle-values hwdec "auto" "no" #menu: Tools > Toggle Hardware Decoding
_ script-message mpv.net show-setup-dialog #menu: Tools > OS Setup...
_ script-message mpv.net show-setup-dialog #menu: Tools > Setup...
_ script-message mpv.net shell-execute https://mpv.io #menu: Help > Website mpv
_ script-message mpv.net shell-execute https://github.com/stax76/mpv.net #menu: Help > Website mpv.net
@@ -178,11 +189,10 @@
Stop stop
Forward seek 60
Rewind seek -60
Mute cycle mute
Volume_Up add volume 10
Volume_Down add volume -10
Wheel_Up add volume 10
Wheel_Down add volume -10
Wheel_Left add volume -10
Wheel_Right add volume 10
Prev playlist-prev
Next playlist-next
MBTN_Forward playlist-next
@@ -193,3 +203,4 @@
Ctrl+Wheel_Down no-osd seek -7
MBTN_Left_DBL cycle fullscreen
KP_Enter cycle fullscreen

View File

@@ -1,14 +1,13 @@
input-default-bindings = no

input-default-bindings = no
input-ar-delay = 500
input-ar-rate = 20
keep-open = yes
keep-open-pause = no
osd-duration = 2000
osd-playing-msg = '${filename}'
script-opts = osc-scalewindowed=1.5
script-opts = osc-scalewindowed=1.5,osc-hidetimeout=2000,console-scale=1
screenshot-directory = '~~desktop/'
cscale = spline36
dscale = spline36
scale = spline36
hwdec = yes
[protocol.https]

View File

@@ -1,7 +1,7 @@
[dark]
heading = #1E90FF
heading = #3C8CC8
foreground = #DDDDDD
foreground2 = #AAAAAA
background = #323232

View File

@@ -1,129 +0,0 @@
using System;
using System.IO;
using System.Threading;
using System.Management.Automation.Runspaces;
using System.Reflection;
using System.Management.Automation;
namespace mpvnet
{
public class PowerShellScript
{
public static void Execute(string filepath, params string[] parameters)
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
runspace.ApartmentState = ApartmentState.STA;
runspace.Open();
using (Pipeline pipeline = runspace.CreatePipeline())
{
pipeline.Commands.AddScript(
"Using namespace mpvnet\n" +
"Using namespace System\n" +
"[System.Reflection.Assembly]::LoadWithPartialName(\"mpvnet\")\n");
pipeline.Commands.AddScript(File.ReadAllText(filepath));
if (parameters != null)
foreach (string i in parameters)
pipeline.Commands[1].Parameters.Add(null, i);
PowerShellOutput output = new PowerShellOutput();
output.ModuleName = Path.GetFileName(filepath);
pipeline.Output.DataReady += output.Output_DataReady;
pipeline.Error.DataReady += output.Error_DataReady;
runspace.SessionStateProxy.SetVariable("Output", output);
try {
pipeline.Invoke();
}
catch (RuntimeException e) {
Msg.ShowError("PowerShell Exception", e.Message + "\n\n" +
e.ErrorRecord.ScriptStackTrace.Replace(" <ScriptBlock>, <No file>", "") +
"\n\n" + Path.GetFileName(filepath));
}
catch (Exception e) {
Msg.ShowException(e);
}
pipeline.Output.DataReady -= output.Output_DataReady;
pipeline.Error.DataReady -= output.Error_DataReady;
}
}
}
public static void Init(string filepath)
{
foreach (var eventInfo in typeof(mp).GetEvents())
{
if (eventInfo.Name.ToLower() ==
Path.GetFileNameWithoutExtension(filepath).ToLower().Replace("-", ""))
{
PowerShellEventObject eventObject = new PowerShellEventObject();
MethodInfo mi;
eventObject.Filepath = filepath;
if (eventInfo.EventHandlerType == typeof(Action))
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.Invoke));
else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>))
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeEndFile));
else if (eventInfo.EventHandlerType == typeof(Action<string[]>))
mi = eventObject.GetType().GetMethod(nameof(PowerShellEventObject.InvokeStrings));
else
throw new Exception();
eventObject.EventInfo = eventInfo;
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventObject, mi);
eventObject.Delegate = handler;
eventInfo.AddEventHandler(eventObject, handler);
return;
}
}
Execute(filepath);
}
class PowerShellOutput
{
public string ModuleName { get; set; }
public bool WriteStandard { get; set; } = true;
public bool WriteError { get; set; } = true;
public void Output_DataReady(object sender, EventArgs e)
{
if (!WriteStandard)
return;
var output = sender as PipelineReader<PSObject>;
while (output.Count > 0)
Console.WriteLine("[" + ModuleName + "] " + output.Read().ToString());
}
public void Error_DataReady(object sender, EventArgs e)
{
if (!WriteError)
return;
var output = sender as PipelineReader<Object>;
while (output.Count > 0)
ConsoleHelp.WriteError("[" + ModuleName + "] " + output.Read().ToString());
}
}
}
public class PowerShellEventObject
{
public EventInfo EventInfo { get; set; }
public Delegate Delegate { get; set; }
public string Filepath { get; set; }
public void Invoke() => PowerShellScript.Execute(Filepath);
public void InvokeEndFile(EndFileEventMode arg) => PowerShellScript.Execute(Filepath, arg.ToString());
public void InvokeStrings(string[] args) => PowerShellScript.Execute(Filepath, args);
}
}

View File

@@ -1,60 +0,0 @@
using System;
using System.IO;
using System.Reflection;
using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
namespace mpvnet
{
public class PythonScript
{
ScriptEngine engine;
ScriptScope scope;
public PythonScript(string scriptPath)
{
try
{
engine = Python.CreateEngine();
scope = engine.CreateScope();
scope.ImportModule("clr");
engine.Execute("import clr", scope);
engine.Execute("clr.AddReference(\"mpvnet\")", scope);
engine.Execute("import mpvnet", scope);
engine.Execute("from mpvnet import *", scope);
engine.Execute(File.ReadAllText(scriptPath), scope);
}
catch (Exception ex)
{
if (ex is SyntaxErrorException e)
Msg.ShowError(e.GetType().Name,$"{e.Line}, {e.Column}: " + e.Message + "\n\n" + Path.GetFileName(scriptPath));
else
Msg.ShowError(ex.GetType().Name, ex.Message + "\n\n" + Path.GetFileName(scriptPath));
}
}
}
public class PythonEventObject
{
public PythonFunction PythonFunction { get; set; }
public EventInfo EventInfo { get; set; }
public Delegate Delegate { get; set; }
public void Invoke() => PythonCalls.Call(PythonFunction);
public void InvokeEndFileEventMode(EndFileEventMode arg)
{
PythonCalls.Call(PythonFunction, new[] { arg });
}
public void InvokeStrings(string[] arg)
{
PythonCalls.Call(PythonFunction, new[] { arg });
}
}
}

View File

@@ -1,23 +1,67 @@
$ErrorActionPreference = 'Stop'
$targetDir = (Split-Path $args[1]) + "\new version"
if ($args.Length -ne 2) {
Write-Host 'Invalid arguments' -ForegroundColor Red
exit
}
if (-not (Get-Command curl.exe)) {
Write-Host 'Error using curl.exe' -ForegroundColor Red
exit
}
if ($PSVersionTable.PSVersion.Major -lt 5) {
Write-Host 'PowerShell 5.1 not found' -ForegroundColor Red
exit
}
Add-Type -AssemblyName Microsoft.VisualBasic
# Delete directory using recycle bin
function Remove-Directory($directory) {
if (Test-Path $directory) {
[Microsoft.VisualBasic.FileIO.FileSystem]::
DeleteDirectory($directory, 'OnlyErrorDialogs', 'SendToRecycleBin')
}
}
$currentDir = $args[1];
$targetDir = (Split-Path $currentDir) + "\new version"
Remove-Directory $targetDir
$targetFile = $targetDir + '.7z'
Remove-Item $targetFile -Force -ErrorAction Ignore
Write-Host 'Download new version' -ForegroundColor Green
curl.exe $args[0] --location --output $targetFile
if ($LastExitCode) { throw $LastExitCode }
Write-Host 'Unpack new version' -ForegroundColor Green
& ($args[1] + '\7z\7za.exe') x -y $targetFile -o"$targetDir"
& ($currentDir + '\7z\7za.exe') x -y $targetFile -o"$targetDir"
if ($LastExitCode) { throw $LastExitCode }
Write-Host 'Delete downloaded file' -ForegroundColor Green
Remove-Item $targetFile -Force -Recurse
$portableConfigDir = $currentDir + '\portable_config'
$portableConfigTempDir = (Split-Path $currentDir) + '\portable config dir temp'
Remove-Item $portableConfigTempDir -Force -ErrorAction Ignore
if (Test-Path $portableConfigDir) {
Write-Host 'Backup portable config' -ForegroundColor Green
Copy-Item $portableConfigDir $portableConfigTempDir -Recurse -Force
}
Write-Host 'Delete current version' -ForegroundColor Green
Remove-Item $args[1] -Force -Recurse
Remove-Directory $currentDir
Write-Host 'Rename directory' -ForegroundColor Green
Rename-Item $targetDir (Split-Path $args[1] -Leaf)
Rename-Item $targetDir (Split-Path $currentDir -Leaf)
if (Test-Path $portableConfigTempDir) {
Write-Host 'Restore portable config' -ForegroundColor Green
Move-Item $portableConfigTempDir $portableConfigDir -Force
}
Write-Host 'Update is complete' -ForegroundColor Green

View File

@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="About mpv.net"
@@ -13,13 +13,13 @@
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}">
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock FontSize="48" HorizontalAlignment="Center" Margin="0,0,0,10">mpv.net</TextBlock>
<TextBlock HorizontalAlignment="Center">Copyright (c) 2017-2019 Frank Skare (stax76)</TextBlock>
<TextBlock HorizontalAlignment="Center">Copyright (C) 2017-2020 Frank Skare (stax76)</TextBlock>
<TextBlock Name="Version" HorizontalAlignment="Center" />
<TextBlock Name="mpvVersion" HorizontalAlignment="Center" />
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,20">MIT License</TextBlock>

View File

@@ -1,7 +1,10 @@
using System.IO;

using System.IO;
using System.Windows;
using System.Windows.Input;
using static mpvnet.Core;
namespace mpvnet
{
public partial class AboutWindow : Window
@@ -10,7 +13,7 @@ namespace mpvnet
{
InitializeComponent();
Version.Text = $"mpv.net Version {System.Windows.Forms.Application.ProductVersion} ({File.GetLastWriteTime(System.Windows.Forms.Application.ExecutablePath).ToShortDateString()})";
mpvVersion.Text = $"{mp.get_property_string("mpv-version")} ({File.GetLastWriteTime(Folder.Startup + "mpv-1.dll").ToShortDateString()})";
mpvVersion.Text = $"{core.get_property_string("mpv-version")} ({File.GetLastWriteTime(Folder.Startup + "mpv-1.dll").ToShortDateString()})";
}
protected override void OnPreviewKeyDown(KeyEventArgs e) => Close();

View File

@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="Command Palette"
@@ -21,15 +21,15 @@
</Grid.RowDefinitions>
<TextBox Name="FilterTextBox"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
PreviewKeyDown="FilterTextBox_PreviewKeyDown"
TextChanged="FilterTextBox_TextChanged"/>
<ListView Name="ListView"
Grid.Row="1"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
MouseUp="ListView_MouseUp">
<ListView.ItemContainerStyle>

View File

@@ -1,13 +1,12 @@
using Microsoft.Win32;

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using static mpvnet.Core;
namespace mpvnet
{
@@ -29,16 +28,22 @@ namespace mpvnet
{
if (item.Command == "" || item.Path == "")
return false;
string filter = FilterTextBox.Text.ToLower();
if (filter == "") return true;
if (filter == "")
return true;
if (item.Command.ToLower().Contains(filter) ||
item.Input.ToLower().Contains(filter) ||
item.Path.ToLower().Contains(filter))
return true;
return false;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
@@ -52,14 +57,15 @@ namespace mpvnet
ListView.SelectedIndex = 0;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x200 /*WM_MOUSEMOVE*/ && Mouse.LeftButton != MouseButtonState.Pressed)
handled = true;
return IntPtr.Zero;
}
private void FilterTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
void FilterTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
@@ -67,7 +73,10 @@ namespace mpvnet
{
int index = ListView.SelectedIndex;
index -= 1;
if (index < 0) index = 0;
if (index < 0)
index = 0;
ListView.SelectedIndex = index;
ListView.ScrollIntoView(ListView.SelectedItem);
}
@@ -76,7 +85,10 @@ namespace mpvnet
{
int index = ListView.SelectedIndex;
index += 1;
if (index > ListView.Items.Count - 1) index = ListView.Items.Count - 1;
if (index > ListView.Items.Count - 1)
index = ListView.Items.Count - 1;
ListView.SelectedIndex = index;
ListView.ScrollIntoView(ListView.SelectedItem);
}
@@ -96,16 +108,16 @@ namespace mpvnet
{
CommandItem item = ListView.SelectedItem as CommandItem;
Close();
mp.command(item.Command);
core.command(item.Command);
}
}
private void ListView_MouseUp(object sender, MouseButtonEventArgs e)
void ListView_MouseUp(object sender, MouseButtonEventArgs e)
{
Execute();
}
private void FilterTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
void FilterTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
CollectionView.Refresh();
SelectFirst();

View File

@@ -3,14 +3,14 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="Config Editor"
Height="530"
Width="700"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
ShowInTaskbar="False"
WindowStartupLocation="CenterScreen"
Loaded="ConfWindow1_Loaded">
@@ -26,13 +26,14 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Controls:SearchTextBoxUserControl HintText="Find a setting"
<Controls:SearchTextBoxUserControl
x:Name="SearchControl"
HintText="Find a setting"
Width="250"
Margin="0,20,0,0"
Grid.ColumnSpan="2"/>
<ScrollViewer x:Name="MainScrollViewer"
<ScrollViewer Name="MainScrollViewer"
VerticalScrollBarVisibility="Auto"
Grid.Row="1"
Grid.Column="2"
@@ -42,12 +43,12 @@
</ScrollViewer>
<StackPanel Margin="20,0,0,0" Grid.Row="1">
<ListBox x:Name="FilterListBox"
<ListBox Name="FilterListBox"
ItemsSource="{Binding FilterStrings}"
BorderThickness="0"
SelectionChanged="FilterListBox_SelectionChanged"
Foreground="{x:Static UI:Theme.Heading}"
Background="{x:Static UI:Theme.Background}">
Foreground="{x:Static mpvnet:Theme.Heading}"
Background="{x:Static mpvnet:Theme.Background}">
<ListBox.ItemTemplate>
<DataTemplate>
@@ -56,10 +57,10 @@
</ListBox.ItemTemplate>
</ListBox>
<TextBlock x:Name="OpenSettingsTextBlock" Margin="0,30,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static UI:Theme.Heading}" MouseUp="OpenSettingsTextBlock_MouseUp">Open config folder</TextBlock>
<TextBlock x:Name="PreviewTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static UI:Theme.Heading}" MouseUp="PreviewTextBlock_MouseUp">Preview mpv.conf</TextBlock>
<TextBlock x:Name="ShowManualTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static UI:Theme.Heading}" MouseUp="ShowManualTextBlock_MouseUp">Show mpv manual</TextBlock>
<TextBlock x:Name="SupportTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static UI:Theme.Heading}" MouseUp="SupportTextBlock_MouseUp">Show support forum</TextBlock>
<TextBlock Name="OpenSettingsTextBlock" Margin="0,30,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static mpvnet:Theme.Heading}" MouseUp="OpenSettingsTextBlock_MouseUp">Open config folder</TextBlock>
<TextBlock Name="PreviewTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static mpvnet:Theme.Heading}" MouseUp="PreviewTextBlock_MouseUp">Preview mpv.conf</TextBlock>
<TextBlock Name="ShowManualTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static mpvnet:Theme.Heading}" MouseUp="ShowManualTextBlock_MouseUp">Show mpv manual</TextBlock>
<TextBlock Name="SupportTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static mpvnet:Theme.Heading}" MouseUp="SupportTextBlock_MouseUp">Show support forum</TextBlock>
</StackPanel>
</Grid>
</Window>

View File

@@ -11,12 +11,13 @@ using System.Windows.Controls;
using System.Windows.Input;
using DynamicGUI;
using static mpvnet.Core;
namespace mpvnet
{
public partial class ConfWindow : Window
{
List<SettingBase> SettingsDefinitions = Settings.LoadSettings(Properties.Resources.confToml);
List<SettingBase> SettingsDefinitions = Settings.LoadSettings(Properties.Resources.editor_toml);
List<ConfItem> ConfItems = new List<ConfItem>();
public ObservableCollection<string> FilterStrings { get; } = new ObservableCollection<string>();
string InitialContent;
@@ -26,14 +27,15 @@ namespace mpvnet
InitializeComponent();
DataContext = this;
SearchControl.SearchTextBox.TextChanged += SearchTextBox_TextChanged;
LoadConf(mp.ConfPath);
LoadConf(core.ConfPath);
LoadConf(App.ConfPath);
LoadSettings();
InitialContent = GetCompareString();
SearchControl.Text = RegistryHelp.GetString(App.RegPath, "ConfigEditorSearch");
FilterListBox.SelectedItem = SearchControl.Text.TrimEnd(':');
}
private void LoadSettings()
void LoadSettings()
{
foreach (SettingBase setting in SettingsDefinitions)
{
@@ -73,7 +75,7 @@ namespace mpvnet
if (InitialContent == GetCompareString())
return;
File.WriteAllText(mp.ConfPath, GetContent("mpv"));
File.WriteAllText(core.ConfPath, GetContent("mpv"));
File.WriteAllText(App.ConfPath, GetContent("mpvnet"));
Msg.Show("Changes will be available on next mpv.net startup.");
}
@@ -247,7 +249,7 @@ namespace mpvnet
return "\r\n" + sb.ToString().Trim() + "\r\n";
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
string activeFilter = "";
@@ -275,7 +277,7 @@ namespace mpvnet
MainScrollViewer.ScrollToTop();
}
private void ConfWindow1_Loaded(object sender, RoutedEventArgs e)
void ConfWindow1_Loaded(object sender, RoutedEventArgs e)
{
SearchControl.SearchTextBox.SelectAll();
Keyboard.Focus(SearchControl.SearchTextBox);
@@ -284,27 +286,28 @@ namespace mpvnet
i.Update();
}
private void FilterListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
void FilterListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0) SearchControl.Text = e.AddedItems[0] + ":";
if (e.AddedItems.Count > 0)
SearchControl.Text = e.AddedItems[0] + ":";
}
private void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Process.Start(Path.GetDirectoryName(mp.ConfPath));
Process.Start(Path.GetDirectoryName(core.ConfPath));
}
private void PreviewTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
void PreviewTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Msg.Show("mpv.conf Preview", GetContent("mpv"));
}
private void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Process.Start("https://mpv.io/manual/master/");
}
private void SupportTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
void SupportTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Process.Start("https://github.com/stax76/mpv.net#Support");
}

View File

@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="Media File Search"
@@ -21,14 +21,14 @@
</Grid.RowDefinitions>
<TextBox Name="FilterTextBox"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
PreviewKeyDown="FilterTextBox_PreviewKeyDown"
TextChanged="FilterTextBox_TextChanged"/>
<ListView Name="ListView"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
Grid.Row="1"
MouseUp="ListView_MouseUp"
PreviewKeyDown="ListView_PreviewKeyDown">

View File

@@ -10,6 +10,8 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using static mpvnet.Core;
namespace mpvnet
{
public partial class EverythingWindow : Window
@@ -43,7 +45,7 @@ namespace mpvnet
[DllImport("Everything.dll")]
public static extern UInt32 Everything_GetNumResults();
private void Window_Loaded(object sender, RoutedEventArgs e)
void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
@@ -56,7 +58,7 @@ namespace mpvnet
ListView.SelectedIndex = 0;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == 0x200 /*WM_MOUSEMOVE*/ && Mouse.LeftButton != MouseButtonState.Pressed)
handled = true;
@@ -64,7 +66,7 @@ namespace mpvnet
return IntPtr.Zero;
}
private void FilterTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
void FilterTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
@@ -91,7 +93,7 @@ namespace mpvnet
}
}
private void ListView_PreviewKeyDown(object sender, KeyEventArgs e)
void ListView_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
Close();
@@ -103,14 +105,14 @@ namespace mpvnet
void Execute()
{
if (ListView.SelectedItem != null)
mp.Load(new[] { ListView.SelectedItem as string }, true, Keyboard.Modifiers == ModifierKeys.Control);
core.LoadFiles(new[] { ListView.SelectedItem as string }, true, Keyboard.Modifiers == ModifierKeys.Control);
Keyboard.Focus(FilterTextBox);
}
private void ListView_MouseUp(object sender, MouseButtonEventArgs e) => Execute();
void ListView_MouseUp(object sender, MouseButtonEventArgs e) => Execute();
private void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
void FilterTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
string searchtext = FilterTextBox.Text;
Task.Run(() => Search(searchtext));
@@ -137,12 +139,13 @@ namespace mpvnet
Everything_GetResultFullPathName(i, buf, bufsize);
string ext = buf.ToString().ShortExt();
if (App.AudioTypes.Contains(ext) || App.VideoTypes.Contains(ext) ||
App.ImageTypes.Contains(ext))
if (App.AudioTypes.Contains(ext) || App.VideoTypes.Contains(ext)
|| App.ImageTypes.Contains(ext))
items.Add(buf.ToString());
if (items.Count > 100) break;
if (items.Count > 100)
break;
}
Application.Current.Dispatcher.Invoke(() => {

View File

@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="Input Editor"
@@ -11,8 +11,8 @@
Width="750"
FontSize="13"
ShowInTaskbar="False"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
Loaded="Window_Loaded"
Closed="Window_Closed">

View File

@@ -8,6 +8,8 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using static mpvnet.Core;
namespace mpvnet
{
public partial class InputWindow : Window
@@ -28,7 +30,7 @@ namespace mpvnet
DataGrid.ItemsSource = CollectionView;
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
CollectionView.Refresh();
@@ -70,7 +72,7 @@ namespace mpvnet
return false;
}
private void ButtonClick(object sender, RoutedEventArgs e)
void ButtonClick(object sender, RoutedEventArgs e)
{
CommandItem item = ((Button)e.Source).DataContext as CommandItem;
if (item is null) return;
@@ -88,13 +90,13 @@ namespace mpvnet
items[i.Input] = i;
}
private void Window_Loaded(object sender, RoutedEventArgs e) => Keyboard.Focus(SearchControl.SearchTextBox);
void Window_Loaded(object sender, RoutedEventArgs e) => Keyboard.Focus(SearchControl.SearchTextBox);
string GetInputConfContent()
{
string text = null;
foreach (string line in Properties.Resources.inputConf.Split(new[] { "\r\n" }, StringSplitOptions.None))
foreach (string line in Properties.Resources.input_conf.Split(new[] { "\r\n" }, StringSplitOptions.None))
{
string test = line.Trim();
if (test == "" || test.StartsWith("#")) text += test + "\r\n";
@@ -120,14 +122,14 @@ namespace mpvnet
return text;
}
private void Window_Closed(object sender, EventArgs e)
void Window_Closed(object sender, EventArgs e)
{
if (InitialInputConfContent == GetInputConfContent()) return;
File.WriteAllText(mp.InputConfPath, GetInputConfContent());
File.WriteAllText(core.InputConfPath, GetInputConfContent());
Msg.Show("Changes will be available on next mpv.net startup.");
}
private void DataGrid_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
void DataGrid_PreviewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
DataGrid grid = (DataGrid)sender;

View File

@@ -3,7 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="Learn Input"
@@ -12,12 +12,11 @@
WindowStartupLocation="CenterOwner"
ResizeMode="NoResize"
Loaded="Window_Loaded"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
MouseWheel="Window_MouseWheel"
MouseUp="Window_MouseUp"
MouseDoubleClick="Window_MouseDoubleClick"
TextInput="Window_TextInput">
MouseDoubleClick="Window_MouseDoubleClick" PreviewKeyDown="Window_PreviewKeyDown">
<Grid>
<Grid.RowDefinitions>

View File

@@ -1,11 +1,12 @@

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interop;
using WinForms = System.Windows.Forms;
using System.Windows.Forms;
namespace mpvnet
{
@@ -13,13 +14,68 @@ namespace mpvnet
{
public CommandItem InputItem { get; set; }
string NewKey = "";
string KeyChar = "";
uint MAPVK_VK_TO_VSC = 0;
int VK_MENU = 0x12;
int VK_LMENU = 0xA4;
int VK_RMENU = 0xA5;
int VK_CONTROL = 0x11;
int VK_LCONTROL = 0xA2;
int VK_RCONTROL = 0xA3;
public LearnWindow() => InitializeComponent();
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpKeyState,
StringBuilder pwszBuff, int cchBuff, uint wFlags);
[DllImport("user32.dll")]
static extern bool GetKeyboardState(byte[] lpKeyState);
string ToUnicode(uint vk)
{
WinForms.Message m = new WinForms.Message();
byte[] keys = new byte[256];
if (!GetKeyboardState(keys))
return "";
if ((keys[VK_CONTROL] & 0x80) != 0 && (keys[VK_MENU] & 0x80) == 0)
keys[VK_LCONTROL] = keys[VK_RCONTROL] = keys[VK_CONTROL] = 0;
uint scanCode = MapVirtualKey(vk, MAPVK_VK_TO_VSC);
string ret = ToUnicode(vk, scanCode, keys);
if (ret.Length == 1 && (int)ret[0] < 32)
return "";
if (ret == "" && (keys[VK_MENU] & 0x80) != 0)
{
keys[VK_LMENU] = keys[VK_RMENU] = keys[VK_MENU] = 0;
ret = ToUnicode(vk, scanCode, keys);
}
return ret;
}
public string ToUnicode(uint vk, uint scanCode, byte[] keys)
{
StringBuilder sb = new StringBuilder(10);
ToUnicode(vk, scanCode, keys, sb, sb.Capacity, 0);
return sb.ToString();
}
IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
Message m = new Message();
m.HWnd = hwnd;
m.Msg = msg;
m.WParam = wParam;
@@ -28,315 +84,136 @@ namespace mpvnet
return m.Result;
}
void OnKeyUp(WinForms.KeyEventArgs e)
void OnKeyDown(uint vk)
{
if (e.KeyCode == WinForms.Keys.ControlKey || e.KeyCode == WinForms.Keys.ShiftKey ||
e.KeyCode == WinForms.Keys.Menu || e.KeyCode == WinForms.Keys.None)
Keys key = (Keys)vk;
if (key == Keys.ControlKey || key == Keys.ShiftKey ||
key == Keys.Menu || key == Keys.None)
return;
string text = "";
uint charValue = MapVirtualKey((uint)e.KeyCode, 2);
string text = ToUnicode(vk);
if (charValue == 0 || (charValue & 1 << 31) == 1 << 31)
text = e.KeyCode.ToString().Trim();
else
try {
text = Convert.ToChar(charValue).ToString().ToLower().Trim();
} catch {}
if ((int)key > 111 && (int)key < 136)
text = "F" + ((int)key - 111);
for (int i = 0; i < 13; i++)
if ("D" + i == text)
text = text.Substring(1);
if ((int)key > 95 && (int)key < 106)
text = "KP" + ((int)key - 96);
switch (e.KeyCode)
switch (text)
{
case WinForms.Keys.NumPad0:
case WinForms.Keys.NumPad1:
case WinForms.Keys.NumPad2:
case WinForms.Keys.NumPad3:
case WinForms.Keys.NumPad4:
case WinForms.Keys.NumPad5:
case WinForms.Keys.NumPad6:
case WinForms.Keys.NumPad7:
case WinForms.Keys.NumPad8:
case WinForms.Keys.NumPad9:
text = "KP" + e.KeyCode.ToString()[6]; break;
case WinForms.Keys.Space:
text = "Space"; break;
case WinForms.Keys.Enter:
text = "Enter"; break;
case WinForms.Keys.Tab:
text = "TAB"; break;
case WinForms.Keys.Back:
text = "BS"; break;
case WinForms.Keys.Delete:
text = "DEL"; break;
case WinForms.Keys.Insert:
text = "INS"; break;
case WinForms.Keys.Home:
text = "Home"; break;
case WinForms.Keys.End:
text = "END"; break;
case WinForms.Keys.PageUp:
text = "PGUP"; break;
case WinForms.Keys.PageDown:
text = "PGDWN"; break;
case WinForms.Keys.Escape:
text = "ESC"; break;
case WinForms.Keys.PrintScreen:
text = "Print"; break;
case WinForms.Keys.Play:
text = "Play"; break;
case WinForms.Keys.Pause:
text = "Pause"; break;
case WinForms.Keys.MediaPlayPause:
text = "PlayPause"; break;
case WinForms.Keys.MediaStop:
text = "Stop"; break;
case WinForms.Keys.MediaNextTrack:
text = "Next"; break;
case WinForms.Keys.MediaPreviousTrack:
text = "Prev"; break;
case WinForms.Keys.VolumeUp:
text = "Volume_Up"; break;
case WinForms.Keys.VolumeDown:
text = "Volume_Down"; break;
case WinForms.Keys.VolumeMute:
text = "Mute"; break;
case WinForms.Keys.BrowserHome:
text = "Homepage"; break;
case WinForms.Keys.LaunchMail:
text = "Mail"; break;
case WinForms.Keys.BrowserFavorites:
text = "Favorites"; break;
case WinForms.Keys.BrowserSearch:
text = "Search"; break;
case WinForms.Keys.Sleep:
text = "Sleep"; break;
case WinForms.Keys.Cancel:
text = "Cancel"; break;
case "#": text = "SHARP"; break;
case "´´": text = "´"; break;
case "``": text = "`"; break;
case "^^": text = "^"; break;
}
bool wasModified = false;
bool isAlt = GetKeyState(18) < (short)0;
bool isShift = GetKeyState(16) < (short)0;
bool isCtrl = GetKeyState(17) < (short)0;
if (text.Length == 1 && KeyChar != text)
switch (key)
{
text = KeyChar;
wasModified = true;
case Keys.Left: text = "LEFT"; break;
case Keys.Up: text = "UP"; break;
case Keys.Right: text = "RIGHT"; break;
case Keys.Down: text = "DOWN"; break;
case Keys.Space: text = "SPACE"; break;
case Keys.Enter: text = "ENTER"; break;
case Keys.Tab: text = "TAB"; break;
case Keys.Back: text = "BS"; break;
case Keys.Delete: text = "DEL"; break;
case Keys.Insert: text = "INS"; break;
case Keys.Home: text = "HOME"; break;
case Keys.End: text = "END"; break;
case Keys.PageUp: text = "PGUP"; break;
case Keys.PageDown: text = "PGDWN"; break;
case Keys.Escape: text = "ESC"; break;
case Keys.Sleep: text = "SLEEP"; break;
case Keys.Cancel: text = "CANCEL"; break;
case Keys.PrintScreen: text = "PRINT"; break;
case Keys.BrowserFavorites: text = "FAVORITES"; break;
case Keys.BrowserSearch: text = "SEARCH"; break;
case Keys.BrowserHome: text = "HOMEPAGE"; break;
case Keys.LaunchMail: text = "MAIL"; break;
case Keys.Play: text = "PLAY"; break;
case Keys.Pause: text = "PAUSE"; break;
case Keys.MediaPlayPause: text = "PLAYPAUSE"; break;
case Keys.MediaStop: text = "STOP"; break;
case Keys.MediaNextTrack: text = "NEXT"; break;
case Keys.MediaPreviousTrack: text = "PREV"; break;
case Keys.VolumeUp:
case Keys.VolumeDown:
case Keys.VolumeMute:
text = ""; break;
}
if (text == "#") text = "SHARP";
bool isAlt = GetKeyState(18) < 0;
bool isShift = GetKeyState(16) < 0;
bool isCtrl = GetKeyState(17) < 0;
if (isAlt && !wasModified)
bool isLetter = (int)key > 64 && (int)key < 91;
if (isLetter && isShift)
text = text.ToUpper();
string keyString = ToUnicode(vk);
if (isAlt && !isCtrl)
text = "ALT+" + text;
if (isShift && !wasModified)
if (isShift && keyString == "")
text = "SHIFT+" + text;
if (isCtrl && !wasModified)
if (isCtrl && !(keyString != "" && isCtrl && isAlt))
text = "CTRL+" + text;
if (!string.IsNullOrEmpty(text))
SetKey(text);
}
DateTime LastKeyUp;
void SetKey(string key)
{
NewKey = key;
MenuTextBlock.Text = InputItem.Path;
KeyTextBlock.Text = key;
LastKeyUp = DateTime.Now;
}
[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);
public static WinForms.Keys ModifierKeys {
get {
WinForms.Keys keys = WinForms.Keys.None;
if (GetKeyState(17) < (short)0)
keys |= WinForms.Keys.Control;
if (GetKeyState(16) < (short)0)
keys |= WinForms.Keys.Shift;
if (GetKeyState(18) < (short)0)
keys |= WinForms.Keys.Alt;
return keys;
}
}
void ProcessKeyEventArgs(ref WinForms.Message m)
void ProcessKeyEventArgs(ref Message m)
{
int WM_KEYUP = 0x0101, WM_SYSKEYUP = 0x0105, WM_APPCOMMAND = 0x0319;
int WM_KEYDOWN = 0x100;
int WM_SYSKEYDOWN = 0x104;
int WM_APPCOMMAND = 0x319;
if (m.Msg == WM_KEYUP || m.Msg == WM_SYSKEYUP)
OnKeyUp(new WinForms.KeyEventArgs((WinForms.Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys));
if (m.Msg == WM_KEYDOWN || m.Msg == WM_SYSKEYDOWN)
OnKeyDown((uint)m.WParam.ToInt64());
else if (m.Msg == WM_APPCOMMAND)
{
var value = (AppCommand)(m.LParam.ToInt64() >> 16 & ~0xf000);
string value = mpvHelp.WM_APPCOMMAND_to_mpv_key((int)(m.LParam.ToInt64() >> 16 & ~0xf000));
switch (value)
{
case AppCommand.APPCOMMAND_MEDIA_CHANNEL_DOWN:
SetKey("CHANNEL_DOWN");
break;
case AppCommand.APPCOMMAND_MEDIA_CHANNEL_UP:
SetKey("CHANNEL_UP");
break;
case AppCommand.APPCOMMAND_MEDIA_FAST_FORWARD:
SetKey("FORWARD");
break;
case AppCommand.APPCOMMAND_MEDIA_REWIND:
SetKey("REWIND");
break;
case AppCommand.APPCOMMAND_MEDIA_PAUSE:
SetKey("PAUSE");
break;
case AppCommand.APPCOMMAND_MEDIA_PLAY:
SetKey("PLAY");
break;
case AppCommand.APPCOMMAND_MEDIA_PLAY_PAUSE:
SetKey("PLAYPAUSE");
break;
case AppCommand.APPCOMMAND_MEDIA_NEXTTRACK:
SetKey("NEXT");
break;
case AppCommand.APPCOMMAND_MEDIA_PREVIOUSTRACK:
SetKey("PREV");
break;
case AppCommand.APPCOMMAND_MEDIA_RECORD:
SetKey("RECORD");
break;
case AppCommand.APPCOMMAND_MEDIA_STOP:
SetKey("STOP");
break;
case AppCommand.APPCOMMAND_VOLUME_UP:
SetKey("VOLUME_UP");
break;
case AppCommand.APPCOMMAND_VOLUME_DOWN:
SetKey("VOLUME_DOWN");
break;
case AppCommand.APPCOMMAND_VOLUME_MUTE:
SetKey("MUTE");
break;
case AppCommand.APPCOMMAND_BROWSER_HOME:
SetKey("HOMEPAGE");
break;
case AppCommand.APPCOMMAND_LAUNCH_MAIL:
SetKey("MAIL");
break;
case AppCommand.APPCOMMAND_BROWSER_FAVORITES:
SetKey("FAVORITES");
break;
case AppCommand.APPCOMMAND_BROWSER_SEARCH:
SetKey("SEARCH");
break;
case AppCommand.APPCOMMAND_PRINT:
SetKey("PRINT");
break;
}
if (value != null)
SetKey(value);
}
}
internal enum AppCommand
{
APPCOMMAND_BASS_BOOST = 20,
APPCOMMAND_BASS_DOWN = 19,
APPCOMMAND_BASS_UP = 21,
APPCOMMAND_BROWSER_BACKWARD = 1,
APPCOMMAND_BROWSER_FAVORITES = 6,
APPCOMMAND_BROWSER_FORWARD = 2,
APPCOMMAND_BROWSER_HOME = 7,
APPCOMMAND_BROWSER_REFRESH = 3,
APPCOMMAND_BROWSER_SEARCH = 5,
APPCOMMAND_BROWSER_STOP = 4,
APPCOMMAND_CLOSE = 31,
APPCOMMAND_COPY = 36,
APPCOMMAND_CORRECTION_LIST = 45,
APPCOMMAND_CUT = 37,
APPCOMMAND_DICTATE_OR_COMMAND_CONTROL_TOGGLE = 43,
APPCOMMAND_FIND = 28,
APPCOMMAND_FORWARD_MAIL = 40,
APPCOMMAND_HELP = 27,
APPCOMMAND_LAUNCH_APP1 = 17,
APPCOMMAND_LAUNCH_APP2 = 18,
APPCOMMAND_LAUNCH_MAIL = 15,
APPCOMMAND_LAUNCH_MEDIA_SELECT = 16,
APPCOMMAND_MEDIA_CHANNEL_DOWN = 52,
APPCOMMAND_MEDIA_CHANNEL_UP = 51,
APPCOMMAND_MEDIA_FAST_FORWARD = 49,
APPCOMMAND_MEDIA_NEXTTRACK = 11,
APPCOMMAND_MEDIA_PAUSE = 47,
APPCOMMAND_MEDIA_PLAY = 46,
APPCOMMAND_MEDIA_PLAY_PAUSE = 14,
APPCOMMAND_MEDIA_PREVIOUSTRACK = 12,
APPCOMMAND_MEDIA_RECORD = 48,
APPCOMMAND_MEDIA_REWIND = 50,
APPCOMMAND_MEDIA_STOP = 13,
APPCOMMAND_MIC_ON_OFF_TOGGLE = 44,
APPCOMMAND_MICROPHONE_VOLUME_DOWN = 25,
APPCOMMAND_MICROPHONE_VOLUME_MUTE = 24,
APPCOMMAND_MICROPHONE_VOLUME_UP = 26,
APPCOMMAND_NEW = 29,
APPCOMMAND_OPEN = 30,
APPCOMMAND_PASTE = 38,
APPCOMMAND_PRINT = 33,
APPCOMMAND_REDO = 35,
APPCOMMAND_REPLY_TO_MAIL = 39,
APPCOMMAND_SAVE = 32,
APPCOMMAND_SEND_MAIL = 41,
APPCOMMAND_SPELL_CHECK = 42,
APPCOMMAND_TREBLE_DOWN = 22,
APPCOMMAND_TREBLE_UP = 23,
APPCOMMAND_UNDO = 34,
APPCOMMAND_VOLUME_DOWN = 9,
APPCOMMAND_VOLUME_MUTE = 8,
APPCOMMAND_VOLUME_UP = 10
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
static extern short VkKeyScan(char c);
[DllImport("user32.dll", SetLastError = true)]
static extern int ToAscii(uint uVirtKey,
uint uScanCode,
byte[] lpKeyState,
out uint lpChar,
uint flags);
private void Window_Loaded(object sender, RoutedEventArgs e)
void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));
SetKey(InputItem.Input);
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
InputItem.Input = NewKey;
Close();
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
void ClearButton_Click(object sender, RoutedEventArgs e)
{
InputItem.Input = "_";
Close();
}
private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
void Window_MouseWheel(object sender, MouseWheelEventArgs e)
{
if (e.Delta > 0)
SetKey("WHEEL_UP");
@@ -344,7 +221,7 @@ namespace mpvnet
SetKey("WHEEL_DOWN");
}
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
void Window_MouseUp(object sender, MouseButtonEventArgs e)
{
switch (e.ChangedButton)
{
@@ -368,7 +245,7 @@ namespace mpvnet
bool BlockMBTN_LEFT;
private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
@@ -377,9 +254,13 @@ namespace mpvnet
}
}
private void Window_TextInput(object sender, TextCompositionEventArgs e)
void Window_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
KeyChar = e.Text;
if (e.Key == Key.Tab)
{
OnKeyDown((uint)Keys.Tab);
e.Handled = true;
}
}
}
}

View File

@@ -1,6 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UI="clr-namespace:UI">
xmlns:mpvnet="clr-namespace:mpvnet">
<Style TargetType="TextBox">
<Setter Property="Template">
@@ -26,7 +26,7 @@
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{x:Static UI:Theme.Heading}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{x:Static mpvnet:Theme.Heading}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
@@ -49,8 +49,8 @@
x:Name="normal"
Width="18"
Height="18"
Fill="{x:Static UI:Theme.Background}"
Stroke="{x:Static UI:Theme.Heading}"
Fill="{x:Static mpvnet:Theme.Background}"
Stroke="{x:Static mpvnet:Theme.Heading}"
StrokeThickness="2" />
<Ellipse
x:Name="Checked1"

View File

@@ -3,20 +3,20 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid Name="SearchTextBoxUserControl1"
Background="{x:Static UI:Theme.Background}">
Background="{x:Static mpvnet:Theme.Background}">
<TextBlock Name="HintTextBlock"
Margin="5,2"
Text="Find a setting"
VerticalAlignment="Center"
Foreground="{x:Static UI:Theme.Foreground2}"
Background="{x:Static UI:Theme.Background}" />
Foreground="{x:Static mpvnet:Theme.Foreground2}"
Background="{x:Static mpvnet:Theme.Background}" />
<TextBox Name="SearchTextBox"
Height="25"
@@ -24,8 +24,8 @@
BorderThickness="2"
Background="Transparent"
TextChanged="SearchTextBox_TextChanged"
Foreground="{x:Static UI:Theme.Foreground}"
CaretBrush="{x:Static UI:Theme.Foreground}" />
Foreground="{x:Static mpvnet:Theme.Foreground}"
CaretBrush="{x:Static mpvnet:Theme.Foreground}" />
<Button Name="SearchClearButton"
Background="Transparent"
@@ -40,8 +40,8 @@
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="{x:Static UI:Theme.Background}"/>
<Setter Property="Foreground" Value="{x:Static UI:Theme.Foreground2}"/>
<Setter Property="Background" Value="{x:Static mpvnet:Theme.Background}"/>
<Setter Property="Foreground" Value="{x:Static mpvnet:Theme.Foreground2}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
@@ -58,7 +58,7 @@
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="{x:Static UI:Theme.Heading}"/>
<Setter Property="Foreground" Value="{x:Static mpvnet:Theme.Heading}"/>
</Trigger>
</Style.Triggers>
</Style>

View File

@@ -1,4 +1,5 @@
using System.Windows;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
@@ -13,7 +14,7 @@ namespace Controls
public string Text { get => SearchTextBox.Text; set => SearchTextBox.Text = value; }
private string _HintText;
string _HintText;
public string HintText {
get => _HintText;
@@ -23,13 +24,13 @@ namespace Controls
}
}
private void SearchClearButton_Click(object sender, RoutedEventArgs e)
void SearchClearButton_Click(object sender, RoutedEventArgs e)
{
SearchTextBox.Text = "";
Keyboard.Focus(SearchTextBox);
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
UpdateControls();
}

View File

@@ -3,13 +3,13 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:UI="clr-namespace:UI"
xmlns:mpvnet="clr-namespace:mpvnet"
mc:Ignorable="d"
Title="mpv.net OS Setup"
Title="mpv.net Setup"
FontSize="13"
Foreground="{x:Static UI:Theme.Foreground}"
Background="{x:Static UI:Theme.Background}"
Foreground="{x:Static mpvnet:Theme.Foreground}"
Background="{x:Static mpvnet:Theme.Background}"
SizeToContent="WidthAndHeight"
WindowStartupLocation="CenterOwner" >

View File

@@ -27,13 +27,13 @@ namespace mpvnet
} catch {}
}
private void RegisterVideo_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("video");
private void RegisterAudio_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("audio");
private void RegisterImage_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("image");
void RegisterVideo_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("video");
void RegisterAudio_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("audio");
void RegisterImage_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("image");
private void UnregisterFileAssociations_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("unreg");
void UnregisterFileAssociations_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("unreg");
private void AddToPathEnvVar_Click(object sender, RoutedEventArgs e)
void AddToPathEnvVar_Click(object sender, RoutedEventArgs e)
{
string var = WinForms.Application.StartupPath + ";";
string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);
@@ -47,7 +47,7 @@ namespace mpvnet
}
}
private void RemoveFromPathEnvVar_Click(object sender, RoutedEventArgs e)
void RemoveFromPathEnvVar_Click(object sender, RoutedEventArgs e)
{
string var = WinForms.Application.StartupPath + ";";
string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);

View File

@@ -11,7 +11,7 @@ using System.Globalization;
using System.Diagnostics;
using System.Threading.Tasks;
using UI;
using static mpvnet.Core;
namespace mpvnet
{
@@ -20,13 +20,16 @@ namespace mpvnet
public static MainForm Instance { get; set; }
public static IntPtr Hwnd { get; set; }
public new ContextMenuStripEx ContextMenu { get; set; }
Point LastCursorPosChanged;
int LastCursorChangedTickCount;
Point LastCursorPosition;
int LastCursorChanged;
int LastCycleFullscreen;
int LastAppCommand;
int TaskbarButtonCreatedMessage;
bool WasShown;
DateTime LastCycleFullscreen;
int ShownTickCount;
Taskbar Taskbar;
List<string> RecentFiles;
bool WasMaximized;
public MainForm()
{
@@ -43,41 +46,46 @@ namespace mpvnet
Instance = this;
Hwnd = Handle;
mp.Init();
ConsoleHelp.Padding = 60;
core.Init();
mp.Shutdown += Shutdown;
mp.VideoSizeChanged += VideoSizeChanged;
mp.FileLoaded += FileLoaded;
mp.Idle += Idle;
mp.Seek += () => UpdateProgressBar();
core.Shutdown += Shutdown;
core.VideoSizeChanged += VideoSizeChanged;
core.FileLoaded += FileLoaded;
core.Idle += Idle;
core.Seek += () => UpdateProgressBar();
mp.observe_property_bool("pause", PropChangePause);
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.observe_property_double("window-scale", PropChangeWindowScale);
core.observe_property("window-maximized", PropChangeWindowMaximized);
core.observe_property("window-minimized", PropChangeWindowMinimized);
core.observe_property_bool("pause", PropChangePause);
core.observe_property_bool("fullscreen", PropChangeFullscreen);
core.observe_property_bool("ontop", PropChangeOnTop);
core.observe_property_bool("border", PropChangeBorder);
if (mp.GPUAPI != "vulkan")
mp.ProcessCommandLine(false);
core.observe_property_string("sid", PropChangeSid);
core.observe_property_string("aid", PropChangeAid);
core.observe_property_string("vid", PropChangeVid);
AppDomain.CurrentDomain.UnhandledException += (sender, e) => Msg.ShowError(e.ExceptionObject.ToString());
Application.ThreadException += (sender, e) => Msg.ShowException(e.Exception);
core.observe_property_int("edition", PropChangeEdition);
core.observe_property_double("window-scale", PropChangeWindowScale);
if (core.GPUAPI != "vulkan")
core.ProcessCommandLine(false);
AppDomain.CurrentDomain.UnhandledException += (sender, e) => App.ShowException(e.ExceptionObject);
Application.ThreadException += (sender, e) => App.ShowException(e.Exception);
Msg.SupportURL = "https://github.com/stax76/mpv.net#support";
Text = "mpv.net " + Application.ProductVersion;
TaskbarButtonCreatedMessage = Native.RegisterWindowMessage("TaskbarButtonCreated");
TaskbarButtonCreatedMessage = WinAPI.RegisterWindowMessage("TaskbarButtonCreated");
ContextMenu = new ContextMenuStripEx(components);
ContextMenu.Opened += ContextMenu_Opened;
ContextMenu.Opening += ContextMenu_Opening;
if (mp.Screen == -1)
mp.Screen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
if (core.Screen == -1)
core.Screen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
int targetIndex = mp.Screen;
int targetIndex = core.Screen;
Screen[] screens = Screen.AllScreens;
if (targetIndex < 0)
@@ -91,6 +99,9 @@ namespace mpvnet
Left = target.X + (target.Width - Width) / 2;
Top = target.Y + (target.Height - Height) / 2;
if (!core.Border)
FormBorderStyle = FormBorderStyle.None;
int posX = RegistryHelp.GetInt(App.RegPath, "PosX");
int posY = RegistryHelp.GetInt(App.RegPath, "PosY");
@@ -100,11 +111,17 @@ namespace mpvnet
Top = posY - Height / 2;
}
if (App.Maximized)
if (core.WindowMaximized)
{
SetFormPosAndSize(1, true);
WindowState = FormWindowState.Maximized;
}
if (core.WindowMinimized)
{
SetFormPosAndSize(1, true);
WindowState = FormWindowState.Minimized;
}
}
catch (Exception ex)
{
@@ -121,6 +138,8 @@ namespace mpvnet
BeginInvoke(new Action(() => Text = "mpv.net " + Application.ProductVersion));
}
bool WasShown() => ShownTickCount != 0 && Environment.TickCount > ShownTickCount + 500;
void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
void VideoSizeChanged() => BeginInvoke(new Action(() => SetFormPosAndSize()));
@@ -131,11 +150,20 @@ namespace mpvnet
bool IsFullscreen => WindowState == FormWindowState.Maximized && FormBorderStyle == FormBorderStyle.None;
bool IsMouseInOSC() => PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
bool IsMouseInOSC()
{
Point pos = PointToClient(Control.MousePosition);
float top = 0;
if (FormBorderStyle == FormBorderStyle.None)
top = ClientSize.Height * 0.1f;
return pos.Y > ClientSize.Height * 0.85 || pos.Y < top;
}
void ContextMenu_Opening(object sender, CancelEventArgs e)
{
lock (mp.MediaTracks)
lock (core.MediaTracks)
{
MenuItem trackMenuItem = FindMenuItem("Track");
@@ -143,16 +171,16 @@ namespace mpvnet
{
trackMenuItem.DropDownItems.Clear();
MediaTrack[] audTracks = mp.MediaTracks.Where(track => track.Type == "a").ToArray();
MediaTrack[] subTracks = mp.MediaTracks.Where(track => track.Type == "s").ToArray();
MediaTrack[] vidTracks = mp.MediaTracks.Where(track => track.Type == "v").ToArray();
MediaTrack[] ediTracks = mp.MediaTracks.Where(track => track.Type == "e").ToArray();
MediaTrack[] audTracks = core.MediaTracks.Where(track => track.Type == "a").ToArray();
MediaTrack[] subTracks = core.MediaTracks.Where(track => track.Type == "s").ToArray();
MediaTrack[] vidTracks = core.MediaTracks.Where(track => track.Type == "v").ToArray();
MediaTrack[] ediTracks = core.MediaTracks.Where(track => track.Type == "e").ToArray();
foreach (MediaTrack track in vidTracks)
{
MenuItem mi = new MenuItem(track.Text);
mi.Action = () => mp.commandv("set", "vid", track.ID.ToString());
mi.Checked = mp.Vid == track.ID.ToString();
mi.Action = () => core.commandv("set", "vid", track.ID.ToString());
mi.Checked = core.Vid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi);
}
@@ -162,8 +190,8 @@ namespace mpvnet
foreach (MediaTrack track in audTracks)
{
MenuItem mi = new MenuItem(track.Text);
mi.Action = () => mp.commandv("set", "aid", track.ID.ToString());
mi.Checked = mp.Aid == track.ID.ToString();
mi.Action = () => core.commandv("set", "aid", track.ID.ToString());
mi.Checked = core.Aid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi);
}
@@ -173,16 +201,16 @@ namespace mpvnet
foreach (MediaTrack track in subTracks)
{
MenuItem mi = new MenuItem(track.Text);
mi.Action = () => mp.commandv("set", "sid", track.ID.ToString());
mi.Checked = mp.Sid == track.ID.ToString();
mi.Action = () => core.commandv("set", "sid", track.ID.ToString());
mi.Checked = core.Sid == track.ID.ToString();
trackMenuItem.DropDownItems.Add(mi);
}
if (subTracks.Length > 0)
{
MenuItem mi = new MenuItem("S: No subtitles");
mi.Action = () => mp.commandv("set", "sid", "no");
mi.Checked = mp.Sid == "no";
mi.Action = () => core.commandv("set", "sid", "no");
mi.Checked = core.Sid == "no";
trackMenuItem.DropDownItems.Add(mi);
}
@@ -192,14 +220,14 @@ namespace mpvnet
foreach (MediaTrack track in ediTracks)
{
MenuItem mi = new MenuItem(track.Text);
mi.Action = () => mp.commandv("set", "edition", track.ID.ToString());
mi.Checked = mp.Edition == track.ID;
mi.Action = () => core.commandv("set", "edition", track.ID.ToString());
mi.Checked = core.Edition == track.ID;
trackMenuItem.DropDownItems.Add(mi);
}
}
}
lock (mp.Chapters)
lock (core.Chapters)
{
MenuItem chaptersMenuItem = FindMenuItem("Chapters");
@@ -207,11 +235,11 @@ namespace mpvnet
{
chaptersMenuItem.DropDownItems.Clear();
foreach (var i in mp.Chapters)
foreach (var i in core.Chapters)
{
MenuItem mi = new MenuItem(i.Key);
mi.ShortcutKeyDisplayString = TimeSpan.FromSeconds(i.Value).ToString().Substring(0, 8) + " ";
mi.Action = () => mp.commandv("seek", i.Value.ToString(CultureInfo.InvariantCulture), "absolute");
mi.Action = () => core.commandv("seek", i.Value.ToString(CultureInfo.InvariantCulture), "absolute");
chaptersMenuItem.DropDownItems.Add(mi);
}
}
@@ -224,7 +252,7 @@ namespace mpvnet
recent.DropDownItems.Clear();
foreach (string path in RecentFiles)
MenuItem.Add(recent.DropDownItems, path, () => mp.Load(new[] { path }, true, Control.ModifierKeys.HasFlag(Keys.Control)));
MenuItem.Add(recent.DropDownItems, path, () => core.LoadFiles(new[] { path }, true, Control.ModifierKeys.HasFlag(Keys.Control)));
recent.DropDownItems.Add(new ToolStripSeparator());
MenuItem mi = new MenuItem("Clear List");
@@ -252,16 +280,14 @@ namespace mpvnet
return null;
}
bool WasInitialSizeSet;
void SetFormPosAndSize(double scale = 1, bool force = false)
{
if (!force)
{
if (WindowState == FormWindowState.Maximized)
if (WindowState != FormWindowState.Normal)
return;
if (mp.Fullscreen)
if (core.Fullscreen)
{
CycleFullscreen(true);
return;
@@ -269,56 +295,56 @@ namespace mpvnet
}
Screen screen = Screen.FromControl(this);
int autoFitHeight = Convert.ToInt32(screen.WorkingArea.Height * mp.Autofit);
int autoFitHeight = Convert.ToInt32(screen.WorkingArea.Height * core.Autofit);
if (mp.VideoSize.Height == 0 || mp.VideoSize.Width == 0 ||
mp.VideoSize.Width / (float)mp.VideoSize.Height < App.MinimumAspectRatio)
if (core.VideoSize.Height == 0 || core.VideoSize.Width == 0 ||
core.VideoSize.Width / (float)core.VideoSize.Height < App.MinimumAspectRatio)
mp.VideoSize = new Size((int)(autoFitHeight * (16 / 9.0)), autoFitHeight);
core.VideoSize = new Size((int)(autoFitHeight * (16 / 9f)), autoFitHeight);
Size size = mp.VideoSize;
int height = size.Height;
Size videoSize = core.VideoSize;
int height = videoSize.Height;
if (App.RememberHeight)
{
if (WasInitialSizeSet)
if (core.WasInitialSizeSet || scale != 1)
height = ClientSize.Height;
else
{
int savedHeight = RegistryHelp.GetInt(App.RegPath, "Height");
if (App.StartSize == "always" && savedHeight != 0)
height = savedHeight;
else
if (App.StartSize != "video")
height = autoFitHeight;
WasInitialSizeSet = true;
}
core.WasInitialSizeSet = true;
}
height = Convert.ToInt32(height * scale);
int width = Convert.ToInt32(height * size.Width / (double)size.Height);
int width = height * videoSize.Width / videoSize.Height;
int maxHeight = screen.WorkingArea.Height - (Height - ClientSize.Height);
int maxWidth = screen.WorkingArea.Width - (Width - ClientSize.Width);
if (height > screen.WorkingArea.Height * 0.9)
if (height < maxHeight * core.AutofitSmaller)
{
height = Convert.ToInt32(screen.WorkingArea.Height * 0.9);
width = Convert.ToInt32(height * size.Width / (double)size.Height);
height = Convert.ToInt32(maxHeight * core.AutofitSmaller);
width = Convert.ToInt32(height * videoSize.Width / videoSize.Height);
}
if (width > screen.WorkingArea.Width * 0.9)
if (height > maxHeight * core.AutofitLarger)
{
width = Convert.ToInt32(screen.WorkingArea.Width * 0.9);
height = Convert.ToInt32(width * size.Height / (double)size.Width);
height = Convert.ToInt32(maxHeight * core.AutofitLarger);
width = Convert.ToInt32(height * videoSize.Width / videoSize.Height);
}
if (height < screen.WorkingArea.Height * mp.AutofitSmaller)
if (width > maxWidth)
{
height = Convert.ToInt32(screen.WorkingArea.Height * mp.AutofitSmaller);
width = Convert.ToInt32(height * size.Width / (double)size.Height);
}
if (height > screen.WorkingArea.Height * mp.AutofitLarger)
{
height = Convert.ToInt32(screen.WorkingArea.Height * mp.AutofitLarger);
width = Convert.ToInt32(height * size.Width / (double)size.Height);
width = maxWidth;
height = (int)Math.Ceiling(width * videoSize.Height / (double)videoSize.Width);
}
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 WinAPI.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
NativeHelp.AddWindowBorders(Handle, ref rect);
int left = middlePos.X - rect.Width / 2;
int top = middlePos.Y - rect.Height / 2;
@@ -341,49 +367,59 @@ namespace mpvnet
if (top + rect.Height > maxBottom)
top = maxBottom - rect.Height;
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
WinAPI.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */,
left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
}
public void CycleFullscreen(bool enabled)
{
LastCycleFullscreen = DateTime.Now;
mp.Fullscreen = enabled;
LastCycleFullscreen = Environment.TickCount;
core.Fullscreen = enabled;
if (enabled)
{
if (WindowState != FormWindowState.Maximized || FormBorderStyle != FormBorderStyle.None)
{
if (WindowState == FormWindowState.Maximized)
WindowState = FormWindowState.Minimized;
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
if (WasMaximized)
{
Rectangle b = Screen.FromControl(this).Bounds;
uint SWP_SHOWWINDOW = 0x0040;
IntPtr HWND_TOP= IntPtr.Zero;
WinAPI.SetWindowPos(Handle, HWND_TOP, b.X, b.Y, b.Width, b.Height, SWP_SHOWWINDOW);
}
}
}
else
{
if (WindowState == FormWindowState.Maximized && FormBorderStyle == FormBorderStyle.None)
{
if (WasMaximized)
WindowState = FormWindowState.Maximized;
else
WindowState = FormWindowState.Normal;
if (mp.Border)
if (core.Border)
FormBorderStyle = FormBorderStyle.Sizable;
else
FormBorderStyle = FormBorderStyle.None;
SetFormPosAndSize();
SaveWindowProperties();
}
}
}
public void BuildMenu()
{
string content = File.ReadAllText(mp.InputConfPath);
string content = File.ReadAllText(core.InputConfPath);
var items = CommandItem.GetItems(content);
if (!content.Contains("#menu:"))
{
var defaultItems = CommandItem.GetItems(Properties.Resources.inputConf);
var defaultItems = CommandItem.GetItems(Properties.Resources.input_conf);
foreach (CommandItem item in items)
foreach (CommandItem defaultItem in defaultItems)
@@ -402,7 +438,7 @@ namespace mpvnet
MenuItem menuItem = ContextMenu.Add(path, () => {
try {
mp.command(item.Command);
core.command(item.Command);
} catch (Exception ex) {
Msg.ShowException(ex);
}
@@ -413,21 +449,26 @@ namespace mpvnet
}
}
private void FileLoaded()
void FileLoaded()
{
string path = mp.get_property_string("path");
string path = core.get_property_string("path");
BeginInvoke(new Action(() => {
if (path.Contains("://"))
Text = mp.get_property_string("media-title") + " - mpv.net " + Application.ProductVersion;
Text = core.get_property_string("media-title") + " - mpv.net " + Application.ProductVersion;
else if (path.Contains(":\\") || path.StartsWith("\\\\"))
Text = path.FileName() + " - mpv.net " + Application.ProductVersion;
else
Text = "mpv.net " + Application.ProductVersion;
int interval = (int)(mp.Duration.TotalMilliseconds / 100);
if (interval < 100) interval = 100;
if (interval > 1000) interval = 1000;
int interval = (int)(core.Duration.TotalMilliseconds / 100);
if (interval < 100)
interval = 100;
if (interval > 1000)
interval = 1000;
ProgressTimer.Interval = interval;
UpdateProgressBar();
}));
@@ -441,6 +482,16 @@ namespace mpvnet
RecentFiles.RemoveAt(App.RecentCount);
}
void SaveWindowProperties()
{
if (WindowState == FormWindowState.Normal)
{
RegistryHelp.SetValue(App.RegPath, "PosX", Left + Width / 2);
RegistryHelp.SetValue(App.RegPath, "PosY", Top + Height / 2);
RegistryHelp.SetValue(App.RegPath, "Height", ClientSize.Height);
}
}
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
@@ -455,66 +506,91 @@ namespace mpvnet
switch (m.Msg)
{
case 0x0201: // WM_LBUTTONDOWN
case 0x0202: // WM_LBUTTONUP
case 0x0207: // WM_MBUTTONDOWN
case 0x0208: // WM_MBUTTONUP
case 0x020b: // WM_XBUTTONDOWN
case 0x020c: // WM_XBUTTONUP
case 0x020A: // WM_MOUSEWHEEL
case 0x0100: // WM_KEYDOWN
case 0x0101: // WM_KEYUP
case 0x0104: // WM_SYSKEYDOWN
case 0x0105: // WM_SYSKEYUP
case 0x201: // WM_LBUTTONDOWN
case 0x202: // WM_LBUTTONUP
case 0x207: // WM_MBUTTONDOWN
case 0x208: // WM_MBUTTONUP
case 0x20b: // WM_XBUTTONDOWN
case 0x20c: // WM_XBUTTONUP
case 0x20A: // WM_MOUSEWHEEL
case 0x100: // WM_KEYDOWN
case 0x101: // WM_KEYUP
case 0x104: // WM_SYSKEYDOWN
case 0x105: // WM_SYSKEYUP
{
bool skip = m.Msg == 0x100 && LastAppCommand != 0 &&
(Environment.TickCount - LastAppCommand) < 1000;
if (core.WindowHandle != IntPtr.Zero && !skip)
m.Result = WinAPI.SendMessage(core.WindowHandle, m.Msg, m.WParam, m.LParam);
}
break;
case 0x319: // WM_APPCOMMAND
if (mp.WindowHandle != IntPtr.Zero)
Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
{
string value = mpvHelp.WM_APPCOMMAND_to_mpv_key((int)(m.LParam.ToInt64() >> 16 & ~0xf000));
if (value != null)
{
core.command("keypress " + value);
m.Result = new IntPtr(1);
LastAppCommand = Environment.TickCount;
return;
}
}
break;
case 0x0200: // WM_MOUSEMOVE
{
if ((DateTime.Now - LastCycleFullscreen).TotalMilliseconds > 500)
if (Environment.TickCount - LastCycleFullscreen > 500)
{
Point pos = PointToClient(Cursor.Position);
mp.command($"mouse {pos.X} {pos.Y}");
core.command($"mouse {pos.X} {pos.Y}");
}
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
if (CursorHelp.IsPosDifferent(LastCursorPosition))
CursorHelp.Show();
}
break;
case 0x2a3: // WM_MOUSELEAVE
// osc won't always auto hide
mp.command("mouse 1 1");
//osc won't auto hide after mouse left window in borderless mode
core.command($"mouse {ClientSize.Width / 2} {ClientSize.Height / 3}");
break;
case 0x203: // WM_LBUTTONDBLCLK
{
Point pos = PointToClient(Cursor.Position);
mp.command($"mouse {pos.X} {pos.Y} 0 double");
core.command($"mouse {pos.X} {pos.Y} 0 double");
}
break;
case 0x02E0: // WM_DPICHANGED
{
if (!WasShown)
if (!WasShown())
break;
Native.RECT rect = Marshal.PtrToStructure<Native.RECT>(m.LParam);
Native.SetWindowPos(Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Width, rect.Height, 0);
WinAPI.RECT rect = Marshal.PtrToStructure<WinAPI.RECT>(m.LParam);
WinAPI.SetWindowPos(Handle, IntPtr.Zero, rect.Left, rect.Top, rect.Width, rect.Height, 0);
}
break;
case 0x0214: // WM_SIZING
{
var rc = Marshal.PtrToStructure<Native.RECT>(m.LParam);
var rc = Marshal.PtrToStructure<WinAPI.RECT>(m.LParam);
var r = rc;
NativeHelp.SubtractWindowBorders(Handle, ref r);
int c_w = r.Right - r.Left, c_h = r.Bottom - r.Top;
Size s = mp.VideoSize;
Size s = core.VideoSize;
if (s == Size.Empty)
s = new Size(16, 9);
float aspect = s.Width / (float)s.Height;
int d_w = Convert.ToInt32(c_h * aspect - c_w);
int d_h = Convert.ToInt32(c_w / aspect - c_h);
int d_w = (int)(c_h * aspect - c_w);
int d_h = (int)(c_w / aspect - c_h);
Debug.WriteLine(d_w);
Debug.WriteLine(d_h);
int d_w2 = (int)(c_h * aspect - c_w);
int d_h2 = (int)(c_w / aspect - c_h);
Debug.WriteLine(d_w2);
Debug.WriteLine(d_h2);
int[] d_corners = { d_w, d_h, -d_w, -d_h };
int[] corners = { rc.Left, rc.Top, rc.Right, rc.Bottom };
int corner = NativeHelp.GetResizeBorder(m.WParam.ToInt32());
@@ -522,13 +598,13 @@ namespace mpvnet
if (corner >= 0)
corners[corner] -= d_corners[corner];
Marshal.StructureToPtr<Native.RECT>(new Native.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false);
Marshal.StructureToPtr<WinAPI.RECT>(new WinAPI.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false);
m.Result = new IntPtr(1);
}
return;
case 0x004A: // WM_COPYDATA
{
var copyData = (Native.COPYDATASTRUCT)m.GetLParam(typeof(Native.COPYDATASTRUCT));
var copyData = (WinAPI.COPYDATASTRUCT)m.GetLParam(typeof(WinAPI.COPYDATASTRUCT));
string[] files = copyData.lpData.Split('\n');
string mode = files[0];
files = files.Skip(1).ToArray();
@@ -536,11 +612,11 @@ namespace mpvnet
switch (mode)
{
case "single":
mp.Load(files, true, Control.ModifierKeys.HasFlag(Keys.Control));
core.LoadFiles(files, true, Control.ModifierKeys.HasFlag(Keys.Control));
break;
case "queue":
foreach (string file in files)
mp.commandv("loadfile", file, "append");
core.commandv("loadfile", file, "append");
break;
}
@@ -549,66 +625,100 @@ namespace mpvnet
return;
}
if (m.Msg == TaskbarButtonCreatedMessage && mp.TaskbarProgress)
if (m.Msg == TaskbarButtonCreatedMessage && core.TaskbarProgress)
{
Taskbar = new Taskbar(Handle);
ProgressTimer.Start();
}
// beep sound when closed using taskbar due to exception
if (!IsDisposed)
base.WndProc(ref m);
}
void CursorTimer_Tick(object sender, EventArgs e)
{
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
if (CursorHelp.IsPosDifferent(LastCursorPosition))
{
LastCursorPosChanged = Control.MousePosition;
LastCursorChangedTickCount = Environment.TickCount;
LastCursorPosition = Control.MousePosition;
LastCursorChanged = Environment.TickCount;
}
else if (Environment.TickCount - LastCursorChangedTickCount > 1500 &&
else if (Environment.TickCount - LastCursorChanged > 1500 &&
!IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) &&
Form.ActiveForm == this && !ContextMenu.Visible)
CursorHelp.Hide();
}
private void ProgressTimer_Tick(object sender, EventArgs e) => UpdateProgressBar();
void ProgressTimer_Tick(object sender, EventArgs e) => UpdateProgressBar();
void UpdateProgressBar()
{
if (mp.TaskbarProgress && Taskbar != null)
Taskbar.SetValue(mp.get_property_number("time-pos"), mp.Duration.TotalSeconds);
if (core.TaskbarProgress && Taskbar != null)
Taskbar.SetValue(core.get_property_number("time-pos"), core.Duration.TotalSeconds);
}
void PropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value));
void PropChangeAid(string value) => mp.Aid = value;
void PropChangeAid(string value) => core.Aid = value;
void PropChangeSid(string value) => mp.Sid = value;
void PropChangeSid(string value) => core.Sid = value;
void PropChangeVid(string value) => mp.Vid = value;
void PropChangeVid(string value) => core.Vid = value;
void PropChangeEdition(int value) => mp.Edition = value;
void PropChangeEdition(int value) => core.Edition = value;
void PropChangeWindowScale(double value)
{
if (value != 1)
{
BeginInvoke(new Action(() => SetFormPosAndSize(value)));
mp.command("no-osd set window-scale 1");
core.command("no-osd set window-scale 1");
}
}
void PropChangeWindowMaximized()
{
if (!WasShown())
return;
BeginInvoke(new Action(() =>
{
core.WindowMaximized = core.get_property_bool("window-maximized");
if (core.WindowMaximized && WindowState != FormWindowState.Maximized)
WindowState = FormWindowState.Maximized;
else if (!core.WindowMaximized && WindowState == FormWindowState.Maximized)
WindowState = FormWindowState.Normal;
}));
}
void PropChangeWindowMinimized()
{
if (!WasShown())
return;
BeginInvoke(new Action(() =>
{
core.WindowMinimized = core.get_property_bool("window-minimized");
if (core.WindowMinimized && WindowState != FormWindowState.Minimized)
WindowState = FormWindowState.Minimized;
else if (!core.WindowMinimized && WindowState == FormWindowState.Minimized)
WindowState = FormWindowState.Normal;
}));
}
void PropChangeBorder(bool enabled) {
mp.Border = enabled;
core.Border = enabled;
BeginInvoke(new Action(() => {
if (!IsFullscreen)
{
if (mp.Border && FormBorderStyle == FormBorderStyle.None)
if (core.Border && FormBorderStyle == FormBorderStyle.None)
FormBorderStyle = FormBorderStyle.Sizable;
if (!mp.Border && FormBorderStyle == FormBorderStyle.Sizable)
if (!core.Border && FormBorderStyle == FormBorderStyle.Sizable)
FormBorderStyle = FormBorderStyle.None;
}
}));
@@ -616,7 +726,7 @@ namespace mpvnet
void PropChangePause(bool enabled)
{
if (Taskbar != null && mp.TaskbarProgress)
if (Taskbar != null && core.TaskbarProgress)
{
if (enabled)
Taskbar.SetState(TaskbarStates.Paused);
@@ -629,10 +739,10 @@ namespace mpvnet
{
base.OnLoad(e);
if (mp.GPUAPI != "vulkan")
mp.VideoSizeAutoResetEvent.WaitOne(App.StartThreshold);
if (core.GPUAPI != "vulkan")
core.VideoSizeAutoResetEvent.WaitOne(App.StartThreshold);
LastCycleFullscreen = DateTime.Now;
LastCycleFullscreen = Environment.TickCount;
SetFormPosAndSize();
}
@@ -640,8 +750,8 @@ namespace mpvnet
{
base.OnShown(e);
if (mp.GPUAPI == "vulkan")
mp.ProcessCommandLine(false);
if (core.GPUAPI == "vulkan")
core.ProcessCommandLine(false);
ToolStripRendererEx.ForegroundColor = Theme.Current.GetWinFormsColor("menu-foreground");
ToolStripRendererEx.BackgroundColor = Theme.Current.GetWinFormsColor("menu-background");
@@ -655,35 +765,61 @@ namespace mpvnet
System.Windows.Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
Cursor.Position = new Point(Cursor.Position.X + 1, Cursor.Position.Y);
UpdateCheck.DailyCheck();
mp.LoadScripts();
core.LoadScripts();
Task.Run(() => App.Extension = new Extension());
WasShown = true;
ShownTickCount = Environment.TickCount;
}
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
WinAPI.SendMessage(MainForm.Instance.Handle, m.Msg, m.WParam, m.LParam);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (mp.IsLogoVisible)
mp.ShowLogo();
if (core.IsLogoVisible)
core.ShowLogo();
if (FormBorderStyle != FormBorderStyle.None)
{
if (WindowState == FormWindowState.Maximized)
WasMaximized = true;
else if (WindowState == FormWindowState.Normal)
WasMaximized = false;
}
if (WasShown())
{
if (WindowState == FormWindowState.Minimized)
{
core.set_property_string("window-minimized", "yes");
}
else if (WindowState == FormWindowState.Normal)
{
core.set_property_string("window-maximized", "no");
core.set_property_string("window-minimized", "no");
}
else if (WindowState == FormWindowState.Maximized)
{
core.set_property_string("window-maximized", "yes");
}
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (WindowState == FormWindowState.Normal)
{
RegistryHelp.SetValue(App.RegPath, "PosX", Left + Width / 2);
RegistryHelp.SetValue(App.RegPath, "PosY", Top + Height / 2);
}
SaveWindowProperties();
RegistryHelp.SetValue(App.RegPath, "Recent", RecentFiles.ToArray());
if (mp.IsQuitNeeded)
mp.commandv("quit");
if (core.IsQuitNeeded)
core.commandv("quit");
if (!mp.ShutdownAutoResetEvent.WaitOne(10000))
if (!core.ShutdownAutoResetEvent.WaitOne(10000))
Msg.ShowError("Shutdown thread failed to complete within 10 seconds.");
}
@@ -692,16 +828,15 @@ namespace mpvnet
base.OnMouseDown(e);
if (WindowState == FormWindowState.Normal &&
e.Button == MouseButtons.Left &&
e.Y < ClientSize.Height * 0.9)
e.Button == MouseButtons.Left && !IsMouseInOSC())
{
var HTCAPTION = new IntPtr(2);
Native.ReleaseCapture();
Native.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
WinAPI.ReleaseCapture();
WinAPI.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
}
if (Width - e.Location.X < 10 && e.Location.Y < 10)
mp.commandv("quit");
core.commandv("quit");
}
protected override void OnDragEnter(DragEventArgs e)
@@ -717,10 +852,10 @@ namespace mpvnet
base.OnDragDrop(e);
if (e.Data.GetDataPresent(DataFormats.FileDrop))
mp.Load(e.Data.GetData(DataFormats.FileDrop) as String[], true, Control.ModifierKeys.HasFlag(Keys.Control));
core.LoadFiles(e.Data.GetData(DataFormats.FileDrop) as String[], true, Control.ModifierKeys.HasFlag(Keys.Control));
if (e.Data.GetDataPresent(DataFormats.Text))
mp.Load(new[] { e.Data.GetData(DataFormats.Text).ToString() }, true, Control.ModifierKeys.HasFlag(Keys.Control));
core.LoadFiles(new[] { e.Data.GetData(DataFormats.Text).ToString() }, true, Control.ModifierKeys.HasFlag(Keys.Control));
}
protected override void OnLostFocus(EventArgs e)
@@ -731,7 +866,10 @@ namespace mpvnet
protected override void OnKeyDown(KeyEventArgs e)
{
e.SuppressKeyPress = true; // prevent beep using alt key
// prevent beep using alt key
if (Control.ModifierKeys == Keys.Alt)
e.SuppressKeyPress = true;
base.OnKeyDown(e);
}
}

View File

@@ -76,29 +76,6 @@
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
</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="Microsoft.VisualBasic" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
@@ -118,6 +95,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Misc\App.cs" />
<Compile Include="Misc\NewLine.cs" />
<Compile Include="Misc\Extension.cs" />
<Content Include="..\License.txt">
<Link>License.txt</Link>
@@ -155,6 +133,7 @@
<Compile Include="Misc\UpdateCheck.cs" />
<Compile Include="Misc\RegistryHelp.cs" />
<Compile Include="Misc\Theme.cs" />
<Compile Include="Misc\PowerShell.cs" />
<Compile Include="WPF\SearchTextBoxUserControl.xaml.cs">
<DependentUpon>SearchTextBoxUserControl.xaml</DependentUpon>
</Compile>
@@ -172,13 +151,11 @@
<Compile Include="WinForms\Menu.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Scripting\PowerShellScript.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Scripting\PythonScript.cs" />
<Compile Include="mpv\libmpv.cs" />
<Compile Include="WinForms\MainForm.cs">
<SubType>Form</SubType>
@@ -187,8 +164,8 @@
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Misc\Misc.cs" />
<Compile Include="mpv\mp.cs" />
<Compile Include="Misc\Command.cs" />
<Compile Include="mpv\core.cs" />
<Compile Include="Misc\Commands.cs" />
<Compile Include="Native\Native.cs" />
<Compile Include="Native\NativeHelp.cs" />
<Compile Include="Misc\Program.cs" />
@@ -246,8 +223,8 @@
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<Content Include="mpvnet.ico" />
<Content Include="Resources\mpvConf.txt" />
<Content Include="Resources\confToml.txt" />
<Content Include="Resources\mpv.conf.txt" />
<Content Include="Resources\editor.toml.txt" />
<None Include="Resources\mpvnet.ico" />
<None Include="Resources\mpvnet.png" />
</ItemGroup>
@@ -255,7 +232,7 @@
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Resources\inputConf.txt" />
<Content Include="Resources\input.conf.txt" />
</ItemGroup>
<ItemGroup>
<Page Include="DynamicGUI\OptionSettingControl.xaml">

1179
mpv.net/mpv/Core.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,31 @@
using System;

using System;
using System.Runtime.InteropServices;
using System.Text;
namespace mpvnet
{
public class libmpv
{
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_create();
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_initialize(IntPtr mpvHandle);
public static extern mpv_error mpv_initialize(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_command(IntPtr mpvHandle, IntPtr strings);
public static extern mpv_error mpv_command(IntPtr mpvHandle, IntPtr strings);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_command_string(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
public static extern mpv_error mpv_command_string(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string command);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mpv_error_string(mpv_error error);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_terminate_destroy(IntPtr mpvHandle);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_request_log_messages(IntPtr mpvHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string min_level);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_option(IntPtr mpvHandle, byte[] name, mpv_format format, ref long data);
@@ -27,23 +33,22 @@ namespace mpvnet
public static extern int mpv_set_option_string(IntPtr mpvHandle, byte[] name, byte[] value);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out IntPtr data);
public static extern mpv_error mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out 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);
public static extern mpv_error mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, out 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);
public static extern mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref byte[] data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref Int64 data);
public static extern mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref Int64 data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_observe_property(
IntPtr mpvHandle,
UInt64 reply_userdata,
[MarshalAs(UnmanagedType.LPUTF8Str)] string name,
mpv_format format);
public static extern mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref double data);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern mpv_error mpv_observe_property(IntPtr mpvHandle, UInt64 reply_userdata, [MarshalAs(UnmanagedType.LPUTF8Str)] string name, mpv_format format);
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mpv_unobserve_property(IntPtr mpvHandle, UInt64 registered_reply_userdata);
@@ -147,9 +152,9 @@ namespace mpvnet
[StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message
{
public string prefix;
public string level;
public string text;
public IntPtr prefix;
public IntPtr level;
public IntPtr text;
public mpv_log_level log_level;
}
@@ -183,5 +188,50 @@ namespace mpvnet
public int reason;
public int error;
}
public static IntPtr AllocateUtf8ArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
{
int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel)
byteArrayPointers = new IntPtr[numberOfStrings];
IntPtr rootPointer = Marshal.AllocCoTaskMem(IntPtr.Size * numberOfStrings);
for (int index = 0; index < arr.Length; index++)
{
var bytes = GetUtf8Bytes(arr[index]);
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
byteArrayPointers[index] = unmanagedPointer;
}
Marshal.Copy(byteArrayPointers, 0, rootPointer, numberOfStrings);
return rootPointer;
}
public static string[] ConvertFromUtf8Strings(IntPtr utf8StringArray, int stringCount)
{
IntPtr[] intPtrArray = new IntPtr[stringCount];
string[] stringArray = new string[stringCount];
Marshal.Copy(utf8StringArray, intPtrArray, 0, stringCount);
for (int i = 0; i < stringCount; i++)
stringArray[i] = ConvertFromUtf8(intPtrArray[i]);
return stringArray;
}
public static string ConvertFromUtf8(IntPtr nativeUtf8)
{
int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0)
++len;
byte[] buffer = new byte[len];
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
public static string GetError(mpv_error err) => ConvertFromUtf8(mpv_error_string(err));
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
}

View File

@@ -1,939 +0,0 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
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 WinForms = System.Windows.Forms;
using static mpvnet.libmpv;
using static Native;
namespace mpvnet
{
public delegate void MpvBoolPropChangeHandler(string propName, bool value);
public class mp
{
public static event Action VideoSizeChanged;
// Lua/JS event libmpv event
// 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 event Action Initialized;
public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>();
public static List<KeyValuePair<string, Action<int>>> IntPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<int>>>();
public static List<KeyValuePair<string, Action<double>>> DoublePropChangeActions { get; set; } = new List<KeyValuePair<string, Action<double>>>();
public static List<KeyValuePair<string, Action<string>>> StringPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<string>>>();
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 IntPtr Handle { get; set; }
public static IntPtr WindowHandle { get; set; }
public static List<PythonScript> PythonScripts { get; set; } = new List<PythonScript>();
public static Size VideoSize { get; set; }
public static TimeSpan Duration;
public static AutoResetEvent ShutdownAutoResetEvent { get; set; } = new AutoResetEvent(false);
public static AutoResetEvent VideoSizeAutoResetEvent { get; set; } = new AutoResetEvent(false);
public static string InputConfPath { get => ConfigFolder + "input.conf"; }
public static string ConfPath { get => ConfigFolder + "mpv.conf"; }
public static string Sid { get; set; } = "";
public static string Aid { get; set; } = "";
public static string Vid { get; set; } = "";
public static string GPUAPI { get; set; } = "auto";
public static bool IsLogoVisible { set; get; }
public static bool IsQuitNeeded { set; get; } = true;
public static bool Fullscreen { get; set; }
public static bool Border { get; set; } = true;
public static bool TaskbarProgress { get; set; } = true;
public static int Screen { get; set; } = -1;
public static int Edition { get; set; }
public static float Autofit { get; set; } = 0.5f;
public static float AutofitSmaller { get; set; } = 0.4f;
public static float AutofitLarger { get; set; } = 0.75f;
public static void Init()
{
Handle = mpv_create();
Task.Run(() => EventLoop());
if (App.IsStartedFromTerminal)
{
set_property_string("terminal", "yes");
set_property_string("input-terminal", "yes");
set_property_string("msg-level", "osd/libass=fatal");
}
set_property_string("wid", MainForm.Hwnd.ToString());
set_property_string("osc", "yes");
set_property_string("input-media-keys", "yes");
set_property_string("force-window", "yes");
set_property_string("config-dir", ConfigFolder);
set_property_string("config", "yes");
ProcessCommandLine(true);
mpv_initialize(Handle);
Initialized?.Invoke();
LoadMpvScripts();
}
public static void ProcessProperty(string name, string value)
{
if (name.Any(char.IsUpper))
Msg.ShowError("Uppercase char detected: " + name,
"mpv properties using the command line and the mpv.conf config file are required to be lowercase.");
switch (name)
{
case "autofit":
if (int.TryParse(value.Trim('%'), out int result))
Autofit = result / 100f;
break;
case "autofit-smaller":
if (int.TryParse(value.Trim('%'), out int result2))
AutofitSmaller = result2 / 100f;
break;
case "autofit-larger":
if (int.TryParse(value.Trim('%'), out int result3))
AutofitLarger = result3 / 100f;
break;
case "fs":
case "fullscreen": Fullscreen = value == "yes"; break;
case "border": Border = value == "yes"; break;
case "taskbar-progress": TaskbarProgress = value == "yes"; break;
case "screen": Screen = Convert.ToInt32(value); break;
case "gpu-api": GPUAPI = value; break;
}
}
static string _ConfigFolder;
public static string ConfigFolder {
get {
if (_ConfigFolder == null)
{
string portableFolder = Folder.Startup + @"portable_config\";
_ConfigFolder = portableFolder;
if (!Directory.Exists(_ConfigFolder))
_ConfigFolder = RegistryHelp.GetString(App.RegPath, "ConfigFolder");
if (!Directory.Exists(_ConfigFolder))
{
string appdataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\mpv.net\";
using (TaskDialog<string> td = new TaskDialog<string>())
{
td.MainInstruction = "Choose a settings folder.";
td.AddCommandLink(@"AppData\Roaming\mpv.net", appdataFolder, appdataFolder);
td.AddCommandLink(@"<startup>\portable_config", portableFolder, portableFolder);
td.AddCommandLink("Choose custom folder", "custom");
_ConfigFolder = td.Show();
}
if (_ConfigFolder == null)
{
_ConfigFolder = "";
return "";
}
if (_ConfigFolder == "custom")
{
using (var d = new WinForms.FolderBrowserDialog())
{
d.Description = "Choose a folder.";
if (d.ShowDialog() == WinForms.DialogResult.OK)
_ConfigFolder = d.SelectedPath + @"\";
else
_ConfigFolder = appdataFolder;
}
}
}
if (Folder.Startup == _ConfigFolder)
{
Msg.ShowError("Startup folder and config folder cannot be identical, using portable_config instead.");
_ConfigFolder = portableFolder;
}
if (!Directory.Exists(_ConfigFolder))
Directory.CreateDirectory(_ConfigFolder);
if (!_ConfigFolder.Contains("portable_config"))
RegistryHelp.SetValue(App.RegPath, "ConfigFolder", _ConfigFolder);
if (!File.Exists(_ConfigFolder + "input.conf"))
File.WriteAllText(_ConfigFolder + "input.conf", Properties.Resources.inputConf);
if (!File.Exists(_ConfigFolder + "mpv.conf"))
File.WriteAllText(_ConfigFolder + "mpv.conf", Properties.Resources.mpvConf);
}
return _ConfigFolder;
}
}
static Dictionary<string, string> _Conf;
public static Dictionary<string, string> Conf {
get {
if (_Conf == null)
{
_Conf = new Dictionary<string, string>();
if (File.Exists(ConfPath))
foreach (var i in File.ReadAllLines(ConfPath))
if (i.Contains("=") && !i.TrimStart().StartsWith("#"))
_Conf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
foreach (var i in _Conf)
ProcessProperty(i.Key, i.Value);
}
return _Conf;
}
}
public static void LoadMpvScripts()
{
if (Directory.Exists(Folder.Startup + "Scripts"))
{
string[] startupScripts = Directory.GetFiles(Folder.Startup + "Scripts");
foreach (string path in startupScripts)
if ((path.EndsWith(".lua") || path.EndsWith(".js")) && KnownScripts.Contains(Path.GetFileName(path)))
commandv("load-script", $"{path}");
}
}
public static string[] KnownScripts { get; } = { "osc-visibility.js", "show-playlist.js", "seek-show-position.py", "repl.lua" };
public static void LoadScripts()
{
if (Directory.Exists(Folder.Startup + "Scripts"))
{
foreach (string scriptPath in Directory.GetFiles(Folder.Startup + "Scripts"))
{
if (KnownScripts.Contains(Path.GetFileName(scriptPath)))
{
if (scriptPath.EndsWith(".py"))
Task.Run(() => PythonScripts.Add(new PythonScript(scriptPath)));
else if (scriptPath.EndsWith(".ps1"))
Task.Run(() => PowerShellScript.Init(scriptPath));
}
else
Msg.ShowError("Failed to load script", scriptPath + "\n\nOnly scripts that ship with mpv.net are allowed in <startup>\\scripts\n\nUser scripts have to use <config folder>\\scripts\n\nNever copy or install a new mpv.net version over a old mpv.net version.");
}
}
if (Directory.Exists(ConfigFolder + "scripts"))
foreach (string scriptPath in Directory.GetFiles(ConfigFolder + "scripts"))
if (scriptPath.EndsWith(".py"))
Task.Run(() => PythonScripts.Add(new PythonScript(scriptPath)));
else if (scriptPath.EndsWith(".ps1"))
Task.Run(() => PowerShellScript.Init(scriptPath));
}
public static void EventLoop()
{
while (true)
{
IntPtr ptr = mpv_wait_event(Handle, -1);
mpv_event evt = (mpv_event)Marshal.PtrToStructure(ptr, typeof(mpv_event));
if (WindowHandle == IntPtr.Zero)
WindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null);
// Debug.WriteLine(evt.event_id.ToString());
try
{
switch (evt.event_id)
{
case mpv_event_id.MPV_EVENT_SHUTDOWN:
IsQuitNeeded = false;
Shutdown?.Invoke();
WriteHistory(null);
ShutdownAutoResetEvent.Set();
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));
EndFileEventMode reason = (EndFileEventMode)end_fileData.reason;
EndFile?.Invoke(reason);
break;
case mpv_event_id.MPV_EVENT_FILE_LOADED:
HideLogo();
Duration = TimeSpan.FromSeconds(get_property_number("duration"));
Size vidSize = new Size(get_property_int("width"), get_property_int("height"));
if (vidSize.Width == 0 || vidSize.Height == 0)
vidSize = new Size(1, 1);
if (VideoSize != vidSize)
{
VideoSize = vidSize;
VideoSizeChanged?.Invoke();
}
VideoSizeAutoResetEvent.Set();
Task.Run(new Action(() => ReadMetaData()));
string path = mp.get_property_string("path");
if (path.Contains("://"))
path = mp.get_property_string("media-title");
WriteHistory(path);
FileLoaded?.Invoke();
break;
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();
ShowLogo();
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:
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);
if (args.Length > 1 && args[0] == "mpv.net")
Command.Execute(args[1], args.Skip(2).ToArray());
else if (args.Length > 0)
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 propData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
if (propData.format == mpv_format.MPV_FORMAT_FLAG)
{
lock (BoolPropChangeActions)
foreach (var i in BoolPropChangeActions)
if (i.Key== propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data) == 1);
}
else if (propData.format == mpv_format.MPV_FORMAT_STRING)
{
lock (StringPropChangeActions)
foreach (var i in StringPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(StringFromNativeUtf8(Marshal.PtrToStructure<IntPtr>(propData.data)));
}
else if(propData.format == mpv_format.MPV_FORMAT_INT64)
{
lock (IntPropChangeActions)
foreach (var i in IntPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data));
}
else if (propData.format == mpv_format.MPV_FORMAT_DOUBLE)
{
lock (DoublePropChangeActions)
foreach (var i in DoublePropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(Marshal.PtrToStructure<double>(propData.data));
}
break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
PlaybackRestart?.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;
}
}
catch (Exception ex)
{
Msg.ShowException(ex);
}
}
}
static void HideLogo()
{
command("overlay-remove 0");
IsLogoVisible = false;
}
static List<PythonEventObject> PythonEventObjects = new List<PythonEventObject>();
public static void register_event(string name, IronPython.Runtime.PythonFunction pyFunc)
{
foreach (var eventInfo in typeof(mp).GetEvents())
{
if (eventInfo.Name.ToLower() == name.Replace("-", ""))
{
PythonEventObject eventObject = new PythonEventObject();
PythonEventObjects.Add(eventObject);
eventObject.PythonFunction = pyFunc;
MethodInfo mi;
if (eventInfo.EventHandlerType == typeof(Action))
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.Invoke));
else if (eventInfo.EventHandlerType == typeof(Action<EndFileEventMode>))
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeEndFileEventMode));
else if (eventInfo.EventHandlerType == typeof(Action<string[]>))
mi = eventObject.GetType().GetMethod(nameof(PythonEventObject.InvokeStrings));
else
throw new Exception();
eventObject.EventInfo = eventInfo;
Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, eventObject, mi);
eventObject.Delegate = handler;
eventInfo.AddEventHandler(eventObject, handler);
break;
}
}
}
public static void unregister_event(IronPython.Runtime.PythonFunction pyFunc)
{
foreach (var eventObjects in PythonEventObjects)
if (eventObjects.PythonFunction == pyFunc)
eventObjects.EventInfo.RemoveEventHandler(eventObjects, eventObjects.Delegate);
}
public static void commandv(params string[] args)
{
if (Handle == IntPtr.Zero) return;
IntPtr mainPtr = AllocateUtf8IntPtrArrayWithSentinel(args, out IntPtr[] byteArrayPointers);
int err = mpv_command(Handle, mainPtr);
if (err < 0) throw new Exception($"{(mpv_error)err}");
foreach (var ptr in byteArrayPointers)
Marshal.FreeHGlobal(ptr);
Marshal.FreeHGlobal(mainPtr);
}
public static void command(string command, bool throwException = false)
{
if (Handle == IntPtr.Zero) return;
int err = mpv_command_string(Handle, command);
if (err < 0 && throwException) throw new Exception($"{(mpv_error)err}\n\n" + command);
}
public static void set_property_string(string name, string value, bool throwOnException = false)
{
byte[] bytes = GetUtf8Bytes(value);
int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, ref bytes);
if (err < 0 && throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
}
public static string get_property_string(string name, bool throwOnException = false)
{
try
{
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_STRING, out IntPtr lpBuffer);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
return "";
}
string ret = StringFromNativeUtf8(lpBuffer);
mpv_free(lpBuffer);
return ret;
}
catch (Exception e)
{
if (throwOnException) throw e;
return "";
}
}
public static int get_property_int(string name, bool throwOnException = false)
{
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, out IntPtr lpBuffer);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
return 0;
}
return lpBuffer.ToInt32();
}
public static double get_property_number(string name, bool throwOnException = false)
{
double val = 0;
int err = mpv_get_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_DOUBLE, ref val);
if (err < 0)
{
if (throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
return 0;
}
return val;
}
public static void set_property_int(string name, int value, bool throwOnException = false)
{
Int64 val = value;
int err = mpv_set_property(Handle, GetUtf8Bytes(name), mpv_format.MPV_FORMAT_INT64, ref val);
if (err < 0 && throwOnException) throw new Exception($"{name}: {(mpv_error)err}");
}
public static void observe_property_int(string name, Action<int> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_INT64);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
lock (IntPropChangeActions)
IntPropChangeActions.Add(new KeyValuePair<string, Action<int>>(name, action));
}
public static void observe_property_double(string name, Action<double> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_DOUBLE);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
lock (DoublePropChangeActions)
DoublePropChangeActions.Add(new KeyValuePair<string, Action<double>>(name, action));
}
public static void observe_property_bool(string name, Action<bool> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
lock (BoolPropChangeActions)
BoolPropChangeActions.Add(new KeyValuePair<string, Action<bool>>(name, action));
}
public static void observe_property_string(string name, Action<string> action)
{
int err = mpv_observe_property(Handle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_STRING);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
lock (StringPropChangeActions)
StringPropChangeActions.Add(new KeyValuePair<string, Action<string>>(name, action));
}
public static void ProcessCommandLine(bool preInit)
{
var args = Environment.GetCommandLineArgs().Skip(1);
//Msg.Show(string.Join("\n", args));
string[] preInitProperties = { "input-terminal", "terminal", "input-file", "config", "config-dir", "input-conf", "load-scripts", "scripts", "player-operation-mode" };
foreach (string i in args)
{
string arg = i;
if (arg.StartsWith("--"))
{
try
{
if (!arg.Contains("=")) arg += "=yes";
string left = arg.Substring(2, arg.IndexOf("=") - 2);
string right = arg.Substring(left.Length + 3);
if (left == "script") left = "scripts";
if (preInit && preInitProperties.Contains(left))
{
mp.ProcessProperty(left, right);
if (!App.ProcessProperty(left, right))
set_property_string(left, right, true);
}
else if (!preInit && !preInitProperties.Contains(left))
{
if (!PrintCommandLineArgument(arg))
{
mp.ProcessProperty(left, right);
if (!App.ProcessProperty(left, right))
set_property_string(left, right, true);
}
}
}
catch (Exception e)
{
Msg.ShowException(e);
}
}
}
if (!preInit)
{
List<string> files = new List<string>();
foreach (string i in args)
{
if (!i.StartsWith("--") && (i == "-" || i.Contains("://") ||
i.Contains(":\\") || i.StartsWith("\\\\") || File.Exists(i)))
{
files.Add(i);
}
}
Load(files.ToArray(), !App.Queue, Control.ModifierKeys.HasFlag(Keys.Control) || App.Queue);
if (files.Count == 0 || files[0].Contains("://"))
{
VideoSizeChanged?.Invoke();
VideoSizeAutoResetEvent.Set();
}
}
}
static bool PrintCommandLineArgument(string argument)
{
switch (argument)
{
case "--list-properties=yes":
var list = get_property_string("property-list").Split(',').ToList();
list.Sort();
Console.WriteLine(string.Join("\r\n", list.ToArray()));
return true;
}
return false;
}
public static DateTime LastLoad;
public static void Load(string[] files, bool loadFolder, bool append)
{
if (files is null || files.Length == 0)
return;
HideLogo();
if ((DateTime.Now - LastLoad).TotalMilliseconds < 1000)
append = true;
LastLoad = DateTime.Now;
for (int i = 0; i < files.Length; i++)
if (App.SubtitleTypes.Contains(files[i].ShortExt()))
commandv("sub-add", files[i]);
else
if (i == 0 && !append)
commandv("loadfile", files[i]);
else
commandv("loadfile", files[i], "append");
if (string.IsNullOrEmpty(get_property_string("path")))
set_property_int("playlist-pos", 0);
if (loadFolder && !append)
Task.Run(() => LoadFolder());
}
public static void LoadFolder()
{
if (!App.AutoLoadFolder || Control.ModifierKeys.HasFlag(Keys.Shift))
return;
Thread.Sleep(1000);
string path = get_property_string("path");
if (!File.Exists(path) || get_property_int("playlist-count") != 1)
return;
List<string> files = Directory.GetFiles(Path.GetDirectoryName(path)).ToList();
files = files.Where(file =>
App.VideoTypes.Contains(file.ShortExt()) ||
App.AudioTypes.Contains(file.ShortExt()) ||
App.ImageTypes.Contains(file.ShortExt())).ToList();
files.Sort(new StringLogicalComparer());
int index = files.IndexOf(path);
files.Remove(path);
foreach (string i in files)
commandv("loadfile", i, "append");
if (index > 0)
commandv("playlist-move", "0", (index + 1).ToString());
}
public static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
{
int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel)
byteArrayPointers = new IntPtr[numberOfStrings];
IntPtr rootPointer = Marshal.AllocCoTaskMem(IntPtr.Size * numberOfStrings);
for (int index = 0; index < arr.Length; index++)
{
var bytes = GetUtf8Bytes(arr[index]);
IntPtr unmanagedPointer = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length);
byteArrayPointers[index] = unmanagedPointer;
}
Marshal.Copy(byteArrayPointers, 0, rootPointer, numberOfStrings);
return rootPointer;
}
public static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr unmanagedStringArray, int StringCount)
{
IntPtr[] intPtrArray = new IntPtr[StringCount];
string[] stringArray = new string[StringCount];
Marshal.Copy(unmanagedStringArray, intPtrArray, 0, StringCount);
for (int i = 0; i < StringCount; i++)
stringArray[i] = StringFromNativeUtf8(intPtrArray[i]);
return stringArray;
}
public static string StringFromNativeUtf8(IntPtr nativeUtf8)
{
int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len;
byte[] buffer = new byte[len];
Marshal.Copy(nativeUtf8, buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
static string LastHistoryPath;
static DateTime LastHistoryStartDateTime;
static void WriteHistory(string path)
{
if (!File.Exists(ConfigFolder + "history.txt"))
return;
int totalMinutes = Convert.ToInt32((DateTime.Now - LastHistoryStartDateTime).TotalMinutes);
if (LastHistoryPath != null && totalMinutes > 1)
File.AppendAllText(ConfigFolder + "history.txt", DateTime.Now.ToString().Substring(0, 16) +
" " + totalMinutes.ToString().PadLeft(3) + " " + LastHistoryPath + "\r\n");
LastHistoryPath = path;
LastHistoryStartDateTime = DateTime.Now;
}
public static void ShowLogo()
{
if (MainForm.Instance is null) return;
Rectangle cr = MainForm.Instance.ClientRectangle;
int len = cr.Height / 5;
if (len == 0) return;
using (Bitmap b = new Bitmap(len, len))
{
using (Graphics g = Graphics.FromImage(b))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.Clear(Color.Black);
Rectangle rect = new Rectangle(0, 0, len, len);
g.DrawImage(Properties.Resources.mpvnet, rect);
BitmapData bd = b.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
int x = Convert.ToInt32((cr.Width - len) / 2.0);
int y = Convert.ToInt32(((cr.Height - len) / 2.0) * 0.9);
commandv("overlay-add", "0", $"{x}", $"{y}", "&" + bd.Scan0.ToInt64().ToString(), "0", "bgra", bd.Width.ToString(), bd.Height.ToString(), bd.Stride.ToString());
b.UnlockBits(bd);
IsLogoVisible = true;
}
}
}
static void ReadMetaData()
{
lock (MediaTracks)
{
MediaTracks.Clear();
string path = get_property_string("path");
if (File.Exists(path))
{
using (MediaInfo mi = new MediaInfo(path))
{
int count = mi.GetCount(MediaInfoStreamKind.Video);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetVideo(i, "Format"));
Add(track, mi.GetVideo(i, "Format_Profile"));
Add(track, mi.GetVideo(i, "Width") + "x" + mi.GetVideo(i, "Height"));
Add(track, mi.GetVideo(i, "FrameRate") + " FPS");
Add(track, mi.GetVideo(i, "Language/String"));
Add(track, mi.GetVideo(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetVideo(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetVideo(i, "Title"));
track.Text = "V: " + track.Text.Trim(' ', ',');
track.Type = "v";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = mi.GetCount(MediaInfoStreamKind.Audio);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetAudio(i, "Language/String"));
Add(track, mi.GetAudio(i, "Format"));
Add(track, mi.GetAudio(i, "Format_Profile"));
Add(track, mi.GetAudio(i, "BitRate/String"));
Add(track, mi.GetAudio(i, "Channel(s)/String"));
Add(track, mi.GetAudio(i, "SamplingRate/String"));
Add(track, mi.GetAudio(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetAudio(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetAudio(i, "Title"));
track.Text = "A: " + track.Text.Trim(' ', ',');
track.Type = "a";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = mi.GetCount(MediaInfoStreamKind.Text);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetText(i, "Language/String"));
Add(track, mi.GetText(i, "Format"));
Add(track, mi.GetText(i, "Format_Profile"));
Add(track, mi.GetText(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetText(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetText(i, "Title"));
track.Text = "S: " + track.Text.Trim(' ', ',');
track.Type = "s";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = get_property_int("edition-list/count");
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
track.Text = "E: " + get_property_string($"edition-list/{i}/title");
track.Type = "e";
track.ID = i;
MediaTracks.Add(track);
}
void Add(MediaTrack track, string val)
{
if (!string.IsNullOrEmpty(val) && !(track.Text != null && track.Text.Contains(val)))
track.Text += " " + val + ",";
}
}
}
}
lock (Chapters)
{
Chapters.Clear();
int count = get_property_int("chapter-list/count");
for (int x = 0; x < count; x++)
{
string text = get_property_string($"chapter-list/{x}/title");
double time = get_property_number($"chapter-list/{x}/time");
Chapters.Add(new KeyValuePair<string, double>(text, time));
}
}
}
}
public enum EndFileEventMode
{
Eof,
Stop,
Quit,
Error,
Redirect,
Unknown
}
}

View File

@@ -0,0 +1,39 @@
// This script creates context menu items dynamically.
using mpvnet;
using System.ComponentModel;
using System.Linq;
class Script
{
MainForm MainForm;
Core core;
public Script()
{
core = Core.core;
MainForm = mpvnet.MainForm.Instance;
MainForm.ContextMenu.Opening += ContextMenu_Opening;
}
void ContextMenu_Opening(object sender, CancelEventArgs e)
{
// edit input.conf and add 'Edition' menu item there
MenuItem menuItem = MainForm.FindMenuItem("Edition");
if (menuItem == null)
return;
menuItem.DropDownItems.Clear();
var editionTracks = core.MediaTracks.Where(track => track.Type == "e");
foreach (MediaTrack track in editionTracks)
{
MenuItem mi = new MenuItem(track.Text);
mi.Action = () => { core.commandv("set", "edition", track.ID.ToString()); };
mi.Checked = core.Edition == track.ID;
menuItem.DropDownItems.Add(mi);
}
}
}

View File

@@ -0,0 +1,29 @@
// This script adds a key binding.
using System.Reflection;
using mpvnet;
class Script
{
public Script()
{
string content = "ctrl+w script-message my-message-1 my-argument-1";
string sectionName = Assembly.GetExecutingAssembly().GetName().Name;
Core core = Core.core;
core.commandv("define-section", sectionName, content, "force");
core.commandv("enable-section", sectionName);
core.ClientMessage += ClientMessage;
}
void ClientMessage(string[] args)
{
switch (args[0])
{
case "my-message-1":
Msg.Show(args[1]);
break;
}
}
}

View File

@@ -0,0 +1,21 @@
// This script observes the fullscreen property and
// draws text on screen when the property changes.
using mpvnet;
class Script
{
Core core;
public Script()
{
core = Core.core;
core.observe_property_bool("fullscreen", FullscreenChange);
}
void FullscreenChange(bool value)
{
core.commandv("show-text", "fullscreen: " + value);
}
}

View File

@@ -0,0 +1,45 @@
// Pauses playback when window is minimized and resumes afterwards.
using System;
using System.Windows.Forms;
using mpvnet;
class Script
{
MainForm Form;
Core core;
bool WasPlaying;
bool WasPaused;
public Script()
{
core = Core.core;
Form = MainForm.Instance;
Form.Resize += Form_Resize;
}
private void Form_Resize(object sender, EventArgs e)
{
if (Form.WindowState == FormWindowState.Minimized)
{
WasPlaying = !core.get_property_bool("pause");
if (WasPlaying)
{
core.set_property_bool("pause", true, true);
WasPaused = true;
}
}
else
{
if (WasPaused)
{
core.set_property_bool("pause", false, true);
WasPaused = false;
}
}
}
}

View File

@@ -0,0 +1,35 @@
// When seeking displays position and duration like so: 70:00 / 80:00
// Which is different from most players which use: 01:10:00 / 01:20:00
// In input.conf set the input command prefix no-osd infront of the seek command.
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(_)
{
pos = mp.get_property_number("time-pos");
dur = mp.get_property_number("duration");
if (pos > dur)
pos = dur;
mp.commandv("show-text", format(pos) + " / " + format(dur));
}
mp.register_event("seek", on_seek);

View File

@@ -0,0 +1,26 @@
// This script shows the playlist.
function showPlaylist()
{
// set font size
mp.set_property_number("osd-font-size", 40);
// show playlist for 5 seconds
mp.command("show-text ${playlist} 5000");
// restore original font size in 6 seconds
setTimeout(resetFontSize, 6000);
}
// restore original font size
function resetFontSize()
{
mp.set_property_number("osd-font-size", size);
}
// save original font size
var size = mp.get_property_number("osd-font-size");
// input.conf: key script-binding show-playlist
mp.add_key_binding(null, "show-playlist", showPlaylist);

View File

@@ -0,0 +1,23 @@
-- https://github.com/mpv-player/mpv/blob/master/TOOLS/lua/pause-when-minimize.lua
-- This script pauses playback when minimizing the window, and resumes playback
-- if it's brought back again. If the player was already paused when minimizing,
-- then try not to mess with the pause state.
local did_minimize = false
mp.observe_property("window-minimized", "bool", function(name, value)
local pause = mp.get_property_native("pause")
if value == true then
if pause == false then
mp.set_property_native("pause", true)
did_minimize = true
end
elseif value == false then
if did_minimize and (pause == true) then
mp.set_property_native("pause", false)
end
did_minimize = false
end
end)

View File

@@ -0,0 +1,20 @@
# Shows the Open File dialog to open a file without loading its folder into the playlist.
# In input.conf add: <key> script-message load-without-folder
$code = {
if ($args[0] -eq 'load-without-folder')
{
$dialog = New-Object Windows.Forms.OpenFileDialog
if ($dialog.ShowDialog() -eq 'OK')
{
$core.LoadFiles($dialog.FileNames, $false, $false);
}
$dialog.Dispose()
}
}
$mp.register_event("client-message", $code)

View File

@@ -0,0 +1,25 @@
$code = {
$isMinimized = $args[0]
$isPaused = $mp.get_property_bool('pause')
if ($isMinimized)
{
if (-not $isPaused)
{
$mp.set_property_bool('pause', $true)
$script:wasPaused = $true
}
}
else
{
if ($script:wasPaused -and $isPaused)
{
$mp.set_property_bool('pause', $false)
}
$script:wasPaused = $false
}
}
$mp.observe_property('window-minimized', 'bool', $code)

View File

@@ -0,0 +1,14 @@
# Shows the current file in File Explorer
# In input.conf add: <key> script-message show-in-file-explorer
$code = {
if ($args[0] -eq 'show-in-file-explorer')
{
# probably works only with shell execute for which powershell has no built-in support
[Diagnostics.Process]::Start('explorer.exe', '/n, /select, "' + $mp.get_property_string('path') + '"')
}
}
$mp.register_event("client-message", $code)