Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57ae0d008b | ||
|
|
e128a4868b | ||
|
|
3875e1c1bf | ||
|
|
dd508907eb | ||
|
|
765854cd10 | ||
|
|
69fe7c091a | ||
|
|
45a2b7f976 | ||
|
|
a8cf3407e7 | ||
|
|
b8163c3c3e | ||
|
|
9d27465492 | ||
|
|
bba71c3782 | ||
|
|
eaa8a3ca6c | ||
|
|
0b28770d1a | ||
|
|
45d3386a88 | ||
|
|
8df330b7aa | ||
|
|
69857e0548 | ||
|
|
b1388e10e5 | ||
|
|
6f7f127279 | ||
|
|
18c8f5783b | ||
|
|
cdf3eb1373 | ||
|
|
e5bcd0fc29 | ||
|
|
b0dc44a98e | ||
|
|
41a14cd3b7 | ||
|
|
b38c7cf834 | ||
|
|
2ee4c1e647 | ||
|
|
2bcf97a97a | ||
|
|
8eaec77b0c | ||
|
|
3161ba1022 | ||
|
|
496bba71d4 | ||
|
|
3e1e822e56 | ||
|
|
b1ea6da39b | ||
|
|
8d54abae4c | ||
|
|
3583aa11ed | ||
|
|
642c36bacf | ||
|
|
0343b32847 | ||
|
|
6e2d11952f | ||
|
|
d0ad69656a | ||
|
|
62789fa036 | ||
|
|
a7fe444067 | ||
|
|
1ef4740b09 | ||
|
|
c5e445da17 | ||
|
|
a52de9039d | ||
|
|
8db8e497a5 | ||
|
|
28c9b2710c | ||
|
|
6ba9b1f05a | ||
|
|
ea5d3e64ee | ||
|
|
636d28ac54 | ||
|
|
6e54286a60 | ||
|
|
b396012934 | ||
|
|
f3909acdfe | ||
|
|
3ed71444a7 | ||
|
|
cab7c6d49b | ||
|
|
368641523a | ||
|
|
42c76d25c6 | ||
|
|
3968fa50f5 | ||
|
|
4d985c7b7c | ||
|
|
235a52a701 | ||
|
|
9a63b660de | ||
|
|
8eb0222873 | ||
|
|
bf8636135a | ||
|
|
33cdee5497 | ||
|
|
564d7b10a0 | ||
|
|
a1a408da1d | ||
|
|
85a3506403 | ||
|
|
e860cf52c4 | ||
|
|
32a46e47e2 | ||
|
|
538e91aaa9 | ||
|
|
94eaf8144f | ||
|
|
404a8f110d | ||
|
|
14f5228754 | ||
|
|
5ec2d1b436 | ||
|
|
32c6ce3a44 | ||
|
|
b86148dfb3 | ||
|
|
26e94fad2b | ||
|
|
c306eb032f | ||
|
|
09944583dd | ||
|
|
8f4ad52469 | ||
|
|
5f4eca21e5 | ||
|
|
b4632a0b6e | ||
|
|
c14a11170d | ||
|
|
723005b28f | ||
|
|
72f25a686e | ||
|
|
b1fb940759 | ||
|
|
1020cd70d1 | ||
|
|
50a2f13dbe | ||
|
|
b1ffb8291f | ||
|
|
dbc8ddaf2b | ||
|
|
b42cb6be05 | ||
|
|
b63363336f | ||
|
|
b0ef161ed9 | ||
|
|
7d31237d5f | ||
|
|
3705e2abbf | ||
|
|
7d73529c83 | ||
|
|
2e59d49ad1 | ||
|
|
f61eef8961 | ||
|
|
efb4c085d5 | ||
|
|
4a045d6a91 | ||
|
|
da1a78cfa3 | ||
|
|
5b3697fbde | ||
|
|
726d759589 | ||
|
|
ed614332f7 | ||
|
|
9d46064dca | ||
|
|
407b49b3ad | ||
|
|
0e92d4ec0c | ||
|
|
4f96835e19 | ||
|
|
56d954d94e | ||
|
|
a8aeb1e3e9 | ||
|
|
415fd266a2 | ||
|
|
51f9e17380 | ||
|
|
28045ad33e | ||
|
|
e1bd44cd00 | ||
|
|
27d6026213 | ||
|
|
c67d5f10f8 | ||
|
|
5f73be07e8 | ||
|
|
0cad09d2bb | ||
|
|
54b2612bb8 | ||
|
|
82b4d19282 | ||
|
|
23a587c819 | ||
|
|
d69eecd494 | ||
|
|
0d4cc59f9a | ||
|
|
a394536a8c | ||
|
|
ebcd20ceb5 | ||
|
|
964c2f4dd2 | ||
|
|
e985047a54 | ||
|
|
bafc09a948 | ||
|
|
14a172b78b | ||
|
|
2eb324bf41 | ||
|
|
afe9ad5128 | ||
|
|
cc657d4ec2 | ||
|
|
1270dd474e | ||
|
|
e9ea70838d | ||
|
|
e7fde60150 | ||
|
|
2028113d58 | ||
|
|
4d55423fc7 | ||
|
|
d82874409d | ||
|
|
930ba2b0e7 | ||
|
|
4743f365a1 | ||
|
|
194ff5d62b | ||
|
|
5a726a01e0 | ||
|
|
fee13353ec | ||
|
|
cd3543fff4 | ||
|
|
c54f240346 | ||
|
|
7f55ad908a | ||
|
|
d586a82e37 | ||
|
|
722e06a02b | ||
|
|
2f76d2b775 | ||
|
|
7519977124 | ||
|
|
2e65b9ceb8 | ||
|
|
04bffca206 | ||
|
|
ff5a164c69 | ||
|
|
26b391a8d9 | ||
|
|
32eed33c82 | ||
|
|
639893f2e7 | ||
|
|
42b0cc1a64 | ||
|
|
6f7fa6c9d6 | ||
|
|
e6fb775dcd | ||
|
|
ece15ca9bb | ||
|
|
6b3c2788df | ||
|
|
067bdd20d7 | ||
|
|
97ce08c4c7 | ||
|
|
fbb9cbdc33 | ||
|
|
815adca198 | ||
|
|
ce3d5792ad | ||
|
|
74fc0b3ed7 | ||
|
|
604a1e8499 | ||
|
|
56ade0a0d1 | ||
|
|
6294a96aea | ||
|
|
9a95953a04 | ||
|
|
8d9c0c9092 | ||
|
|
e399ca672f | ||
|
|
660345d18a | ||
|
|
3844db6e79 | ||
|
|
5374e20757 | ||
|
|
bc6167c4ee | ||
|
|
7979f77284 | ||
|
|
71b90ed5cb | ||
|
|
9d2e236b3e | ||
|
|
d42657a3f0 | ||
|
|
e4818fae18 | ||
|
|
7b39bfcbc4 | ||
|
|
fd6f3c94dd | ||
|
|
dd10c07fb5 | ||
|
|
3bbfdcffdd | ||
|
|
6b7c4e1724 | ||
|
|
d3ad1091ec | ||
|
|
65177c448b | ||
|
|
1cdecb3810 | ||
|
|
20ea755a93 | ||
|
|
3b7bc95869 | ||
|
|
5380daad53 | ||
|
|
a5f4d848d4 | ||
|
|
cf0f9bfa01 | ||
|
|
68461e05da | ||
|
|
397b988851 | ||
|
|
eddb95e791 |
10
.github/ISSUE_TEMPLATE/ask-a-question.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
name: Ask a question
|
||||||
|
about: Ask a question about mpv.net
|
||||||
|
title: ''
|
||||||
|
labels: question
|
||||||
|
assignees: stax76
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This template is meant for usage questions of mpv.net.
|
||||||
27
.github/ISSUE_TEMPLATE/report-a-bug.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
name: Report a bug
|
||||||
|
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.
|
||||||
20
.github/ISSUE_TEMPLATE/request-a-feature.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Request a feature
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: feature request
|
||||||
|
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.
|
||||||
890
Manual.md
@@ -1,890 +0,0 @@
|
|||||||
# mpv.net manual
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
|
|
||||||
* [About mpv.net](#about-mpvnet)
|
|
||||||
+ [Target Audience](#target-audience)
|
|
||||||
* [Requirements](#requirements)
|
|
||||||
* [Installation](#installation)
|
|
||||||
+ [File Associations](#file-associations)
|
|
||||||
* [Context Menu](#context-menu)
|
|
||||||
+ [Open > Open Files](#open--open-files)
|
|
||||||
+ [Open > Open URL](#open--open-url)
|
|
||||||
+ [Open > Show media search](#open--show-media-search)
|
|
||||||
+ [Open > Load external audio files](#open--load-external-audio-files)
|
|
||||||
+ [Open > Load external subtitle files](#open--load-external-subtitle-files)
|
|
||||||
+ [Play/Pause](#play-pause)
|
|
||||||
+ [Stop](#stop)
|
|
||||||
+ [Toggle Fullscreen](#toggle-fullscreen)
|
|
||||||
+ [Navigate > Previous File](#navigate--previous-file)
|
|
||||||
+ [Navigate > Next File](#navigate--next-file)
|
|
||||||
+ [Navigate > Next Chapter](#navigate--next-chapter)
|
|
||||||
+ [Navigate > Previous Chapter](#navigate--previous-chapter)
|
|
||||||
+ [Navigate > Jump Next Frame](#navigate--jump-next-frame)
|
|
||||||
+ [Navigate > Jump Previous Frame](#navigate--jump-previous-frame)
|
|
||||||
+ [Navigate > Jump](#navigate--jump)
|
|
||||||
+ [Pan & Scan > Increase Size](#pan--scan--increase-size)
|
|
||||||
+ [Pan & Scan > Decrease Size](#pan--scan--decrease-size)
|
|
||||||
+ [Pan & Scan > Move Left](#pan--scan--move-left)
|
|
||||||
+ [Pan & Scan > Move Right](#pan--scan--move-right)
|
|
||||||
+ [Pan & Scan > Move Up](#pan--scan--move-up)
|
|
||||||
+ [Pan & Scan > Move Down](#pan--scan--move-down)
|
|
||||||
+ [Pan & Scan > Decrease Height](#pan--scan--decrease-height)
|
|
||||||
+ [Pan & Scan > Increase Height](#pan--scan--increase-height)
|
|
||||||
+ [Pan & Scan > Reset](#pan--scan--reset)
|
|
||||||
+ [Video > Decrease Contrast](#video--decrease-contrast)
|
|
||||||
+ [Video > Increase Contrast](#video--increase-contrast)
|
|
||||||
+ [Video > Decrease Brightness](#video--decrease-brightness)
|
|
||||||
+ [Video > Increase Brightness](#video--increase-brightness)
|
|
||||||
+ [Video > Decrease Gamma](#video--decrease-gamma)
|
|
||||||
+ [Video > Increase Gamma](#video--increase-gamma)
|
|
||||||
+ [Video > Decrease Saturation](#video--decrease-saturation)
|
|
||||||
+ [Video > Increase Saturation](#video--increase-saturation)
|
|
||||||
+ [Video > Take Screenshot](#video--take-screenshot)
|
|
||||||
+ [Video > Toggle Deinterlace](#video--toggle-deinterlace)
|
|
||||||
+ [Video > Cycle Aspect Ratio](#video--cycle-aspect-ratio)
|
|
||||||
+ [Audio > Cycle/Next](#audio--cycle-next)
|
|
||||||
+ [Audio > Delay +0.1](#audio--delay--01)
|
|
||||||
+ [Audio > Delay -0.1](#audio--delay--01)
|
|
||||||
+ [Subtitle > Cycle/Next](#subtitle--cycle-next)
|
|
||||||
+ [Subtitle > Toggle Visibility](#subtitle--toggle-visibility)
|
|
||||||
+ [Subtitle > Delay -0.1](#subtitle--delay--01)
|
|
||||||
+ [Subtitle > Delay 0.1](#subtitle--delay-01)
|
|
||||||
+ [Subtitle > Move Up](#subtitle--move-up)
|
|
||||||
+ [Subtitle > Move Down](#subtitle--move-down)
|
|
||||||
+ [Subtitle > Decrease Subtitle Font Size](#subtitle--decrease-subtitle-font-size)
|
|
||||||
+ [Subtitle > Increase Subtitle Font Size](#subtitle--increase-subtitle-font-size)
|
|
||||||
+ [Volume > Up](#volume--up)
|
|
||||||
+ [Volume > Down](#volume--down)
|
|
||||||
+ [Volume > Mute](#volume--mute)
|
|
||||||
+ [Speed > -10%](#speed---10-)
|
|
||||||
+ [Speed > 10%](#speed--10-)
|
|
||||||
+ [Speed > Half](#speed--half)
|
|
||||||
+ [Speed > Double](#speed--double)
|
|
||||||
+ [Speed > Reset](#speed--reset)
|
|
||||||
+ [Extensions > Rating > 0stars](#extensions--rating--0stars)
|
|
||||||
+ [View > On Top > Enable](#view--on-top--enable)
|
|
||||||
+ [View > On Top > Disable](#view--on-top--disable)
|
|
||||||
+ [View > File Info](#view--file-info)
|
|
||||||
+ [View > Show Statistics](#view--show-statistics)
|
|
||||||
+ [View > Toggle Statistics](#view--toggle-statistics)
|
|
||||||
+ [View > Toggle OSC Visibility](#view--toggle-osc-visibility)
|
|
||||||
+ [View > Show Playlist](#view--show-playlist)
|
|
||||||
+ [View > Show Audio/Video/Subtitle List](#view--show-audio-video-subtitle-list)
|
|
||||||
+ [Settings > Show Config Editor](#settings--show-config-editor)
|
|
||||||
+ [Settings > Show Input Editor](#settings--show-input-editor)
|
|
||||||
+ [Settings > Open Config Folder](#settings--open-config-folder)
|
|
||||||
+ [Tools > Command Palette](#tools--command-palette)
|
|
||||||
+ [Tools > Show History](#tools--show-history)
|
|
||||||
+ [Tools > Set/clear A-B loop points](#tools--set-clear-a-b-loop-points)
|
|
||||||
+ [Tools > Toggle infinite file looping](#tools--toggle-infinite-file-looping)
|
|
||||||
+ [Tools > Cycle Hardware Decoding](#tools--cycle-hardware-decoding)
|
|
||||||
+ [Tools > Execute mpv command](#tools--execute-mpv-command)
|
|
||||||
+ [Tools > Manage File Associations](#tools--manage-file-associations)
|
|
||||||
+ [Help > Show mpv manual](#help--show-mpv-manual)
|
|
||||||
+ [Help > Show mpv default keys](#help--show-mpv-default-keys)
|
|
||||||
+ [Help > Show mpv.net default keys](#help--show-mpvnet-default-keys)
|
|
||||||
+ [Help > Show mpv.net manual](#help--show-mpvnet-manual)
|
|
||||||
+ [Help > About mpv.net](#help--about-mpvnet)
|
|
||||||
+ [Exit](#exit)
|
|
||||||
+ [Exit Watch Later](#exit-watch-later)
|
|
||||||
* [External Tools](#external-tools)
|
|
||||||
+ [Play with mpv](#play-with-mpv)
|
|
||||||
+ [External Application Button](#external-application-button)
|
|
||||||
+ [Open with++](#open-with)
|
|
||||||
+ [MediaInfo.NET](#mediainfonet)
|
|
||||||
* [Command Line Interface](#command-line-interface)
|
|
||||||
* [Color Theme](#color-theme)
|
|
||||||
* [Hidden and secret features](#hidden-and-secret-features)
|
|
||||||
|
|
||||||
## About mpv.net
|
|
||||||
|
|
||||||
mpv.net is a modern media player for Windows. mpv is similar to VLC not based on DirectShow like MPC, mpv.net is based on libmpv which in return is based on ffmpeg.
|
|
||||||
|
|
||||||
libmpv provides the majority of the features of the mpv media player, a fork of mplayer. mpv focuses on the usage of the command line interface, mpv.net retains the ability to be used from the command line and adds a modern GUI on top of it.
|
|
||||||
|
|
||||||
mpv.net is meant to be a small single person project, it's designed to be mpv compatible, can use the same settings as mpv and offers almost all mpv features because they are all contained in libmpv, this means the official [mpv manual](https://mpv.io/manual/master/) fully applies to mpv.net.
|
|
||||||
|
|
||||||
### Target Audience
|
|
||||||
|
|
||||||
The target audience of mpv.net are programmers, nerds and software enthusiasts that need something more advanced than typical media players.
|
|
||||||
|
|
||||||
Furthermore mpv.net is well suited for people who are interested to learn mpv.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
mpv.net is based on the .NET Framework and requires Windows 7 as minimum version of the Windows operating system. As of the writing of this manual the minimum required .NET Framework version is 4.8.
|
|
||||||
|
|
||||||
For optimal results a modern graphics card is recommended.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
mpv.net is available as setup and as portable download in the 7zip and zip archive format, to unpack the portable download 7zip can be used, it is available at www.7-zip.org.
|
|
||||||
|
|
||||||
If you are new to mpv.net the portable download is recommended, for regular mpv.net users the setup is typically more easy to use.
|
|
||||||
|
|
||||||
The x64 editions require more memory and have the advantage of decoders being typically first and better optimized for x64.
|
|
||||||
|
|
||||||
x86 should though still be well supported and work but it's not as well tested than x64.
|
|
||||||
|
|
||||||
### File Associations
|
|
||||||
|
|
||||||
File Associations can be created using the setup or with the context menu under 'Tools > OS Setup'.
|
|
||||||
|
|
||||||
Windows 10 prevents apps to register as the default app, to define the default video or audio player app in Windows 10 go to the Windows settings under 'Settings > Apps > Default apps' or shell execute 'ms-settings:defaultapps'.
|
|
||||||
|
|
||||||
It's also possible to change the default application using the 'Open with' feature of the context menu in Windows File Explorer.
|
|
||||||
|
|
||||||
## Context Menu
|
|
||||||
|
|
||||||
The most important part of the user interface in mpv.net is the context menu, the menu can be customized as it's defined in the same file where the key bindings are defined.
|
|
||||||
|
|
||||||
### Open > Open Files
|
|
||||||
|
|
||||||
The Open Files menu entry is one way to open files in mpv.net, it supports multi selection.
|
|
||||||
|
|
||||||
Another way to open files is the command line, it is used by the Windows File Explorer if file associations exist.
|
|
||||||
|
|
||||||
When mpv.net is started from a terminal such as PowerShell, mpv.net attaches to the terminal and outputs status and debug messages.
|
|
||||||
|
|
||||||
A third way is to drag and drop files on the main window.
|
|
||||||
|
|
||||||
Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist. This works in all mpv.net features that open files or URLs.
|
|
||||||
|
|
||||||
Pressing the shift key while opening a single file will suppress loading all files in the folder.
|
|
||||||
|
|
||||||
### Open > Open URL
|
|
||||||
|
|
||||||
The Open URL menu entry can be used to open URLs for example from YouTube.
|
|
||||||
|
|
||||||
mpv.net monitors the Windows clipboard and ask if URLs should be played in case it finds a URL in the clipboard. This feature uses a keyword whitelist that can be configured in the config editor.
|
|
||||||
|
|
||||||
When mpv.net is started from a terminal such as PowerShell, mpv.net attaches to the terminal and outputs status and debug messages.
|
|
||||||
|
|
||||||
Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist. This works in all mpv.net features that open files or URLs.
|
|
||||||
|
|
||||||
### Open > Show media search
|
|
||||||
|
|
||||||
mpv.net supports system wide media searches using the Everything indexing service installed by the popular file search tool Everything (www.voidtools.com).
|
|
||||||
|
|
||||||
### Open > Load external audio files
|
|
||||||
|
|
||||||
Allows to load an external audio file. It's also possible to auto detect external audio files based on the file name, the option for this can be found in the settings under 'Settings > Show Config Editor > Audio > audio-file-auto'.
|
|
||||||
|
|
||||||
### Open > Load external subtitle files
|
|
||||||
|
|
||||||
Allows to load an external subtitle file. It's also possible to auto detect external subtitle files based on the file name, the option for this can be found in the settings under 'Settings > Show Config Editor > Subtitles > sub-auto'.
|
|
||||||
|
|
||||||
### Play/Pause
|
|
||||||
|
|
||||||
Play/Pause using the command:
|
|
||||||
|
|
||||||
`cycle pause`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[pause property](https://mpv.io/manual/master/#options-pause)
|
|
||||||
|
|
||||||
### Stop
|
|
||||||
|
|
||||||
Stops the player and unloads the playlist using the command:
|
|
||||||
|
|
||||||
`stop`
|
|
||||||
|
|
||||||
[stop command](https://mpv.io/manual/master/#command-interface-stop)
|
|
||||||
|
|
||||||
### Toggle Fullscreen
|
|
||||||
|
|
||||||
Toggles fullscreen using the command:
|
|
||||||
|
|
||||||
`cycle fullscreen`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[fullscreen property](https://mpv.io/manual/master/#options-fs)
|
|
||||||
|
|
||||||
### Navigate > Previous File
|
|
||||||
|
|
||||||
Navigates to the previous file in the playlist using the command:
|
|
||||||
|
|
||||||
`playlist-prev`
|
|
||||||
|
|
||||||
[playlist-prev command](https://mpv.io/manual/master/#command-interface-playlist-prev)
|
|
||||||
|
|
||||||
### Navigate > Next File
|
|
||||||
|
|
||||||
Navigates to the next file in the playlist using the command:
|
|
||||||
|
|
||||||
`playlist-next`
|
|
||||||
|
|
||||||
[playlist-next command](https://mpv.io/manual/master/#command-interface-playlist-next)
|
|
||||||
|
|
||||||
### Navigate > Next Chapter
|
|
||||||
|
|
||||||
Navigates to the next chapter using the command:
|
|
||||||
|
|
||||||
`add chapter 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[chapter property](https://mpv.io/manual/master/#command-interface-chapter)
|
|
||||||
|
|
||||||
### Navigate > Previous Chapter
|
|
||||||
|
|
||||||
Navigates to the previous chapter using the command:
|
|
||||||
|
|
||||||
`add chapter -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[chapter property](https://mpv.io/manual/master/#command-interface-chapter)
|
|
||||||
|
|
||||||
### Navigate > Jump Next Frame
|
|
||||||
|
|
||||||
Jumps to the next frame using the command:
|
|
||||||
|
|
||||||
`frame-step`
|
|
||||||
|
|
||||||
[frame-step command](https://mpv.io/manual/master/#command-interface-frame-step)
|
|
||||||
|
|
||||||
### Navigate > Jump Previous Frame
|
|
||||||
|
|
||||||
Jumps to the previous frame using the command:
|
|
||||||
|
|
||||||
`frame-back-step`
|
|
||||||
|
|
||||||
[frame-back-step command](https://mpv.io/manual/master/#command-interface-frame-back-step)
|
|
||||||
|
|
||||||
### Navigate > Jump
|
|
||||||
|
|
||||||
Seeking using the command:
|
|
||||||
|
|
||||||
`no-osd seek sec`
|
|
||||||
|
|
||||||
sec is the relative amount of seconds to jump, the no-osd prefix
|
|
||||||
is used because mpv.net includes a script that shows the position
|
|
||||||
when a seek operation is performed, the script uses a more simple
|
|
||||||
time format.
|
|
||||||
|
|
||||||
[no-osd command prefix](https://mpv.io/manual/master/#command-interface-no-osd)
|
|
||||||
|
|
||||||
[seek command](https://mpv.io/manual/master/#command-interface-[relative|absolute|absolute-percent|relative-percent|exact|keyframes])
|
|
||||||
|
|
||||||
### Pan & Scan > Increase Size
|
|
||||||
|
|
||||||
Adds video zoom using the command:
|
|
||||||
|
|
||||||
`add video-zoom 0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-zoom property](https://mpv.io/manual/master/#options-video-zoom)
|
|
||||||
|
|
||||||
### Pan & Scan > Decrease Size
|
|
||||||
|
|
||||||
Adds negative video zoom using the command:
|
|
||||||
|
|
||||||
`add video-zoom -0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-zoom property](https://mpv.io/manual/master/#options-video-zoom)
|
|
||||||
|
|
||||||
### Pan & Scan > Move Left
|
|
||||||
|
|
||||||
`add video-pan-x -0.01`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-pan-x, video-pan-y property](https://mpv.io/manual/master/#options-video-pan-y)
|
|
||||||
|
|
||||||
### Pan & Scan > Move Right
|
|
||||||
|
|
||||||
`add video-pan-x 0.01`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-pan-x, video-pan-y property](https://mpv.io/manual/master/#options-video-pan-y)
|
|
||||||
|
|
||||||
### Pan & Scan > Move Up
|
|
||||||
|
|
||||||
`add video-pan-y -0.01`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-pan-x, video-pan-y property](https://mpv.io/manual/master/#options-video-pan-y)
|
|
||||||
|
|
||||||
### Pan & Scan > Move Down
|
|
||||||
|
|
||||||
`add video-pan-y 0.01`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[video-pan-x, video-pan-y property](https://mpv.io/manual/master/#options-video-pan-y)
|
|
||||||
|
|
||||||
### Pan & Scan > Decrease Height
|
|
||||||
|
|
||||||
`add panscan -0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[panscan property](https://mpv.io/manual/master/#options-panscan)
|
|
||||||
|
|
||||||
### Pan & Scan > Increase Height
|
|
||||||
|
|
||||||
`add panscan 0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[panscan property](https://mpv.io/manual/master/#options-panscan)
|
|
||||||
|
|
||||||
### Pan & Scan > Reset
|
|
||||||
|
|
||||||
Resets Pan & Scan, multiple commands in the same line are separated with semicolon.
|
|
||||||
|
|
||||||
`set video-zoom 0; set video-pan-x 0; set video-pan-y 0`
|
|
||||||
|
|
||||||
[video-zoom property](https://mpv.io/manual/master/#options-video-zoom)
|
|
||||||
|
|
||||||
[video-pan-x, video-pan-y property](https://mpv.io/manual/master/#options-video-pan-y)
|
|
||||||
|
|
||||||
### Video > Decrease Contrast
|
|
||||||
|
|
||||||
Decreases contrast with the following command:
|
|
||||||
|
|
||||||
`add contrast -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[contrast property](https://mpv.io/manual/master/#options-contrast)
|
|
||||||
|
|
||||||
### Video > Increase Contrast
|
|
||||||
|
|
||||||
Increases contrast with the following command:
|
|
||||||
|
|
||||||
`add contrast 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[contrast property](https://mpv.io/manual/master/#options-contrast)
|
|
||||||
|
|
||||||
### Video > Decrease Brightness
|
|
||||||
|
|
||||||
Decreases brightness using the following command:
|
|
||||||
|
|
||||||
`add brightness -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[brightness property](https://mpv.io/manual/master/#options-brightness)
|
|
||||||
|
|
||||||
### Video > Increase Brightness
|
|
||||||
|
|
||||||
Increases brightness using the following command:
|
|
||||||
|
|
||||||
`add brightness 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[brightness property](https://mpv.io/manual/master/#options-brightness)
|
|
||||||
|
|
||||||
### Video > Decrease Gamma
|
|
||||||
|
|
||||||
Decreases gamma using the following command:
|
|
||||||
|
|
||||||
`add gamma -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[gamma property](https://mpv.io/manual/master/#options-gamma)
|
|
||||||
|
|
||||||
### Video > Increase Gamma
|
|
||||||
|
|
||||||
Increases gamma using the following command:
|
|
||||||
|
|
||||||
`add gamma 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[gamma property](https://mpv.io/manual/master/#options-gamma)
|
|
||||||
|
|
||||||
### Video > Decrease Saturation
|
|
||||||
|
|
||||||
Decreases saturation using the following command:
|
|
||||||
|
|
||||||
`add saturation -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[saturation property](https://mpv.io/manual/master/#options-saturation)
|
|
||||||
|
|
||||||
### Video > Increase Saturation
|
|
||||||
|
|
||||||
Increases saturation using the following command:
|
|
||||||
|
|
||||||
`add saturation 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[saturation property](https://mpv.io/manual/master/#options-saturation)
|
|
||||||
|
|
||||||
### Video > Take Screenshot
|
|
||||||
|
|
||||||
`async screenshot`
|
|
||||||
|
|
||||||
[async command prefix](https://mpv.io/manual/master/#command-interface-async)
|
|
||||||
|
|
||||||
[screenshot command](https://mpv.io/manual/master/#command-interface-[subtitles|video|window|single|each-frame])
|
|
||||||
|
|
||||||
### Video > Toggle Deinterlace
|
|
||||||
|
|
||||||
Cycles the deinterlace property using the following command:
|
|
||||||
|
|
||||||
`cycle deinterlace`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[deinterlace property](https://mpv.io/manual/master/#options-deinterlace)
|
|
||||||
|
|
||||||
### Video > Cycle Aspect Ratio
|
|
||||||
|
|
||||||
Cycles the aspect ratio using the following command:
|
|
||||||
|
|
||||||
`cycle-values video-aspect "16:9" "4:3" "2.35:1" "-1"`
|
|
||||||
|
|
||||||
[cycle-values command](https://mpv.io/manual/master/#command-interface-cycle-values)
|
|
||||||
|
|
||||||
[video-aspect property](https://mpv.io/manual/master/#options-video-aspect)
|
|
||||||
|
|
||||||
### Audio > Cycle/Next
|
|
||||||
|
|
||||||
This uses a mpv.net command that shows better info then the mpv preset
|
|
||||||
and also has the advantage of not showing no audio.
|
|
||||||
|
|
||||||
### Audio > Delay +0.1
|
|
||||||
|
|
||||||
Adds a audio delay using the following command:
|
|
||||||
|
|
||||||
`add audio-delay 0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[audio-delay property](https://mpv.io/manual/master/#options-audio-delay)
|
|
||||||
|
|
||||||
### Audio > Delay -0.1
|
|
||||||
|
|
||||||
Adds a negative audio delay using the following command:
|
|
||||||
|
|
||||||
`add audio-delay -0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[audio-delay property](https://mpv.io/manual/master/#options-audio-delay)
|
|
||||||
|
|
||||||
### Subtitle > Cycle/Next
|
|
||||||
|
|
||||||
Shows the next subtitle track using the following command:
|
|
||||||
|
|
||||||
`cycle sub`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[sub/sid property](https://mpv.io/manual/master/#options-sid)
|
|
||||||
|
|
||||||
### Subtitle > Toggle Visibility
|
|
||||||
|
|
||||||
Cycles the subtitle visibility using the following command:
|
|
||||||
|
|
||||||
`cycle sub-visibility`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[sub-visibility property](https://mpv.io/manual/master/#options-no-sub-visibility)
|
|
||||||
|
|
||||||
### Subtitle > Delay -0.1
|
|
||||||
|
|
||||||
Adds a negative subtitle delay using the following command:
|
|
||||||
|
|
||||||
`add sub-delay -0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-delay property](https://mpv.io/manual/master/#options-sub-delay)
|
|
||||||
|
|
||||||
### Subtitle > Delay 0.1
|
|
||||||
|
|
||||||
Adds a positive subtitle delay using the following command:
|
|
||||||
|
|
||||||
`add sub-delay 0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-delay property](https://mpv.io/manual/master/#options-sub-delay)
|
|
||||||
|
|
||||||
### Subtitle > Move Up
|
|
||||||
|
|
||||||
Moves the subtitle up using the following command:
|
|
||||||
|
|
||||||
`add sub-pos -1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-pos property](https://mpv.io/manual/master/#options-sub-pos)
|
|
||||||
|
|
||||||
### Subtitle > Move Down
|
|
||||||
|
|
||||||
Moves the subtitle down using the following command:
|
|
||||||
|
|
||||||
`add sub-pos 1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-pos property](https://mpv.io/manual/master/#options-sub-pos)
|
|
||||||
|
|
||||||
### Subtitle > Decrease Subtitle Font Size
|
|
||||||
|
|
||||||
Decreases the subtitle font size using the following command:
|
|
||||||
|
|
||||||
`add sub-scale -0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-scale property](https://mpv.io/manual/master/#options-sub-scale)
|
|
||||||
|
|
||||||
### Subtitle > Increase Subtitle Font Size
|
|
||||||
|
|
||||||
Increases the subtitle font size using the following command:
|
|
||||||
|
|
||||||
`add sub-scale 0.1`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[sub-scale property](https://mpv.io/manual/master/#options-sub-scale)
|
|
||||||
|
|
||||||
### Volume > Up
|
|
||||||
|
|
||||||
Increases the volume using the following command:
|
|
||||||
|
|
||||||
`add volume 10`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[volume property](https://mpv.io/manual/master/#options-volume)
|
|
||||||
|
|
||||||
### Volume > Down
|
|
||||||
|
|
||||||
Decreases the volume using the following command:
|
|
||||||
|
|
||||||
`add volume -10`
|
|
||||||
|
|
||||||
[add command](https://mpv.io/manual/master/#command-interface-add-%3Cproperty%3E-[%3Cvalue%3E])
|
|
||||||
|
|
||||||
[volume property](https://mpv.io/manual/master/#options-volume)
|
|
||||||
|
|
||||||
### Volume > Mute
|
|
||||||
|
|
||||||
Cycles the mute property using the following command:
|
|
||||||
|
|
||||||
`cycle mute`
|
|
||||||
|
|
||||||
[cycle command](https://mpv.io/manual/master/#command-interface-cycle-%3Cproperty%3E-[up|down])
|
|
||||||
|
|
||||||
[mute property](https://mpv.io/manual/master/#options-mute)
|
|
||||||
|
|
||||||
### Speed > -10%
|
|
||||||
|
|
||||||
Decreases the speed by 10% using the following command:
|
|
||||||
|
|
||||||
`multiply speed 1/1.1`
|
|
||||||
|
|
||||||
[multiply command](https://mpv.io/manual/master/#command-interface-multiply-%3Cproperty%3E-%3Cfactor%3E)
|
|
||||||
|
|
||||||
[speed property](https://mpv.io/manual/master/#options-speed)
|
|
||||||
|
|
||||||
### Speed > 10%
|
|
||||||
|
|
||||||
Increases the speed by 10% using the following command:
|
|
||||||
|
|
||||||
`multiply speed 1.1`
|
|
||||||
|
|
||||||
[multiply command](https://mpv.io/manual/master/#command-interface-multiply-%3Cproperty%3E-%3Cfactor%3E)
|
|
||||||
|
|
||||||
[speed property](https://mpv.io/manual/master/#options-speed)
|
|
||||||
|
|
||||||
### Speed > Half
|
|
||||||
|
|
||||||
Halfs the speed using the following command:
|
|
||||||
|
|
||||||
`multiply speed 0.5`
|
|
||||||
|
|
||||||
[multiply command](https://mpv.io/manual/master/#command-interface-multiply-%3Cproperty%3E-%3Cfactor%3E)
|
|
||||||
|
|
||||||
[speed property](https://mpv.io/manual/master/#options-speed)
|
|
||||||
|
|
||||||
### Speed > Double
|
|
||||||
|
|
||||||
Doubles the speed using the following command:
|
|
||||||
|
|
||||||
`multiply speed 2`
|
|
||||||
|
|
||||||
[multiply command](https://mpv.io/manual/master/#command-interface-multiply-%3Cproperty%3E-%3Cfactor%3E)
|
|
||||||
|
|
||||||
[speed property](https://mpv.io/manual/master/#options-speed)
|
|
||||||
|
|
||||||
### Speed > Reset
|
|
||||||
|
|
||||||
Resets the speed using the following command:
|
|
||||||
|
|
||||||
`set speed 1`
|
|
||||||
|
|
||||||
[set command](https://mpv.io/manual/master/#command-interface-set-%3Cproperty%3E-%22%3Cvalue%3E%22)
|
|
||||||
|
|
||||||
[speed property](https://mpv.io/manual/master/#options-speed)
|
|
||||||
|
|
||||||
### Extensions > Rating > 0stars
|
|
||||||
|
|
||||||
A plugin the writes the rating to the filename.
|
|
||||||
|
|
||||||
### View > On Top > Enable
|
|
||||||
|
|
||||||
Forces the player to stay on top of other windows using the following command:
|
|
||||||
|
|
||||||
`set ontop yes`
|
|
||||||
|
|
||||||
[set command](https://mpv.io/manual/master/#command-interface-set-%3Cproperty%3E-%22%3Cvalue%3E%22)
|
|
||||||
|
|
||||||
[ontop property](https://mpv.io/manual/master/#options-ontop)
|
|
||||||
|
|
||||||
### View > On Top > Disable
|
|
||||||
|
|
||||||
Disables the player to stay on top of other windows using the following command:
|
|
||||||
|
|
||||||
`set ontop no`
|
|
||||||
|
|
||||||
[set command](https://mpv.io/manual/master/#command-interface-set-%3Cproperty%3E-%22%3Cvalue%3E%22)
|
|
||||||
|
|
||||||
[ontop property](https://mpv.io/manual/master/#options-ontop)
|
|
||||||
|
|
||||||
### View > File Info
|
|
||||||
|
|
||||||
Shows info using a mpv.net command about the current file, shows length, position, formats, size and filename.
|
|
||||||
|
|
||||||
### View > Show Statistics
|
|
||||||
|
|
||||||
Show statistics using the following command:
|
|
||||||
|
|
||||||
`script-binding stats/display-stats`
|
|
||||||
|
|
||||||
[script-binding command](https://mpv.io/manual/master/#command-interface-script-binding)
|
|
||||||
|
|
||||||
### View > Toggle Statistics
|
|
||||||
|
|
||||||
Toggles statistics using the following command:
|
|
||||||
|
|
||||||
`script-binding stats/display-stats-toggle`
|
|
||||||
|
|
||||||
[script-binding command](https://mpv.io/manual/master/#command-interface-script-binding)
|
|
||||||
|
|
||||||
### View > Toggle OSC Visibility
|
|
||||||
|
|
||||||
Toggles OSC Visibility using the following command:
|
|
||||||
|
|
||||||
`script-binding osc/visibility`
|
|
||||||
|
|
||||||
[script-binding command](https://mpv.io/manual/master/#command-interface-script-binding)
|
|
||||||
|
|
||||||
### View > Show Playlist
|
|
||||||
|
|
||||||
Shows the playlist for 5 seconds using the following command:
|
|
||||||
|
|
||||||
`show-text ${playlist} 5000`
|
|
||||||
|
|
||||||
[show-text command](https://mpv.io/manual/master/#command-interface-show-text)
|
|
||||||
|
|
||||||
### View > Show Audio/Video/Subtitle List
|
|
||||||
|
|
||||||
Shows the Audio/Video/Subtitle list for 5 seconds using the following command:
|
|
||||||
|
|
||||||
`show-text ${track-list} 5000`
|
|
||||||
|
|
||||||
[show-text command](https://mpv.io/manual/master/#command-interface-show-text)
|
|
||||||
|
|
||||||
### Settings > Show Config Editor
|
|
||||||
|
|
||||||
Shows mpv.net's config editor.
|
|
||||||
|
|
||||||
### Settings > Show Input Editor
|
|
||||||
|
|
||||||
Shows mpv.net's key binding editor.
|
|
||||||
|
|
||||||
### Settings > Open Config Folder
|
|
||||||
|
|
||||||
Opens the config folder which contains:
|
|
||||||
|
|
||||||
mpv.conf file containing mpv settings
|
|
||||||
|
|
||||||
mpvnet.conf file containing mpv.net settings
|
|
||||||
|
|
||||||
input.conf containing mpv key bindings
|
|
||||||
|
|
||||||
User scripts and user extensions
|
|
||||||
|
|
||||||
### Tools > Command Palette
|
|
||||||
|
|
||||||
Shows the command palette window which allows to find mpv.net commands, it shows the command's location in the context menu and the key shortcut.
|
|
||||||
|
|
||||||
### Tools > Show History
|
|
||||||
|
|
||||||
Shows a text file that contains the file history. If the file don't exist it asks if the file should be created in the settings folder. Once the file exist then the history is logged. It logges the playback history containing the time and filename.
|
|
||||||
|
|
||||||
### Tools > Set/clear A-B loop points
|
|
||||||
|
|
||||||
Enables to set loop start and end points using the following command:
|
|
||||||
|
|
||||||
`ab-loop`
|
|
||||||
|
|
||||||
[ab-loop command](https://mpv.io/manual/master/#command-interface-ab-loop)
|
|
||||||
|
|
||||||
### Tools > Toggle infinite file looping
|
|
||||||
|
|
||||||
Loops the current file infinitely using the following command:
|
|
||||||
|
|
||||||
cycle-values loop-file "inf" "no"
|
|
||||||
|
|
||||||
[cycle-values command](https://mpv.io/manual/master/#command-interface-cycle-values)
|
|
||||||
|
|
||||||
[loop-file command](https://mpv.io/manual/master/#options-loop)
|
|
||||||
|
|
||||||
### Tools > Cycle Hardware Decoding
|
|
||||||
|
|
||||||
Cycles the hwdec property to enable/disable hardware decoding using the following command:
|
|
||||||
|
|
||||||
`cycle-values hwdec "auto" "no"`
|
|
||||||
|
|
||||||
[cycle-values command](https://mpv.io/manual/master/#command-interface-cycle-values)
|
|
||||||
|
|
||||||
[hwdec property](https://mpv.io/manual/master/#options-hwdec)
|
|
||||||
|
|
||||||
### Tools > Execute mpv command
|
|
||||||
|
|
||||||
Allows to execute mpv commands.
|
|
||||||
|
|
||||||
### Tools > Manage File Associations
|
|
||||||
|
|
||||||
Allows to manage file associations.
|
|
||||||
|
|
||||||
### Help > Show mpv manual
|
|
||||||
|
|
||||||
Shows the [mpv manual](https://mpv.io/manual/stable/).
|
|
||||||
|
|
||||||
### Help > Show mpv default keys
|
|
||||||
|
|
||||||
Shows the [mpv default keys](https://github.com/mpv-player/mpv/blob/master/etc/input.conf)
|
|
||||||
|
|
||||||
### Help > Show mpv.net default keys
|
|
||||||
|
|
||||||
Shows the [mpv.net default keys](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt)
|
|
||||||
|
|
||||||
Help > Show mpv.net web site
|
|
||||||
|
|
||||||
Shows the [mpv.net web site](https://mpv-net.github.io/mpv.net-web-site/)
|
|
||||||
|
|
||||||
### Help > Show mpv.net manual
|
|
||||||
|
|
||||||
Shows the [mpv.net manual](https://github.com/stax76/mpv.net/blob/master/Manual.md)
|
|
||||||
|
|
||||||
### Help > About mpv.net
|
|
||||||
|
|
||||||
Shows the mpv.net about dialog.
|
|
||||||
|
|
||||||
### Exit
|
|
||||||
|
|
||||||
Exits mpv.net using the following command:
|
|
||||||
|
|
||||||
`quit`
|
|
||||||
|
|
||||||
[quit command](https://mpv.io/manual/master/#command-interface-quit-[%3Ccode%3E])
|
|
||||||
|
|
||||||
### Exit Watch Later
|
|
||||||
|
|
||||||
Exits mpv.net and remembers the position in the file using the following command:
|
|
||||||
|
|
||||||
`quit-watch-later`
|
|
||||||
|
|
||||||
[quit-watch-later command](https://mpv.io/manual/master/#command-interface-quit-watch-later)
|
|
||||||
|
|
||||||
## External Tools
|
|
||||||
|
|
||||||
### Play with mpv
|
|
||||||
|
|
||||||
In order to play videos from sites such as YouTube the Chrome Extension [Play with mpv](https://chrome.google.com/webstore/detail/play-with-mpv/hahklcmnfgffdlchjigehabfbiigleji) can be used.
|
|
||||||
|
|
||||||
Due to Chrome Extensions not being able to start a app, another app that communicates with the extension is required, this app can be downloaded [here](http://www.mediafire.com/file/lezj8lwqt5zf75v/play-with-mpvnet-server.7z/file). The extension works only when the app is running, to have the app always running a link can be created in the auto start folder located at:
|
|
||||||
|
|
||||||
`C:\Users\%username%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
|
|
||||||
|
|
||||||
This will start the app on system start and have it running in the background. When the file association registration of mpv.net was executed then the app should find the location of mpv.net, alternativly the mpv.net folder can be added to the PATH environment variable.
|
|
||||||
|
|
||||||
### External Application Button
|
|
||||||
|
|
||||||
Alternatively the Chrome extension [External Application Button](https://github.com/stax76/mpv.net/issues/106) can be used.
|
|
||||||
|
|
||||||
### Open with++
|
|
||||||
|
|
||||||
Open with++ allows to customize the File Explorer context menu for instance to add a 'Add to mpv.net playlist' menu item.
|
|
||||||
|
|
||||||
https://github.com/stax76/OpenWithPlusPlus
|
|
||||||
|
|
||||||
https://github.com/stax76/OpenWithPlusPlus#mpvnet
|
|
||||||
|
|
||||||
https://github.com/stax76/OpenWithPlusPlus#add-to-mpvnet-playlist
|
|
||||||
|
|
||||||
### MediaInfo.NET
|
|
||||||
|
|
||||||
MediaInfo.NET is a media info GUI.
|
|
||||||
|
|
||||||
https://github.com/stax76/MediaInfo.NET
|
|
||||||
|
|
||||||
To start a external application mpv has the run input command (it does not use the shell).
|
|
||||||
|
|
||||||
If the path has spaces then it must be enclosed in quotes and then double backslashes must be used for escaping or alternatively forward slashes.
|
|
||||||
|
|
||||||
`_ run D:\Software\MediaInfoNET.exe "${path}" #menu: Tools > Open file with MediaInfo.NET`
|
|
||||||
|
|
||||||
`_ run "D:\\Soft ware\\MediaInfoNET.exe" "${path}" #menu: Tools > Open file with MediaInfo.NET`
|
|
||||||
|
|
||||||
`_ run "D:/Soft ware/MediaInfoNET.exe" "${path}" #menu: Tools > Open file with MediaInfo.NET`
|
|
||||||
|
|
||||||
## Command Line Interface
|
|
||||||
|
|
||||||
mpvnet implements a command line interface to set mpv commands.
|
|
||||||
|
|
||||||
Supported are all mpv properties which are documented at:
|
|
||||||
|
|
||||||
<https://mpv.io/manual/master>
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
`mpvnet --mute=yes <file|URL>`
|
|
||||||
|
|
||||||
## Color Theme
|
|
||||||
|
|
||||||
mpv.net supports custom color themes, the definition of the built-in themes can be found at:
|
|
||||||
|
|
||||||
<https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/theme.txt>
|
|
||||||
|
|
||||||
Custom themes can be saved at:
|
|
||||||
|
|
||||||
`<conf folder>\theme.conf`
|
|
||||||
|
|
||||||
The theme.conf file may contain an unlimited amount of themes.
|
|
||||||
|
|
||||||
In the config editor under UI there are the settings dark-theme and light-theme to define the themes used in dark and in light mode.
|
|
||||||
|
|
||||||
## Hidden and secret features
|
|
||||||
|
|
||||||
Selecting multiple files in File Explorer and pressing enter will open the files in mpv.net. Explorer restricts this to maximum 15 files and the order will be random.
|
|
||||||
|
|
||||||
Whenever the control key is pressed when files or URLs are opened, the playlist is not cleared but the files or URLs are appended to the playlist. This works in all mpv.net features that open files or URLs.
|
|
||||||
|
|
||||||
Pressing the shift key while opening a single file will suppress loading all files in the folder.
|
|
||||||
284
README.md
@@ -1,263 +1,149 @@
|
|||||||

|
|
||||||
|
|
||||||
     [](https://www.paypal.me/stax76)
|

|
||||||
|
|
||||||
# 🎞 mpv.net
|
    
|
||||||
|
|
||||||
mpv.net is a modern media player for Windows that works just like [mpv](https://mpv.io).
|
🎞 mpv.net
|
||||||
|
==========
|
||||||
|
|
||||||
#### Based on libmpv
|
mpv.net is a modern media player for Windows based on the popular [mpv](https://mpv.io) player.
|
||||||
|
|
||||||
|
|
||||||
|
#### Graphical User Interface
|
||||||
|
|
||||||
|
Modern GUI with customizable color themes.
|
||||||
|
|
||||||
mpv.net is based on libmpv which offers a straightforward C API that was designed from the ground up to make mpv usable as a library and facilitate easy integration into other applications. mpv is like vlc not based on DirectShow or Media Foundation.
|
|
||||||
|
|
||||||
#### Command Line Interface
|
#### Command Line Interface
|
||||||
|
|
||||||
mpv.net has the [CLI of mpv](https://mpv.io/manual/master/#options).
|
mpv.net supports mpvs property based command line switches.
|
||||||
|
|
||||||
|
|
||||||
#### High quality video output
|
#### 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
|
#### 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
|
#### 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
|
#### 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.
|
||||||
|
|
||||||
|
|
||||||
Table of contents
|
Table of contents
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Screenshots](#screenshots)
|
|
||||||
- [Manual](#manual)
|
|
||||||
- [Context Menu](#context-menu)
|
|
||||||
- [Settings](#settings)
|
|
||||||
- [Scripting](#scripting)
|
|
||||||
- [Extensions](#extensions)
|
|
||||||
- [Architecture](#architecture)
|
|
||||||
- [Support](#support)
|
- [Support](#support)
|
||||||
- [Links](#links)
|
|
||||||
- [Changelog](#changelog)
|
|
||||||
- [Download](#download)
|
- [Download](#download)
|
||||||
|
|
||||||
### 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
|
|
||||||
- Searchable command palette to quickly launch commands and look for keys ([Screenshot](#command-palette-screenshot))
|
|
||||||
- Modern WPF based graphical user interface with dark mode ([Screenshot](#config-editor-screenshot))
|
|
||||||
- Extension API for .NET languages
|
|
||||||
- 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)
|
|
||||||
- [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 Explorer using the shell extension [Open with++](https://github.com/stax76/OpenWithPlusPlus#add-to-mpvnet-playlist) and the --queue switch
|
|
||||||
- [Manual](#manual)
|
- [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
|
||||||
|
- Global hotkeys
|
||||||
|
- 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 installer, portable and Chocolatey
|
||||||
|
- Build-in media streaming (requires youtube-dl being installed)
|
||||||
|
- 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
|
||||||
|
|
||||||
#### Context Menu Screenshot
|
|
||||||
|
|
||||||
Context menu defined in the input.conf file with dark mode support.
|
## [Support](docs/Manual.md#support)
|
||||||
|
|
||||||

|
[Support section of the manual.](docs/Manual.md#support)
|
||||||
|
|
||||||
#### Config Editor Screenshot
|
|
||||||
|
|
||||||
A searchable config editor as alternative to edit the mpv.conf file manually.
|
## [Download](docs/Manual.md#download)
|
||||||
|
|
||||||

|
[Download section of the manual.](docs/Manual.md#download)
|
||||||
|
|
||||||
#### Terminal and REPL Screenshot
|
|
||||||
|
|
||||||
mpv.net attached to a PowerShell terminal showing the [OSD REPL](https://github.com/rossy/mpv-repl).
|
## [Manual](docs/Manual.md)
|
||||||
|
|
||||||

|
[The mpv.net documentation.](docs/Manual.md)
|
||||||
|
|
||||||
#### Input Editor Screenshot
|
|
||||||
|
|
||||||
A searchable key and mouse binding editor.
|
Screenshots
|
||||||
|
-----------
|
||||||
|
|
||||||

|
#### Main Window
|
||||||
|
|
||||||
#### 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.
|
|
||||||
|
|
||||||

|
#### Context Menu
|
||||||
|
|
||||||
#### Media Search Screenshot
|
Context menu using dark mode.
|
||||||
|
|
||||||
Media search feature powered by [Everything](https://www.voidtools.com) to find and play media.
|

|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Manual
|
#### Config Editor
|
||||||
|
|
||||||
[Manual](Manual.md)
|
Searchable config editor as alternative to edit the conf file manually.
|
||||||
|
|
||||||
### Context Menu
|

|
||||||
|
|
||||||
The context menu can be customized via input.conf file located in the config directory:
|
|
||||||
|
|
||||||
```Text
|
#### Terminal
|
||||||
C:\Users\%username%\AppData\Roaming\mpv\input.conf
|
|
||||||
```
|
|
||||||
|
|
||||||
if it's missing mpv.net generates it with the following defaults:
|
OSD console and status printed on the terminal.
|
||||||
|
|
||||||
<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
|
#### Input Editor
|
||||||
|
|
||||||
When mpv.net finds no config folder on startup it will ask for a location.
|
Searchable key and mouse binding editor.
|
||||||
|
|
||||||
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
|
#### Command Palette
|
||||||
mpv.net generates it with the following defaults:
|
|
||||||
|
|
||||||
<https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt>
|
Command Palette to find commands and shortcut keys easily.
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
### Download
|
|
||||||
|
|
||||||
<https://github.com/stax76/mpv.net/releases>
|
|
||||||
|
|||||||
@@ -1,4 +1,313 @@
|
|||||||
|
|
||||||
|
5.4.9.0 (2021-05-29)
|
||||||
|
====================
|
||||||
|
|
||||||
|
- `window-scale 1` does not work correctly in mpv,
|
||||||
|
so I've removed support for it and added my own implementation:
|
||||||
|
`script-message mpv.net window-scale`.
|
||||||
|
- The CS-Script library was replaced with my own C# scripting implementation.
|
||||||
|
- If a player window border is near to a screen border and the window size
|
||||||
|
changes, the player windows sticks to that near screen border location.
|
||||||
|
Furthermore the `remember-window-position` option remembers a near screen
|
||||||
|
border position instead of remembering the window center position.
|
||||||
|
- Multi monitor fix using different DPI values.
|
||||||
|
- `start-size` option has new options, see config editor and manual.
|
||||||
|
- Improved `script-message mpv.net cycle-audio` OSD info.
|
||||||
|
- The logic for finding the config directory has changed, see manual.
|
||||||
|
- The dotnet script and extension host was a little redesigned, this breaks
|
||||||
|
backward compatibility unfortunately, but I can help fixing existing
|
||||||
|
open source code however. Example scripts and extensions were updated.
|
||||||
|
- Fix console not working due to incorrect mpv.conf value generated
|
||||||
|
(script-opts=console-scale=0).
|
||||||
|
- Settings are stored in the file settings.xml now instead of the Registry.
|
||||||
|
- Video rotation support added.
|
||||||
|
- After using the config editor it's no longer necessary to restart mpv.net.
|
||||||
|
- Improved input editor theming.
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.8 Beta (2021-05-09)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
- Improved window scaling.
|
||||||
|
- Title property implementation.
|
||||||
|
- Command palette shows commands without assigned menu item.
|
||||||
|
- The code from the included JavaScript file was ported into the core
|
||||||
|
player because JavaScript is currently broken in the builds of shinshiro.
|
||||||
|
- New option `--command=<input command>`, can be used in combination
|
||||||
|
with `process-instance=single` to control mpv.net via command line,
|
||||||
|
for instance to create global hotkeys with AutoHotkey.
|
||||||
|
- New global hotkey feature added using the file global-input.conf.
|
||||||
|
- The global-media-keys option was removed because global-input.conf
|
||||||
|
can be used instead.
|
||||||
|
- MediaInfo 21.3
|
||||||
|
- libmpv shinchiro 2021-04-04
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.7 Beta (2021-03-09)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
- History feature can be configured to ingore defined strings:
|
||||||
|
script-opt = history-discard=value1;value2
|
||||||
|
- Web stream audio and subtitle track selection.
|
||||||
|
- On Windows 10 1903 and later the default code page was changed to UTF-8.
|
||||||
|
- Support of --version command.
|
||||||
|
- File associations and auto-load-folder can be customized with
|
||||||
|
video-file-extensions, audio-file-extensions and image-file-extensions.
|
||||||
|
- Fix menu not showing key '&'.
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.6 Beta (2020-12-24)
|
||||||
|
=========================
|
||||||
|
|
||||||
|
- Santa hat shown in december. (#216)
|
||||||
|
- Filename not being always shown in title bar. (#214)
|
||||||
|
- Improved OSD message when cycling audio.
|
||||||
|
- DVD audio and subtitle track selection.
|
||||||
|
- Input keylist can be shown on the command-line with --input-keylist
|
||||||
|
- Profiles can be shown on the command-line with --profile=help
|
||||||
|
- Decoders can be shown on the command-line with --ad=help and --vd=help
|
||||||
|
- Decoders can be shown in the menu under: View > Show Decoders
|
||||||
|
- Demuxers can be shown in the menu under: View > Show Demuxers
|
||||||
|
- Audio devices can be shown in the menu under: View > Show Audio Devices
|
||||||
|
- Audio devices can be shown on the command-line with --audio-device=help
|
||||||
|
- Protocols can be shown in the menu under: View > Show Protocols
|
||||||
|
- Allow mpv.net to start from different startup locations
|
||||||
|
without showing the setup dialog. (#218)
|
||||||
|
- libmpv updated to shinshiro 2020-12-20
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.5 Beta
|
||||||
|
============
|
||||||
|
|
||||||
|
- Load AviSynth DLL from environment variable AviSynthDLL
|
||||||
|
in order to support AviSynth portable mode.
|
||||||
|
- New option global-media-keys (next, previous, play/pause, stop).
|
||||||
|
- Redesigned PowerShell based setup and setup dialog, it was done
|
||||||
|
because of a bug and upcoming chocolately and winget integration.
|
||||||
|
- Redesigned auto update using different file name, using zip instead
|
||||||
|
of 7zip format now, 7zip distribution will shortly get dropped.
|
||||||
|
- Whenever there is a new startup location, the setup dialog is shown.
|
||||||
|
- libmpv updated to shinshiro 2020-12-06
|
||||||
|
- youtube-dl is no longer included, users have to install it
|
||||||
|
on their own (put it in the startup or a PATH folder).
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.4 Beta
|
||||||
|
============
|
||||||
|
|
||||||
|
- Blu-rays with dozens of titles showed all titles in the menu
|
||||||
|
which was difficult to choose and extremely slow.
|
||||||
|
- Blu-ray folder paths are auto detected when received
|
||||||
|
from drag & drop and command-line.
|
||||||
|
- Cycle audio was not working for Blu-ray.
|
||||||
|
- Message asking if image is BD or DVD in case image is < 10 GB.
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.3 Beta
|
||||||
|
============
|
||||||
|
|
||||||
|
- Support Wheel_Left and Wheel_Right. (arnesacnussem)
|
||||||
|
- Default screen was changed from primary to OS default to start
|
||||||
|
from the same screen where the File Explorer window is located.
|
||||||
|
- When mpv.net was started maximized and next was invoked then the
|
||||||
|
window state was changed to normal instead of staying maximized.
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.2 Beta
|
||||||
|
============
|
||||||
|
|
||||||
|
- CLI --shuffle issue fix.
|
||||||
|
- ISO image files can be opened like any other files, files smaller
|
||||||
|
than 10 GB are handled as DVD and larger as BluRay.
|
||||||
|
- Menu support for BluRay title, audio and subtitle selection.
|
||||||
|
- Property input-key-list can be shown with: View > Show Keys
|
||||||
|
- youtube-dl 2020.06.16.1
|
||||||
|
- libmpv 2020-06-21 shinchiro
|
||||||
|
|
||||||
|
|
||||||
|
5.4.8.1 Beta
|
||||||
|
============
|
||||||
|
|
||||||
|
- fix stdin support
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
- new: readme/website and manual were improved
|
||||||
|
- new: source code includes now the release script to build the archives and setup files
|
||||||
|
- new: the history feature now uses the full path
|
||||||
|
- new: install via Scoop and Chocolatey added to readme/website (Restia666Ashdoll)
|
||||||
|
- 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/input.conf.txt))
|
||||||
|
|
||||||
|
- update: libmpv shinchiro 2019-11-10
|
||||||
|
|
||||||
|
- fix: often the OSC was shown when fullscreen was entered
|
||||||
|
or on app startup, this is now suppressed
|
||||||
|
- fix: the file association routine uses no longer 'Play with mpv.net' for the
|
||||||
|
default open verb caption because it doesn't support multi selection,
|
||||||
|
it shows now only Open, the manual explains how to get multi selection
|
||||||
|
in File Explorer, read about it [here](https://github.com/stax76/mpv.net/blob/master/Manual.md#open-with)
|
||||||
|
- fix: x86 builds had an older version included because
|
||||||
|
of a misconfiguration in the solution file
|
||||||
|
|
||||||
### 5.4.3.0
|
### 5.4.3.0
|
||||||
|
|
||||||
- new: the color themes can now be customized ([manual](https://github.com/stax76/mpv.net/blob/master/Manual.md#color-theme))
|
- new: the color themes can now be customized ([manual](https://github.com/stax76/mpv.net/blob/master/Manual.md#color-theme))
|
||||||
@@ -25,7 +334,7 @@
|
|||||||
context menu item in explorer with multi selection support use my
|
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
|
[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
|
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 config editor keeps profiles and comments in mpv.conf intact!
|
||||||
- the options in the config editor are better organized
|
- the options in the config editor are better organized
|
||||||
|
|
||||||
@@ -92,19 +401,18 @@
|
|||||||
- libmpv was updated to shinchiro 2019-08-04
|
- libmpv was updated to shinchiro 2019-08-04
|
||||||
- in case mpv.net was started from a terminal it sets now the mpv property input-terminal to yes,
|
- in case mpv.net was started from a terminal it sets now the mpv property input-terminal to yes,
|
||||||
this means mpv.net will now receive and handle input keys from the terminal
|
this means mpv.net will now receive and handle input keys from the terminal
|
||||||
- certain command line properties didn't work (input-terminal, terminal, input-file,
|
- certain command-line properties didn't work (input-terminal, terminal, input-file,
|
||||||
config, config-dir, input-conf, load-scripts, script, scripts, player-operation-mode)
|
config, config-dir, input-conf, load-scripts, script, scripts, player-operation-mode)
|
||||||
- the about dialog shows now the mpv version and build date
|
- the about dialog shows now the mpv version and build date
|
||||||
- the dialog that asks for a config folder has now a cancel option
|
- the dialog that asks for a config folder has now a cancel option
|
||||||
|
|
||||||
### 5.1
|
### 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
|
- many [wiki pages](https://github.com/stax76/mpv.net/wiki) were improved
|
||||||
- the logo/icon had a very small cosmetic change
|
- the logo/icon had a very small cosmetic change
|
||||||
- the help in the context menu was improved,
|
- the help in the context menu was improved,
|
||||||
for quick access consider the command palette (F1 key)
|
for quick access consider the command palette (F1 key)
|
||||||
- config options specific to mpv.net are now available from the command line
|
- config options specific to mpv.net are now available from the command-line
|
||||||
- the input editor no longer has known limitations, 'alt gr' and ctrl+alt are working now
|
- the input editor no longer has known limitations, 'alt gr' and ctrl+alt are working now
|
||||||
- the help in the input editor was simplified and the filter logic was improved
|
- the help in the input editor was simplified and the filter logic was improved
|
||||||
- fixed issue in file associations causing mpv.net not to appear in OS default apps
|
- fixed issue in file associations causing mpv.net not to appear in OS default apps
|
||||||
@@ -120,10 +428,10 @@
|
|||||||
- the search field in the config editor was not always remembered
|
- the search field in the config editor was not always remembered
|
||||||
- new setting remember-volume added, saves volume and mute on exit
|
- new setting remember-volume added, saves volume and mute on exit
|
||||||
and restores it on start.
|
and restores it on start.
|
||||||
- it's now enforced that mpv properties on the command line and in
|
- it's now enforced that mpv properties on the command-line and in
|
||||||
the mpv.conf config file are lowercase, if not a error is shown
|
the mpv.conf config file are lowercase, if not a error is shown
|
||||||
- gpu-api vulkan was not working if media files were opened via
|
- gpu-api vulkan was not working if media files were opened via
|
||||||
command line (that included Explorer)
|
command-line (that included Explorer)
|
||||||
- new setting minimum-aspect-ratio added, minimum aspect ratio for the window,
|
- new setting minimum-aspect-ratio added, minimum aspect ratio for the window,
|
||||||
this was previously hard coded to 1.3
|
this was previously hard coded to 1.3
|
||||||
- new setting auto-load-folder added, for single files automatically load
|
- new setting auto-load-folder added, for single files automatically load
|
||||||
@@ -219,11 +527,11 @@
|
|||||||
- fix for middle mouse button not working
|
- fix for middle mouse button not working
|
||||||
- fix of logo overlay using a huge amount of memory (thx for the [ghacks article](https://www.ghacks.net/2019/07/05/a-look-at-mpv-net-a-mpv-frontend-with-everything-integration/))
|
- fix of logo overlay using a huge amount of memory (thx for the [ghacks article](https://www.ghacks.net/2019/07/05/a-look-at-mpv-net-a-mpv-frontend-with-everything-integration/))
|
||||||
- fix config dialog showing a message about app restart without reason
|
- fix config dialog showing a message about app restart without reason
|
||||||
- when multiple files are selected in Windows File Explorer and enter is
|
- when multiple files are selected in File Explorer and enter is
|
||||||
pressed, the files are opened as selected, the order is random though
|
pressed, the files are opened as selected, the order is random though
|
||||||
because Explorer starts multiple mpv.net processes concurrently
|
because Explorer starts multiple mpv.net processes concurrently
|
||||||
- libmpv was updated to shinchiro 2019-06-30
|
- 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
|
- 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
|
the window size will use a aspect ratio of 1.8
|
||||||
- new JavaScript script osc-visibility.js included in the distribution
|
- new JavaScript script osc-visibility.js included in the distribution
|
||||||
@@ -234,11 +542,11 @@
|
|||||||
|
|
||||||
- opening a URL manually no longer uses a input box but uses the clipboard directly
|
- opening a URL manually no longer uses a input box but uses the clipboard directly
|
||||||
- the manifest was missing the company attribute which caused
|
- the manifest was missing the company attribute which caused
|
||||||
mpv.net not appearing in the 'Open with' menu of the Windows File Explorer,
|
mpv.net not appearing in the 'Open with' menu of the File Explorer,
|
||||||
thanks to 44vince44 for pointing this out!!!
|
thanks to 44vince44 for pointing this out!!!
|
||||||
- new Python and C# script examples were added to the wiki and the scripting and
|
- new Python and C# script examples were added to the wiki and the scripting and
|
||||||
extension documentation was improved
|
extension documentation was improved
|
||||||
- invalid command line arguments were ignored, now an error message is shown
|
- invalid command-line arguments were ignored, now an error message is shown
|
||||||
- a description on how to start mpv.net from Google Chrome was added to the
|
- a description on how to start mpv.net from Google Chrome was added to the
|
||||||
manual, it's useful to play videos from sites like YouTube, find the
|
manual, it's useful to play videos from sites like YouTube, find the
|
||||||
description [here](https://github.com/stax76/mpv.net/blob/master/Manual.md#chrome-extension)
|
description [here](https://github.com/stax76/mpv.net/blob/master/Manual.md#chrome-extension)
|
||||||
@@ -262,7 +570,7 @@
|
|||||||
- the default key binding of the Everything media search was changed to F3
|
- 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
|
- 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
|
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
|
### 4.3.1
|
||||||
|
|
||||||
@@ -277,14 +585,14 @@
|
|||||||
- the help and layout in the config editor was improved
|
- the help and layout in the config editor was improved
|
||||||
- clipboard monitoring for URLs can be disabled in the settings
|
- clipboard monitoring for URLs can be disabled in the settings
|
||||||
- the context menu has a new feature: Open > Add files to playlist,
|
- 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
|
- a setting was added to force using a single mpv.net process instance
|
||||||
|
|
||||||
### 4.1
|
### 4.1
|
||||||
|
|
||||||
- drag & drop support for subtitles was added
|
- drag & drop support for subtitles was added
|
||||||
- libmpv was updated
|
- libmpv was updated
|
||||||
- command line support for stdin and URLs was added
|
- command-line support for stdin and URLs was added
|
||||||
- there was a crash happening when the player is
|
- there was a crash happening when the player is
|
||||||
minimized in the taskbar
|
minimized in the taskbar
|
||||||
|
|
||||||
@@ -297,7 +605,7 @@
|
|||||||
- the info command (i key) shows the audio format
|
- the info command (i key) shows the audio format
|
||||||
- new options osd-font-size, sub-font, sub-font-size
|
- new options osd-font-size, sub-font, sub-font-size
|
||||||
- new color options with dedicated GUI support: sub-color, sub-border-color, sub-back-color
|
- new color options with dedicated GUI support: sub-color, sub-border-color, sub-back-color
|
||||||
- the config editor no longer shows the command line switches
|
- the config editor no longer shows the command-line switches
|
||||||
- the github start page was greatly improved
|
- the github start page was greatly improved
|
||||||
- the setup.ps1 PowerShell script was greatly improved in regard of error handling and readability
|
- the setup.ps1 PowerShell script was greatly improved in regard of error handling and readability
|
||||||
- a [manual to mpv.net](Manual.md) was created
|
- a [manual to mpv.net](Manual.md) was created
|
||||||
@@ -319,7 +627,7 @@
|
|||||||
- there was a bug that caused underscores beeing removed from input like MBTN_LEFT_DBL
|
- 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
|
- 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
|
- 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
|
### 3.5
|
||||||
|
|
||||||
@@ -332,6 +640,6 @@
|
|||||||
|
|
||||||
### 3.4
|
### 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 zip download option added
|
||||||
- new x86 download option added
|
- new x86 download option added
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright 2002-2017 Frank Skare (stax76)
|
Copyright (C) 2017-2021 mpv.net/mpv/mplayer
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person
|
Permission is hereby granted, free of charge, to any person
|
||||||
obtaining a copy of this software and ssociated documentation
|
obtaining a copy of this software and ssociated documentation
|
||||||
1401
docs/Manual.md
Normal file
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
|
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 628 KiB |
BIN
docs/img/Menu.jpg
Normal file
|
After Width: | Height: | Size: 603 KiB |
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
BIN
docs/img/mpvnet-santa.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
@@ -1,36 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("RatingExtension")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("RatingExtension")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2017-2019 Frank Skare (stax76)")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("55c88710-539d-4402-84c8-31694841c731")]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
// This extension writes a rating to the filename of rated videos when mpv.net shuts down.
|
|
||||||
|
|
||||||
// The input.conf defaults contain key bindings for this extension to set ratings.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using mpvnet;
|
|
||||||
|
|
||||||
namespace RatingExtension // the assembly name must end with 'Extension'
|
|
||||||
{
|
|
||||||
[Export(typeof(IExtension))]
|
|
||||||
public class RatingExtension : IExtension
|
|
||||||
{
|
|
||||||
// dictionory to store the filename and the rating
|
|
||||||
Dictionary<string, int> Dic = new Dictionary<string, int>();
|
|
||||||
|
|
||||||
public RatingExtension() // plugin initialization
|
|
||||||
{
|
|
||||||
mp.ClientMessage += ClientMessage; //handles keys defined in input.conf
|
|
||||||
mp.Shutdown += Shutdown; // handles MPV_EVENT_SHUTDOWN
|
|
||||||
}
|
|
||||||
|
|
||||||
// handles MPV_EVENT_SHUTDOWN
|
|
||||||
void Shutdown()
|
|
||||||
{
|
|
||||||
foreach (var i in Dic)
|
|
||||||
{
|
|
||||||
string filepath = i.Key;
|
|
||||||
int rating = i.Value;
|
|
||||||
if (String.IsNullOrEmpty(filepath) || !File.Exists(filepath))
|
|
||||||
return;
|
|
||||||
string basename = Path.GetFileNameWithoutExtension(filepath);
|
|
||||||
|
|
||||||
for (int x = 0; x < 6; x++)
|
|
||||||
if (basename.Contains(" (" + x + "stars)"))
|
|
||||||
basename = basename.Replace(" (" + x + "stars)", "");
|
|
||||||
|
|
||||||
basename += $" ({rating}stars)";
|
|
||||||
string newPath = Path.Combine(Path.GetDirectoryName(filepath), basename + Path.GetExtension(filepath));
|
|
||||||
if (filepath.ToLower() != newPath.ToLower())
|
|
||||||
File.Move(filepath, newPath);
|
|
||||||
File.SetLastWriteTime(newPath, DateTime.Now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//handles keys defined in input.conf
|
|
||||||
void ClientMessage(string[] args)
|
|
||||||
{
|
|
||||||
if (args[0] != "rate-file") return;
|
|
||||||
|
|
||||||
if (int.TryParse(args[1], out int rating))
|
|
||||||
{
|
|
||||||
string path = mp.get_property_string("path");
|
|
||||||
if (!File.Exists(path)) return;
|
|
||||||
Dic[path] = rating;
|
|
||||||
mp.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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
// This extension implements the C# scripting feature of mpv.net which
|
|
||||||
// is based on CS-Script (https://www.cs-script.net).
|
|
||||||
|
|
||||||
// I also use this extension to code scripts in order to have full
|
|
||||||
// code completion and debugger support, once the script code is
|
|
||||||
// finished I move it from the extension to a standalone script.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel.Composition;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using mpvnet;
|
|
||||||
using CSScriptLibrary;
|
|
||||||
|
|
||||||
namespace ScriptingExtension // the file name of extensions must end with 'Extension'
|
|
||||||
{
|
|
||||||
[Export(typeof(IExtension))]
|
|
||||||
public class ScriptingExtension : IExtension
|
|
||||||
{
|
|
||||||
//Script Script;
|
|
||||||
|
|
||||||
public ScriptingExtension()
|
|
||||||
{
|
|
||||||
//Script = new Script();
|
|
||||||
List<string> scriptFiles = new List<string>();
|
|
||||||
|
|
||||||
if (Directory.Exists(mp.ConfigFolder + "scripts"))
|
|
||||||
scriptFiles.AddRange(Directory.GetFiles(mp.ConfigFolder + "scripts", "*.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"));
|
|
||||||
|
|
||||||
if (scriptFiles.Count == 0) return;
|
|
||||||
CSScriptLibrary.CSScript.EvaluatorConfig.Engine = EvaluatorEngine.CodeDom;
|
|
||||||
|
|
||||||
foreach (var i in scriptFiles)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
CSScriptLibrary.CSScript.Evaluator.LoadCode(File.ReadAllText(i));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Msg.ShowException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{94255EF2-C823-4D82-9017-0E993CC0F5A1}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>ScriptingExtension</RootNamespace>
|
|
||||||
<AssemblyName>ScriptingExtension</AssemblyName>
|
|
||||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<TargetFrameworkProfile />
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x86\Extensions\ScriptingExtension\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x86\Extensions\ScriptingExtension\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x64\Extensions\ScriptingExtension\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x64\Extensions\ScriptingExtension\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="CSScriptLibrary">
|
|
||||||
<HintPath>.\CSScriptLibrary.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="System" />
|
|
||||||
<Reference Include="System.ComponentModel.Composition" />
|
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Windows.Forms" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="ScriptingExtension.cs" />
|
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
<Compile Include="script.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\mpv.net\mpv.net.csproj">
|
|
||||||
<Project>{1751f378-8edf-4b62-be6d-304c7c287089}</Project>
|
|
||||||
<Name>mpv.net</Name>
|
|
||||||
<Private>False</Private>
|
|
||||||
</ProjectReference>
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
</Project>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.29123.88
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptingExtension", "ScriptingExtension.csproj", "{55C88710-539D-4402-84C8-31694841C731}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x86.ActiveCfg = Debug|x86
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x86.Build.0 = Debug|x86
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x64.Build.0 = Release|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x86.Build.0 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {C8A53BF6-B00A-4215-B9D7-801ADB9C8F60}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
|
|
||||||
using mpvnet;
|
|
||||||
|
|
||||||
class Script
|
|
||||||
{
|
|
||||||
public Script()
|
|
||||||
{
|
|
||||||
mp.Shutdown += Shutdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Shutdown()
|
|
||||||
{
|
|
||||||
foreach (string file in Directory.GetFiles(@"C:\Users\frank\Desktop\aaa"))
|
|
||||||
File.Delete(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
img/Avatar.png
|
Before Width: | Height: | Size: 11 KiB |
BIN
img/Menu.png
|
Before Width: | Height: | Size: 1.3 MiB |
51
mpv.net.sln
@@ -1,51 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.28729.10
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mpv.net", "mpv.net\mpv.net.csproj", "{1751F378-8EDF-4B62-BE6D-304C7C287089}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RatingExtension", "extensions\RatingExtension\RatingExtension.csproj", "{55C88710-539D-4402-84C8-31694841C731}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScriptingExtension", "extensions\ScriptingExtension\ScriptingExtension.csproj", "{94255EF2-C823-4D82-9017-0E993CC0F5A1}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Debug|x86.ActiveCfg = Debug|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Debug|x86.Build.0 = Debug|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Release|x64.Build.0 = Release|x64
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{1751F378-8EDF-4B62-BE6D-304C7C287089}.Release|x86.Build.0 = Release|x86
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x86.ActiveCfg = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Debug|x86.Build.0 = Debug|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x64.Build.0 = Release|x64
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{55C88710-539D-4402-84C8-31694841C731}.Release|x86.Build.0 = Release|x86
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Debug|x86.ActiveCfg = Debug|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Debug|x86.Build.0 = Debug|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Release|x64.Build.0 = Release|x64
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Release|x86.ActiveCfg = Release|x86
|
|
||||||
{94255EF2-C823-4D82-9017-0E993CC0F5A1}.Release|x86.Build.0 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {8DE9E796-619E-44B8-9576-F4DE028701BF}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<UserControl x:Name="OptionSettingControl1" x:Class="DynamicGUI.OptionSettingControl"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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:local="clr-namespace:DynamicGUI"
|
|
||||||
xmlns:UI="clr-namespace:UI"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800">
|
|
||||||
|
|
||||||
<Grid Margin="20,0">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBox x:Name="TitleTextBox"
|
|
||||||
FontSize="24"
|
|
||||||
Margin="0,10"
|
|
||||||
BorderThickness="0"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Foreground="{x:Static UI:Theme.Heading}"
|
|
||||||
Background="{x:Static UI:Theme.Background}" />
|
|
||||||
|
|
||||||
<ItemsControl x:Name="ItemsControl">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<WrapPanel Orientation="Vertical">
|
|
||||||
<RadioButton x:Name="RadioButton"
|
|
||||||
VerticalContentAlignment="Center"
|
|
||||||
IsChecked="{Binding Checked}"
|
|
||||||
GroupName="{Binding OptionSetting.Name}"
|
|
||||||
Content="{Binding Text}"
|
|
||||||
FontSize="16"
|
|
||||||
FontWeight="Normal"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"/>
|
|
||||||
|
|
||||||
<TextBox x:Name="ItemHelpTextBox"
|
|
||||||
TextWrapping="WrapWithOverflow"
|
|
||||||
Text="{Binding Help}"
|
|
||||||
Visibility="{Binding Visibility}"
|
|
||||||
Margin="10,0,0,0" BorderThickness="0"
|
|
||||||
IsReadOnly="True" Padding="7,0,0,0"
|
|
||||||
MinHeight="0"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground2}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"/>
|
|
||||||
|
|
||||||
</WrapPanel>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
|
|
||||||
<TextBox x:Name="HelpTextBox"
|
|
||||||
TextWrapping="WrapWithOverflow"
|
|
||||||
BorderThickness="0"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Margin="0,10,0,0"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"/>
|
|
||||||
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
|
|
||||||
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<UserControl x:Name="StringSettingControl1" x:Class="DynamicGUI.StringSettingControl"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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:local="clr-namespace:DynamicGUI"
|
|
||||||
xmlns:UI="clr-namespace:UI"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800">
|
|
||||||
|
|
||||||
<Grid Margin="20,0">
|
|
||||||
<StackPanel>
|
|
||||||
<TextBox x:Name="TitleTextBox"
|
|
||||||
FontSize="24"
|
|
||||||
Margin="0,10"
|
|
||||||
BorderThickness="0"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Foreground="{x:Static UI:Theme.Heading}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"/>
|
|
||||||
|
|
||||||
<Grid Margin="0,0,0,10">
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
<ColumnDefinition Width="Auto" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<TextBox x:Name="ValueTextBox"
|
|
||||||
Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
|
||||||
Width="150"
|
|
||||||
Height="20"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
CaretBrush="{x:Static UI:Theme.Foreground}"
|
|
||||||
TextChanged="ValueTextBox_TextChanged"/>
|
|
||||||
|
|
||||||
<Button x:Name="Button"
|
|
||||||
Height="20"
|
|
||||||
Grid.Column="1"
|
|
||||||
Visibility="{Binding Path=Text, ElementName=StringSettingControl1}"
|
|
||||||
Margin="5,0,0,0"
|
|
||||||
Width="20"
|
|
||||||
Click="Button_Click">...</Button>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<TextBox x:Name="HelpTextBox"
|
|
||||||
TextWrapping="WrapWithOverflow"
|
|
||||||
BorderThickness="0"
|
|
||||||
IsReadOnly="True"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"/>
|
|
||||||
|
|
||||||
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
|
|
||||||
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
|
|
||||||
</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using UI;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public static class App
|
|
||||||
{
|
|
||||||
public static string[] VideoTypes { get; } = "264 265 asf avc avi avs flv h264 h265 hevc m2ts m2v m4v mkv mov mp4 mpeg mpg mpv mts ts vob vpy webm wmv y4m".Split(' ');
|
|
||||||
public static string[] AudioTypes { get; } = "mp3 mp2 ac3 ogg opus flac wav w64 m4a dts dtsma dtshr dtshd eac3 thd thd+ac3 mka aac mpa".Split(' ');
|
|
||||||
public static string[] ImageTypes { get; } = {"jpg", "bmp", "gif", "png"};
|
|
||||||
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 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 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;
|
|
||||||
public static bool AutoLoadFolder { get; set; } = true;
|
|
||||||
public static bool Queue { get; set; }
|
|
||||||
|
|
||||||
public static int StartThreshold { get; set; } = 1500;
|
|
||||||
public static int RecentCount { get; set; } = 15;
|
|
||||||
|
|
||||||
public static float MinimumAspectRatio { get; set; } = 1.3f;
|
|
||||||
|
|
||||||
public static Extension Extension { get; set; }
|
|
||||||
|
|
||||||
public static bool IsDarkMode {
|
|
||||||
get => (DarkMode == "system" && Sys.IsDarkTheme) || DarkMode == "always";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
string dummy = mp.ConfigFolder;
|
|
||||||
var dummy2 = mp.Conf;
|
|
||||||
|
|
||||||
foreach (var i in Conf)
|
|
||||||
ProcessProperty(i.Key, i.Value);
|
|
||||||
|
|
||||||
if (App.DebugMode)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string filePath = mp.ConfigFolder + "mpvnet-debug.log";
|
|
||||||
if (File.Exists(filePath)) File.Delete(filePath);
|
|
||||||
Trace.Listeners.Add(new TextWriterTraceListener(filePath));
|
|
||||||
Trace.AutoFlush = true;
|
|
||||||
//if (App.DebugMode) Trace.WriteLine("");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Msg.ShowException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string themeContent = null;
|
|
||||||
|
|
||||||
if (File.Exists(mp.ConfigFolder + "theme.conf"))
|
|
||||||
themeContent = File.ReadAllText(mp.ConfigFolder + "theme.conf");
|
|
||||||
|
|
||||||
Theme.Init(
|
|
||||||
themeContent,
|
|
||||||
Properties.Resources.theme,
|
|
||||||
IsDarkMode ? DarkTheme : LightTheme);
|
|
||||||
|
|
||||||
mp.Shutdown += Shutdown;
|
|
||||||
mp.Initialized += Initialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Initialized()
|
|
||||||
{
|
|
||||||
if (RememberVolume)
|
|
||||||
{
|
|
||||||
mp.set_property_int("volume", RegHelp.GetInt(App.RegPath, "Volume", 70));
|
|
||||||
mp.set_property_string("mute", RegHelp.GetString(App.RegPath, "Mute", "no"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Shutdown()
|
|
||||||
{
|
|
||||||
if (RememberVolume)
|
|
||||||
{
|
|
||||||
RegHelp.SetObject(App.RegPath, "Volume", mp.get_property_int("volume"));
|
|
||||||
RegHelp.SetObject(App.RegPath, "Mute", mp.get_property_string("mute"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (string i in File.ReadAllLines(ConfPath))
|
|
||||||
if (i.Contains("=") && !i.StartsWith("#"))
|
|
||||||
_Conf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
|
|
||||||
}
|
|
||||||
return _Conf;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ProcessProperty(string name, string value)
|
|
||||||
{
|
|
||||||
switch (name)
|
|
||||||
{
|
|
||||||
case "remember-position": RememberPosition = value == "yes"; return true;
|
|
||||||
case "maximized": Maximized = value == "yes"; return true;
|
|
||||||
case "start-size": RememberHeight = value == "previous"; return true;
|
|
||||||
case "process-instance": ProcessInstance = value; return true;
|
|
||||||
case "dark-mode": DarkMode = value; return true;
|
|
||||||
case "debug-mode": DebugMode = value == "yes"; return true;
|
|
||||||
case "remember-volume": RememberVolume = value == "yes"; return true;
|
|
||||||
case "start-threshold": StartThreshold = value.Int(); return true;
|
|
||||||
case "minimum-aspect-ratio": MinimumAspectRatio = value.Float(); return true;
|
|
||||||
case "auto-load-folder": AutoLoadFolder = value == "yes"; return true;
|
|
||||||
case "recent-count": RecentCount = value.Int(); return true;
|
|
||||||
case "queue": Queue = value == "yes"; return true;
|
|
||||||
case "dark-theme": DarkTheme = value.Trim('\'', '"'); return true;
|
|
||||||
case "light-theme": LightTheme = value.Trim('\'', '"'); return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Windows.Interop;
|
|
||||||
|
|
||||||
using VB = Microsoft.VisualBasic;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public class Command
|
|
||||||
{
|
|
||||||
public static void Execute(string id, string[] args)
|
|
||||||
{
|
|
||||||
switch (id)
|
|
||||||
{
|
|
||||||
case "open-files": OpenFiles(args); break;
|
|
||||||
case "open-url": OpenURL(); break;
|
|
||||||
case "open-optical-media": Open_DVD_Or_BD_Folder(); break;
|
|
||||||
case "manage-file-associations": // deprecated 2019
|
|
||||||
case "show-setup-dialog": ShowDialog(typeof(SetupWindow)); break;
|
|
||||||
case "cycle-audio": CycleAudio(); break;
|
|
||||||
case "load-audio": LoadAudio(); break;
|
|
||||||
case "load-sub": LoadSubtitle(); break;
|
|
||||||
case "execute-mpv-command": ExecuteMpvCommand(); break;
|
|
||||||
case "show-history": ShowHistory(); break;
|
|
||||||
case "show-media-search": ShowDialog(typeof(EverythingWindow)); break;
|
|
||||||
case "show-command-palette": ShowDialog(typeof(CommandPaletteWindow)); break;
|
|
||||||
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 "shell-execute": Process.Start(args[0]); break;
|
|
||||||
case "show-info": ShowInfo(); break;
|
|
||||||
case "playlist-last": PlaylistLast(); 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 ShowDialog(Type winType)
|
|
||||||
{
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
Window win = Activator.CreateInstance(winType) as Window;
|
|
||||||
new WindowInteropHelper(win).Owner = MainForm.Instance.Handle;
|
|
||||||
win.ShowDialog();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void OpenFiles(params string[] args)
|
|
||||||
{
|
|
||||||
bool append = Control.ModifierKeys.HasFlag(Keys.Control);
|
|
||||||
bool loadFolder = true;
|
|
||||||
|
|
||||||
foreach (string arg in args)
|
|
||||||
{
|
|
||||||
if (arg == "append") append = true;
|
|
||||||
if (arg == "no-folder") loadFolder = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
using (var d = new OpenFileDialog() { Multiselect = true })
|
|
||||||
if (d.ShowDialog() == DialogResult.OK)
|
|
||||||
mp.Load(d.FileNames, loadFolder, append);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Open_DVD_Or_BD_Folder()
|
|
||||||
{
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
using (var d = new FolderBrowserDialog())
|
|
||||||
{
|
|
||||||
d.Description = "Select a DVD or Blu-ray folder.";
|
|
||||||
d.ShowNewFolderButton = false;
|
|
||||||
|
|
||||||
if (d.ShowDialog() == DialogResult.OK)
|
|
||||||
{
|
|
||||||
if (Directory.Exists(d.SelectedPath + "\\BDMV"))
|
|
||||||
{
|
|
||||||
mp.set_property_string("bluray-device", d.SelectedPath);
|
|
||||||
mp.Load(new[] { @"bd://" }, false, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mp.set_property_string("dvd-device", d.SelectedPath);
|
|
||||||
mp.Load(new[] { @"dvd://" }, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PlaylistLast() => mp.set_property_int("playlist-pos", mp.get_property_int("playlist-count") - 1);
|
|
||||||
|
|
||||||
public static void ShowHistory()
|
|
||||||
{
|
|
||||||
if (File.Exists(mp.ConfigFolder + "history.txt"))
|
|
||||||
Process.Start(mp.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", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ShowInfo()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string performer, title, album, genre, date, duration, text = "";
|
|
||||||
long fileSize = 0;
|
|
||||||
string path = mp.get_property_string("path");
|
|
||||||
|
|
||||||
if (path.Contains("://"))
|
|
||||||
path = mp.get_property_string("media-title");
|
|
||||||
|
|
||||||
int width = mp.get_property_int("video-params/w");
|
|
||||||
int height = mp.get_property_int("video-params/h");
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
{
|
|
||||||
fileSize = new FileInfo(path).Length;
|
|
||||||
|
|
||||||
if (App.AudioTypes.Contains(path.ShortExt()))
|
|
||||||
{
|
|
||||||
using (MediaInfo mediaInfo = new MediaInfo(path))
|
|
||||||
{
|
|
||||||
performer = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Performer");
|
|
||||||
title = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Title");
|
|
||||||
album = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Album");
|
|
||||||
genre = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Genre");
|
|
||||||
date = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Recorded_Date");
|
|
||||||
duration = mediaInfo.GetInfo(MediaInfoStreamKind.Audio, "Duration/String");
|
|
||||||
|
|
||||||
if (performer != "") text += "Artist: " + performer + "\n";
|
|
||||||
if (title != "") text += "Title: " + title + "\n";
|
|
||||||
if (album != "") text += "Album: " + album + "\n";
|
|
||||||
if (genre != "") text += "Genre: " + genre + "\n";
|
|
||||||
if (date != "") text += "Year: " + date + "\n";
|
|
||||||
if (duration != "") text += "Length: " + duration + "\n";
|
|
||||||
text += "Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n";
|
|
||||||
text += "Type: " + path.ShortExt().ToUpper();
|
|
||||||
|
|
||||||
mp.commandv("show-text", text, "5000");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (App.ImageTypes.Contains(path.ShortExt()))
|
|
||||||
{
|
|
||||||
using (MediaInfo mediaInfo = new MediaInfo(path))
|
|
||||||
{
|
|
||||||
text =
|
|
||||||
"Width: " + mediaInfo.GetInfo(MediaInfoStreamKind.Image, "Width") + "\n" +
|
|
||||||
"Height: " + mediaInfo.GetInfo(MediaInfoStreamKind.Image, "Height") + "\n" +
|
|
||||||
"Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n" +
|
|
||||||
"Type: " + path.ShortExt().ToUpper();
|
|
||||||
|
|
||||||
mp.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();
|
|
||||||
|
|
||||||
text = path.FileName() + "\n" +
|
|
||||||
FormatTime(position.TotalMinutes) + ":" +
|
|
||||||
FormatTime(position.Seconds) + " / " +
|
|
||||||
FormatTime(duration2.TotalMinutes) + ":" +
|
|
||||||
FormatTime(duration2.Seconds) + "\n" +
|
|
||||||
$"{width} x {height}\n";
|
|
||||||
|
|
||||||
if (fileSize > 0)
|
|
||||||
text += Convert.ToInt32(fileSize / 1024.0 / 1024.0) + " MB\n";
|
|
||||||
|
|
||||||
text += $"{videoFormat}\n{audioCodec}";
|
|
||||||
|
|
||||||
mp.commandv("show-text", text, "5000");
|
|
||||||
string FormatTime(double value) => ((int)value).ToString("00");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Msg.ShowException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExecuteMpvCommand() // deprecated 2019
|
|
||||||
{
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
string command = VB.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegHelp.GetString(App.RegPath, "RecentExecutedCommand"));
|
|
||||||
if (string.IsNullOrEmpty(command)) return;
|
|
||||||
RegHelp.SetObject(App.RegPath, "RecentExecutedCommand", command);
|
|
||||||
mp.command(command, false);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void OpenURL()
|
|
||||||
{
|
|
||||||
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.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mp.Load(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void LoadSubtitle()
|
|
||||||
{
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
using (var d = new OpenFileDialog())
|
|
||||||
{
|
|
||||||
string path = mp.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);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void LoadAudio()
|
|
||||||
{
|
|
||||||
InvokeOnMainThread(new Action(() => {
|
|
||||||
using (var d = new OpenFileDialog())
|
|
||||||
{
|
|
||||||
string path = mp.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);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CycleAudio()
|
|
||||||
{
|
|
||||||
string path = mp.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");
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static string FileName(this string path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(path)) return "";
|
|
||||||
int index = path.LastIndexOf('\\');
|
|
||||||
if (index > -1) return path.Substring(index + 1);
|
|
||||||
index = path.LastIndexOf('/');
|
|
||||||
if (index > -1) return path.Substring(index + 1);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ShortExt(this string path)
|
|
||||||
{
|
|
||||||
return Path.GetExtension(path).ToLower().TrimStart('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Int(this string value)
|
|
||||||
{
|
|
||||||
int.TryParse(value, out int result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float Float(this string value)
|
|
||||||
{
|
|
||||||
float.TryParse(value.Replace(",", "."), NumberStyles.Float, CultureInfo.InvariantCulture, out float result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
class ConsoleHelp
|
|
||||||
{
|
|
||||||
public static void WriteError(object obj)
|
|
||||||
{
|
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
|
||||||
Console.WriteLine(obj?.ToString());
|
|
||||||
Console.ResetColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
|
|
||||||
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;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public class Sys
|
|
||||||
{
|
|
||||||
public static bool IsDarkTheme {
|
|
||||||
get {
|
|
||||||
object value = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
|
|
||||||
|
|
||||||
if (value is null)
|
|
||||||
value = 1;
|
|
||||||
|
|
||||||
return (int)value == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsDirectoryWritable(string dirPath)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (FileStream fs = File.Create(Path.Combine(dirPath,
|
|
||||||
Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose))
|
|
||||||
{ }
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{ }
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringLogicalComparer : IComparer, IComparer<string>
|
|
||||||
{
|
|
||||||
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern int StrCmpLogical(string x, string y);
|
|
||||||
|
|
||||||
int IComparer_Compare(object x, object y) => StrCmpLogical(x.ToString(), y.ToString());
|
|
||||||
int IComparer.Compare(object x, object y) => IComparer_Compare(x, y);
|
|
||||||
int IComparerOfString_Compare(string x, string y) => StrCmpLogical(x, y);
|
|
||||||
int IComparer<string>.Compare(string x, string y) => IComparerOfString_Compare(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FileAssociation
|
|
||||||
{
|
|
||||||
static string ExePath = Application.ExecutablePath;
|
|
||||||
static string ExeFilename = Path.GetFileName(Application.ExecutablePath);
|
|
||||||
static string ExeFilenameNoExt = Path.GetFileNameWithoutExtension(Application.ExecutablePath);
|
|
||||||
static string[] Types;
|
|
||||||
|
|
||||||
public static void Register(string[] types)
|
|
||||||
{
|
|
||||||
Types = types;
|
|
||||||
|
|
||||||
RegHelp.SetObject(@"HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename, null, ExePath);
|
|
||||||
RegHelp.SetObject(@"HKCR\Applications\" + ExeFilename, "FriendlyAppName", "mpv.net media player");
|
|
||||||
RegHelp.SetObject($@"HKCR\Applications\{ExeFilename}\shell\open\command", null, $"\"{ExePath}\" \"%1\"");
|
|
||||||
RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities", "ApplicationDescription", "mpv.net media player");
|
|
||||||
RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities", "ApplicationName", "mpv.net");
|
|
||||||
RegHelp.SetObject(@"HKCR\SystemFileAssociations\video\OpenWithList\" + ExeFilename, null, "");
|
|
||||||
RegHelp.SetObject(@"HKCR\SystemFileAssociations\audio\OpenWithList\" + ExeFilename, null, "");
|
|
||||||
RegHelp.SetObject(@"HKLM\SOFTWARE\RegisteredApplications", "mpv.net", @"SOFTWARE\Clients\Media\mpv.net\Capabilities");
|
|
||||||
|
|
||||||
foreach (string ext in Types)
|
|
||||||
{
|
|
||||||
RegHelp.SetObject($@"HKCR\Applications\{ExeFilename}\SupportedTypes", "." + ext, "");
|
|
||||||
RegHelp.SetObject($@"HKCR\" + "." + ext, null, ExeFilenameNoExt + "." + ext);
|
|
||||||
RegHelp.SetObject($@"HKCR\" + "." + ext + @"\OpenWithProgIDs", ExeFilenameNoExt + "." + ext, "");
|
|
||||||
|
|
||||||
if (App.VideoTypes.Contains(ext))
|
|
||||||
RegHelp.SetObject(@"HKCR\" + "." + ext, "PerceivedType", "video");
|
|
||||||
|
|
||||||
if (App.AudioTypes.Contains(ext))
|
|
||||||
RegHelp.SetObject(@"HKCR\" + "." + ext, "PerceivedType", "audio");
|
|
||||||
|
|
||||||
if (App.ImageTypes.Contains(ext))
|
|
||||||
RegHelp.SetObject(@"HKCR\" + "." + ext, "PerceivedType", "image");
|
|
||||||
|
|
||||||
RegHelp.SetObject($@"HKCR\" + ExeFilenameNoExt + "." + ext + @"\shell\open", null, "Play with " + Application.ProductName);
|
|
||||||
RegHelp.SetObject($@"HKCR\" + ExeFilenameNoExt + "." + ext + @"\shell\open\command", null, $"\"{ExePath}\" \"%1\"");
|
|
||||||
RegHelp.SetObject(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities\FileAssociations", "." + ext, ExeFilenameNoExt + "." + ext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Unregister()
|
|
||||||
{
|
|
||||||
RegHelp.RemoveKey($@"HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename);
|
|
||||||
RegHelp.RemoveKey($@"HKCR\Applications\" + ExeFilename);
|
|
||||||
RegHelp.RemoveKey($@"HKLM\SOFTWARE\Clients\Media\mpv.net");
|
|
||||||
RegHelp.RemoveKey($@"HKCR\SystemFileAssociations\video\OpenWithList\" + ExeFilename);
|
|
||||||
RegHelp.RemoveKey($@"HKCR\SystemFileAssociations\audio\OpenWithList\" + ExeFilename);
|
|
||||||
RegHelp.RemoveValue(@"HKLM\SOFTWARE\RegisteredApplications", "mpv.net");
|
|
||||||
|
|
||||||
foreach (string id in Registry.ClassesRoot.GetSubKeyNames())
|
|
||||||
{
|
|
||||||
if (id.StartsWith(ExeFilenameNoExt + "."))
|
|
||||||
Registry.ClassesRoot.DeleteSubKeyTree(id);
|
|
||||||
|
|
||||||
RegHelp.RemoveValue($@"HKCR\Software\Classes\{id}\OpenWithProgIDs", ExeFilenameNoExt + id);
|
|
||||||
RegHelp.RemoveValue($@"HKLM\Software\Classes\{id}\OpenWithProgIDs", ExeFilenameNoExt + id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RegHelp
|
|
||||||
{
|
|
||||||
public static void SetObject(string path, string name, object value)
|
|
||||||
{
|
|
||||||
using (RegistryKey regKey = GetRootKey(path).CreateSubKey(path.Substring(5), RegistryKeyPermissionCheck.ReadWriteSubTree))
|
|
||||||
regKey.SetValue(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetString(string path, string name, string defaultValue = "")
|
|
||||||
{
|
|
||||||
object value = GetObject(path, name, defaultValue);
|
|
||||||
return !(value is string) ? defaultValue : value.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetInt(string path, string name, int defaultValue = 0)
|
|
||||||
{
|
|
||||||
object value = GetObject(path, name, defaultValue);
|
|
||||||
return !(value is int) ? defaultValue : (int)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object GetObject(string path, string name, object defaultValue = null)
|
|
||||||
{
|
|
||||||
using (RegistryKey regKey = GetRootKey(path).OpenSubKey(path.Substring(5)))
|
|
||||||
return regKey == null ? null : regKey.GetValue(name, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveKey(string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false);
|
|
||||||
} catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveValue(string path, string name)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (RegistryKey regKey = GetRootKey(path).OpenSubKey(path.Substring(5), true))
|
|
||||||
if (regKey != null)
|
|
||||||
regKey.DeleteValue(name, false);
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
static RegistryKey GetRootKey(string path)
|
|
||||||
{
|
|
||||||
switch (path.Substring(0, 4))
|
|
||||||
{
|
|
||||||
case "HKLM": return Registry.LocalMachine;
|
|
||||||
case "HKCU": return Registry.CurrentUser;
|
|
||||||
case "HKCR": return Registry.ClassesRoot;
|
|
||||||
default: throw new Exception();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MediaTrack
|
|
||||||
{
|
|
||||||
public string Text { get; set; }
|
|
||||||
public string Type { get; set; }
|
|
||||||
public int ID { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CommandItem : INotifyPropertyChanged
|
|
||||||
{
|
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
|
||||||
|
|
||||||
public string Path { get; set; } = "";
|
|
||||||
public string Command { get; set; } = "";
|
|
||||||
|
|
||||||
public CommandItem() { }
|
|
||||||
|
|
||||||
public CommandItem(SerializationInfo info, StreamingContext context) { }
|
|
||||||
|
|
||||||
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
|
|
||||||
{
|
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _Input = "";
|
|
||||||
|
|
||||||
public string Input {
|
|
||||||
get => _Input;
|
|
||||||
set {
|
|
||||||
_Input = value;
|
|
||||||
NotifyPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ObservableCollection<CommandItem> GetItems(string content)
|
|
||||||
{
|
|
||||||
var items = new ObservableCollection<CommandItem>();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
foreach (string line in content.Split('\r', '\n'))
|
|
||||||
{
|
|
||||||
string val = line.Trim();
|
|
||||||
|
|
||||||
if (val.StartsWith("#"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!val.Contains(" "))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CommandItem item = new CommandItem();
|
|
||||||
item.Input = val.Substring(0, val.IndexOf(" "));
|
|
||||||
|
|
||||||
if (item.Input == "_")
|
|
||||||
item.Input = "";
|
|
||||||
|
|
||||||
val = val.Substring(val.IndexOf(" ") + 1);
|
|
||||||
|
|
||||||
if (val.Contains("#menu:"))
|
|
||||||
{
|
|
||||||
item.Path = val.Substring(val.IndexOf("#menu:") + 6).Trim();
|
|
||||||
val = val.Substring(0, val.IndexOf("#menu:"));
|
|
||||||
|
|
||||||
if (item.Path.Contains(";"))
|
|
||||||
item.Path = item.Path.Substring(item.Path.IndexOf(";") + 1).Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Command = val.Trim();
|
|
||||||
|
|
||||||
if (item.Command == "")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (item.Command.ToLower() == "ignore")
|
|
||||||
item.Command = "";
|
|
||||||
|
|
||||||
MigrateCommands(item);
|
|
||||||
items.Add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ObservableCollection<CommandItem> _Items;
|
|
||||||
|
|
||||||
public static ObservableCollection<CommandItem> Items {
|
|
||||||
get {
|
|
||||||
if (_Items is null)
|
|
||||||
_Items = GetItems(File.ReadAllText(mp.InputConfPath));
|
|
||||||
|
|
||||||
return _Items;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
public static string Startup { get; } = Application.StartupPath + "\\";
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PathHelp
|
|
||||||
{
|
|
||||||
public static string GetBaseName(string value)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(value))
|
|
||||||
return "";
|
|
||||||
|
|
||||||
int index = value.IndexOf("/");
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
value = value.Substring(index + 1);
|
|
||||||
|
|
||||||
index = value.IndexOf("\\");
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
value = value.Substring(index + 1);
|
|
||||||
|
|
||||||
index = value.LastIndexOf(".");
|
|
||||||
|
|
||||||
if (index > -1)
|
|
||||||
value = value.Substring(0, index);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
public class MediaInfo : IDisposable
|
|
||||||
{
|
|
||||||
IntPtr Handle;
|
|
||||||
static bool Loaded;
|
|
||||||
|
|
||||||
public MediaInfo(string sourcepath)
|
|
||||||
{
|
|
||||||
if (!Loaded)
|
|
||||||
{
|
|
||||||
if (Native.LoadLibrary("MediaInfo.dll") == IntPtr.Zero)
|
|
||||||
throw new Exception("Failed to load MediaInfo.dll.");
|
|
||||||
|
|
||||||
Loaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle = MediaInfo_New();
|
|
||||||
MediaInfo_Open(Handle, sourcepath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetInfo(MediaInfoStreamKind streamKind, string parameter)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, streamKind, 0, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetCount(MediaInfoStreamKind streamKind) => MediaInfo_Count_Get(Handle, streamKind, -1);
|
|
||||||
|
|
||||||
public string GetVideo(int streamNumber, string parameter)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetAudio(int streamNumber, 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Disposed;
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!Disposed)
|
|
||||||
{
|
|
||||||
Disposed = true;
|
|
||||||
MediaInfo_Close(Handle);
|
|
||||||
MediaInfo_Delete(Handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~MediaInfo() { Dispose(); }
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll")]
|
|
||||||
static extern IntPtr MediaInfo_New();
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
|
||||||
static extern int MediaInfo_Open(IntPtr handle, string path);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
|
||||||
static extern IntPtr MediaInfo_Option(IntPtr handle, string optionString, string value);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll")]
|
|
||||||
static extern IntPtr MediaInfo_Inform(IntPtr handle, int reserved);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll")]
|
|
||||||
static extern int MediaInfo_Close(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll")]
|
|
||||||
static extern void MediaInfo_Delete(IntPtr handle);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
|
||||||
static extern IntPtr MediaInfo_Get(IntPtr handle,
|
|
||||||
MediaInfoStreamKind streamKind,
|
|
||||||
int streamNumber,
|
|
||||||
string parameter,
|
|
||||||
MediaInfoInfoKind kindOfInfo,
|
|
||||||
MediaInfoInfoKind kindOfSearch);
|
|
||||||
|
|
||||||
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
|
||||||
static extern int MediaInfo_Count_Get(IntPtr handle,
|
|
||||||
MediaInfoStreamKind streamKind,
|
|
||||||
int streamNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MediaInfoStreamKind
|
|
||||||
{
|
|
||||||
General,
|
|
||||||
Video,
|
|
||||||
Audio,
|
|
||||||
Text,
|
|
||||||
Other,
|
|
||||||
Image,
|
|
||||||
Menu,
|
|
||||||
Max,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MediaInfoInfoKind
|
|
||||||
{
|
|
||||||
Name,
|
|
||||||
Text,
|
|
||||||
Measure,
|
|
||||||
Options,
|
|
||||||
NameText,
|
|
||||||
MeasureText,
|
|
||||||
Info,
|
|
||||||
HowTo
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
public class Native
|
|
||||||
{
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern bool AttachConsole(int dwProcessId);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern bool FreeConsole();
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern IntPtr LoadLibrary(string path);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
public static extern int RegisterWindowMessage(string id);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern bool AllowSetForegroundWindow(int dwProcessId);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern void ReleaseCapture();
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
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);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
|
|
||||||
private static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
|
|
||||||
|
|
||||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
|
|
||||||
private static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);
|
|
||||||
|
|
||||||
public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex)
|
|
||||||
{
|
|
||||||
if (IntPtr.Size == 8)
|
|
||||||
return GetWindowLong64(hWnd, nIndex);
|
|
||||||
else
|
|
||||||
return GetWindowLong32(hWnd, nIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct RECT
|
|
||||||
{
|
|
||||||
public int Left;
|
|
||||||
public int Top;
|
|
||||||
public int Right;
|
|
||||||
public int Bottom;
|
|
||||||
|
|
||||||
public RECT(Rectangle r)
|
|
||||||
{
|
|
||||||
Left = r.Left;
|
|
||||||
Top = r.Top;
|
|
||||||
Right = r.Right;
|
|
||||||
Bottom = r.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RECT(int left, int top, int right, int bottom)
|
|
||||||
{
|
|
||||||
Left = left;
|
|
||||||
Top = top;
|
|
||||||
Right = right;
|
|
||||||
Bottom = bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
|
|
||||||
public Size Size => new Size(Right - Left, Bottom - Top);
|
|
||||||
public int Width => Right - Left;
|
|
||||||
public int Height => Bottom - Top;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct COPYDATASTRUCT
|
|
||||||
{
|
|
||||||
public IntPtr dwData;
|
|
||||||
public int cbData;
|
|
||||||
[MarshalAs(UnmanagedType.LPTStr)]
|
|
||||||
public string lpData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public static class NativeHelp
|
|
||||||
{
|
|
||||||
public static int GetResizeBorder(int v)
|
|
||||||
{
|
|
||||||
switch (v)
|
|
||||||
{
|
|
||||||
case 1 /* WMSZ_LEFT */ : return 3;
|
|
||||||
case 3 /* WMSZ_TOP */ : return 2;
|
|
||||||
case 2 /* WMSZ_RIGHT */ : return 3;
|
|
||||||
case 6 /* WMSZ_BOTTOM */ : return 2;
|
|
||||||
case 4 /* WMSZ_TOPLEFT */ : return 1;
|
|
||||||
case 5 /* WMSZ_TOPRIGHT */ : return 1;
|
|
||||||
case 7 /* WMSZ_BOTTOMLEFT */ : return 3;
|
|
||||||
case 8 /* WMSZ_BOTTOMRIGHT */: return 3;
|
|
||||||
default: return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SubtractWindowBorders(IntPtr hwnd, ref Native.RECT rc)
|
|
||||||
{
|
|
||||||
var b = new Native.RECT(0, 0, 0, 0);
|
|
||||||
AddWindowBorders(hwnd, ref b);
|
|
||||||
rc.Left -= b.Left;
|
|
||||||
rc.Top -= b.Top;
|
|
||||||
rc.Right -= b.Right;
|
|
||||||
rc.Bottom -= b.Bottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddWindowBorders(IntPtr hwnd, ref Native.RECT rc)
|
|
||||||
{
|
|
||||||
Native.AdjustWindowRect(ref rc, (uint)Native.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,657 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
public class Msg
|
|
||||||
{
|
|
||||||
private static string ShownMessages;
|
|
||||||
|
|
||||||
public static string SupportURL { get; set; }
|
|
||||||
|
|
||||||
public static void Show(string mainInstruction, string content = null)
|
|
||||||
{
|
|
||||||
Msg.Show(mainInstruction, content, MsgIcon.Info, MsgButtons.Ok, MsgResult.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ShowError(string mainInstruction, string content = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (TaskDialog<string> td = new TaskDialog<string>())
|
|
||||||
{
|
|
||||||
td.AllowCancel = false;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
if (mainInstruction.Length < 80)
|
|
||||||
td.MainInstruction = mainInstruction;
|
|
||||||
else
|
|
||||||
td.Content = mainInstruction;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
td.MainInstruction = mainInstruction;
|
|
||||||
td.Content = content;
|
|
||||||
}
|
|
||||||
|
|
||||||
td.MainIcon = MsgIcon.Error;
|
|
||||||
td.Footer = "[Copy Message](copymsg)";
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Msg.SupportURL))
|
|
||||||
td.Footer += $" [Contact Support]({SupportURL})";
|
|
||||||
|
|
||||||
td.Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MessageBox.Show(e.GetType().Name + "\n\n" + e.Message + "\n\n" + e,
|
|
||||||
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ShowException(Exception exception)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (TaskDialog<string> td = new TaskDialog<string>())
|
|
||||||
{
|
|
||||||
td.MainInstruction = exception.GetType().Name;
|
|
||||||
td.Content = exception.Message;
|
|
||||||
td.MainIcon = MsgIcon.Error;
|
|
||||||
td.ExpandedInformation = exception.ToString();
|
|
||||||
td.Footer = "[Copy Message](copymsg)";
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Msg.SupportURL))
|
|
||||||
td.Footer += $" [Contact Support]({SupportURL})";
|
|
||||||
|
|
||||||
td.Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MessageBox.Show(e.GetType().Name + "\n\n" + e.Message + "\n\n" + e,
|
|
||||||
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ShowWarning(string mainInstruction,
|
|
||||||
string content = null,
|
|
||||||
bool onlyOnce = false)
|
|
||||||
{
|
|
||||||
if (onlyOnce && Msg.ShownMessages != null &&
|
|
||||||
Msg.ShownMessages.Contains(mainInstruction + content))
|
|
||||||
return;
|
|
||||||
|
|
||||||
Msg.Show(mainInstruction, content, MsgIcon.Warning, MsgButtons.Ok, MsgResult.None);
|
|
||||||
if (!onlyOnce) return;
|
|
||||||
Msg.ShownMessages += mainInstruction + content;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return Msg.Show(mainInstruction, content, MsgIcon.None, buttons, MsgResult.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MsgResult Show(string mainInstruction,
|
|
||||||
string content,
|
|
||||||
MsgIcon icon,
|
|
||||||
MsgButtons buttons,
|
|
||||||
MsgResult defaultButton = MsgResult.None)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (TaskDialog<MsgResult> td = new TaskDialog<MsgResult>())
|
|
||||||
{
|
|
||||||
td.AllowCancel = false;
|
|
||||||
td.DefaultButton = defaultButton;
|
|
||||||
td.MainIcon = icon;
|
|
||||||
|
|
||||||
if (content == null)
|
|
||||||
{
|
|
||||||
if (mainInstruction.Length < 80)
|
|
||||||
td.MainInstruction = mainInstruction;
|
|
||||||
else
|
|
||||||
td.Content = mainInstruction;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
td.MainInstruction = mainInstruction;
|
|
||||||
td.Content = content;
|
|
||||||
}
|
|
||||||
if (buttons == MsgButtons.OkCancel)
|
|
||||||
{
|
|
||||||
td.AddButton("OK", MsgResult.OK);
|
|
||||||
td.AddButton("Cancel", MsgResult.Cancel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
td.CommonButtons = buttons;
|
|
||||||
return td.Show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return (MsgResult)MessageBox.Show(e.GetType().Name + "\n\n" + e.Message + "\n\n" + e,
|
|
||||||
Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
const int TDE_CONTENT = 0;
|
|
||||||
const int TDE_EXPANDED_INFORMATION = 1;
|
|
||||||
const int TDE_FOOTER = 2;
|
|
||||||
const int TDE_MAIN_INSTRUCTION = 3;
|
|
||||||
const int TDN_CREATED = 0;
|
|
||||||
const int TDN_NAVIGATED = 1;
|
|
||||||
const int TDN_BUTTON_CLICKED = 2;
|
|
||||||
const int TDN_HYPERLINK_CLICKED = 3;
|
|
||||||
const int TDN_TIMER = 4;
|
|
||||||
const int TDN_DESTROYED = 5;
|
|
||||||
const int TDN_RADIO_BUTTON_CLICKED = 6;
|
|
||||||
const int TDN_DIALOG_CONSTRUCTED = 7;
|
|
||||||
const int TDN_VERIFICATION_CLICKED = 8;
|
|
||||||
const int TDN_HELP = 9;
|
|
||||||
const int TDN_EXPANDO_BUTTON_CLICKED = 10;
|
|
||||||
const int TDM_NAVIGATE_PAGE = 1125;
|
|
||||||
const int TDM_CLICK_BUTTON = 1126;
|
|
||||||
const int TDM_SET_MARQUEE_PROGRESS_BAR = 1127;
|
|
||||||
const int TDM_SET_PROGRESS_BAR_STATE = 1128;
|
|
||||||
const int TDM_SET_PROGRESS_BAR_RANGE = 1129;
|
|
||||||
const int TDM_SET_PROGRESS_BAR_POS = 1130;
|
|
||||||
const int TDM_SET_PROGRESS_BAR_MARQUEE = 1131;
|
|
||||||
const int TDM_SET_ELEMENT_TEXT = 1132;
|
|
||||||
const int TDM_CLICK_RADIO_BUTTON = 1134;
|
|
||||||
const int TDM_ENABLE_BUTTON = 1135;
|
|
||||||
const int TDM_ENABLE_RADIO_BUTTON = 1136;
|
|
||||||
const int TDM_CLICK_VERIFICATION = 1137;
|
|
||||||
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()
|
|
||||||
{
|
|
||||||
IdValueDic = new Dictionary<int, T>();
|
|
||||||
IdTextDic = new Dictionary<int, string>();
|
|
||||||
CommandLinkShieldList = new List<int>();
|
|
||||||
Buttons = new List<TaskDialogNative.TASKDIALOG_BUTTON>();
|
|
||||||
RadioButtons = new List<TaskDialogNative.TASKDIALOG_BUTTON>();
|
|
||||||
_SelectedID = -1;
|
|
||||||
Config = new TaskDialogNative.TASKDIALOGCONFIG();
|
|
||||||
Config.cbSize = (uint)Marshal.SizeOf(Config);
|
|
||||||
Config.hwndParent = GetHandle();
|
|
||||||
Config.hInstance = IntPtr.Zero;
|
|
||||||
Config.dwFlags = TaskDialogNative.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION;
|
|
||||||
Config.dwCommonButtons = MsgButtons.None;
|
|
||||||
Config.MainIcon = new TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION(0);
|
|
||||||
Config.FooterIcon = new TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION(0);
|
|
||||||
Config.cxWidth = 0U;
|
|
||||||
Config.cButtons = 0U;
|
|
||||||
Config.cRadioButtons = 0U;
|
|
||||||
Config.pButtons = IntPtr.Zero;
|
|
||||||
Config.pRadioButtons = IntPtr.Zero;
|
|
||||||
Config.nDefaultButton = 0;
|
|
||||||
Config.nDefaultRadioButton = 0;
|
|
||||||
Config.pszWindowTitle = ((AssemblyProductAttribute)Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true)[0]).Product;
|
|
||||||
Config.pszMainInstruction = "";
|
|
||||||
Config.pszContent = "";
|
|
||||||
Config.pfCallback = new PFTASKDIALOGCALLBACK(this.DialogProc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntPtr GetHandle()
|
|
||||||
{
|
|
||||||
StringBuilder lpszFileName = new StringBuilder(260);
|
|
||||||
IntPtr foregroundWindow = TaskDialogNative.GetForegroundWindow();
|
|
||||||
TaskDialogNative.GetWindowModuleFileName(foregroundWindow, lpszFileName, 260U);
|
|
||||||
|
|
||||||
if (Path.GetFileName(lpszFileName.ToString().Replace(".vshost", "")) ==
|
|
||||||
Path.GetFileName(Assembly.GetEntryAssembly().Location))
|
|
||||||
|
|
||||||
return foregroundWindow;
|
|
||||||
|
|
||||||
return IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AllowCancel {
|
|
||||||
set {
|
|
||||||
if (value)
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION;
|
|
||||||
else
|
|
||||||
Config.dwFlags ^= TaskDialogNative.TASKDIALOG_FLAGS.TDF_ALLOW_DIALOG_CANCELLATION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string MainInstruction {
|
|
||||||
get => Config.pszMainInstruction;
|
|
||||||
set => Config.pszMainInstruction = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Content {
|
|
||||||
get => Config.pszContent;
|
|
||||||
set => Config.pszContent = ExpandMarkdownMarkup(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExpandedInformation {
|
|
||||||
get => Config.pszExpandedInformation;
|
|
||||||
set => Config.pszExpandedInformation = ExpandMarkdownMarkup(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string VerificationText {
|
|
||||||
get => Config.pszVerificationText;
|
|
||||||
set => Config.pszVerificationText = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MsgResult DefaultButton {
|
|
||||||
get => (MsgResult)Config.nDefaultButton;
|
|
||||||
set => Config.nDefaultButton = (int)value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Footer {
|
|
||||||
get => Config.pszFooter;
|
|
||||||
set => Config.pszFooter = ExpandMarkdownMarkup(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MsgIcon MainIcon {
|
|
||||||
set => Config.MainIcon = new TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION((int)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int _SelectedID;
|
|
||||||
|
|
||||||
public int SelectedID {
|
|
||||||
get => _SelectedID;
|
|
||||||
set {
|
|
||||||
foreach (var i in IdValueDic)
|
|
||||||
if (i.Key == value) _SelectedID = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T SelectedValue {
|
|
||||||
get {
|
|
||||||
if (IdValueDic.ContainsKey(SelectedID))
|
|
||||||
return IdValueDic[SelectedID];
|
|
||||||
return SelectedValueValue;
|
|
||||||
}
|
|
||||||
set => SelectedValueValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SelectedText {
|
|
||||||
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;
|
|
||||||
set {
|
|
||||||
if (value)
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED;
|
|
||||||
else
|
|
||||||
Config.dwFlags ^= TaskDialogNative.TASKDIALOG_FLAGS.TDF_VERIFICATION_FLAG_CHECKED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MsgButtons CommonButtons {
|
|
||||||
get => Config.dwCommonButtons;
|
|
||||||
set => Config.dwCommonButtons = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Timeout {
|
|
||||||
get => Convert.ToInt32(TimeoutValue / 1000.0);
|
|
||||||
set {
|
|
||||||
TimeoutValue = value * 1000;
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_CALLBACK_TIMER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddButton(string text, T value)
|
|
||||||
{
|
|
||||||
int n = 1000 + IdValueDic.Count + 1;
|
|
||||||
IdValueDic[n] = value;
|
|
||||||
Buttons.Add(new TaskDialogNative.TASKDIALOG_BUTTON(n, text));
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ExpandMarkdownMarkup(string value)
|
|
||||||
{
|
|
||||||
if (value.Contains("["))
|
|
||||||
{
|
|
||||||
Regex regex = new Regex(@"\[(.+)\]\((.+)\)");
|
|
||||||
|
|
||||||
if (regex.Match(value).Success)
|
|
||||||
{
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_ENABLE_HYPERLINKS;
|
|
||||||
value = regex.Replace(value, "<a href=\"$2\">$1</a>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCommandLink(string text, T value)
|
|
||||||
{
|
|
||||||
int n = 1000 + IdValueDic.Count + 1;
|
|
||||||
IdValueDic[n] = value;
|
|
||||||
IdTextDic[n] = text;
|
|
||||||
Buttons.Add(new TaskDialogNative.TASKDIALOG_BUTTON(n, text));
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddCommandLink(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;
|
|
||||||
Buttons.Add(new TaskDialogNative.TASKDIALOG_BUTTON(n, text));
|
|
||||||
Config.dwFlags |= TaskDialogNative.TASKDIALOG_FLAGS.TDF_USE_COMMAND_LINKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddRadioButton(string text, T value)
|
|
||||||
{
|
|
||||||
int n = 1000 + IdValueDic.Count + 1;
|
|
||||||
IdValueDic[n] = value;
|
|
||||||
RadioButtons.Add(new TaskDialogNative.TASKDIALOG_BUTTON(n, text));
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Show()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
CheckBoxChecked = isChecked;
|
|
||||||
if (SelectedValue is MsgResult) SelectedValue = (T)(object)SelectedID;
|
|
||||||
return SelectedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int DialogProc(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam, IntPtr lpRefData)
|
|
||||||
{
|
|
||||||
switch (msg)
|
|
||||||
{
|
|
||||||
case 0: //TDN_CREATED
|
|
||||||
foreach (var i in CommandLinkShieldList)
|
|
||||||
SendMessage(hwnd, TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, new IntPtr(i), new IntPtr(1));
|
|
||||||
break;
|
|
||||||
case 2: //TDN_BUTTON_CLICKED
|
|
||||||
case 6: //TDN_RADIO_BUTTON_CLICKED
|
|
||||||
if (SelectedValue is MsgResult)
|
|
||||||
_SelectedID = wParam.ToInt32();
|
|
||||||
else
|
|
||||||
SelectedID = wParam.ToInt32();
|
|
||||||
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)(() => {
|
|
||||||
Clipboard.SetText(MainInstruction + "\r\n\r\n" + Content + "\r\n\r\n" + ExpandedInformation);
|
|
||||||
MessageBox.Show("Message was copied to clipboard.", Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
||||||
}));
|
|
||||||
thread.SetApartmentState(ApartmentState.STA);
|
|
||||||
thread.Start();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 4: //TDN_TIMER
|
|
||||||
if (ExitTickCount == 0) ExitTickCount = Environment.TickCount + Timeout * 1000;
|
|
||||||
if (Environment.TickCount > ExitTickCount)
|
|
||||||
TaskDialogNative.SendMessage(hwnd, 1126, new IntPtr(1), IntPtr.Zero);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarshalDialogControlStructs()
|
|
||||||
{
|
|
||||||
if (Buttons != null && Buttons.Count > 0)
|
|
||||||
{
|
|
||||||
ButtonArray = TaskDialog<T>.AllocateAndMarshalButtons(Buttons);
|
|
||||||
Config.pButtons = ButtonArray;
|
|
||||||
Config.cButtons = (uint)Buttons.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RadioButtons == null || RadioButtons.Count <= 0) return;
|
|
||||||
RadioButtonArray = TaskDialog<T>.AllocateAndMarshalButtons(RadioButtons);
|
|
||||||
Config.pRadioButtons = RadioButtonArray;
|
|
||||||
Config.cRadioButtons = (uint)RadioButtons.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntPtr AllocateAndMarshalButtons(List<TaskDialogNative.TASKDIALOG_BUTTON> structs)
|
|
||||||
{
|
|
||||||
var initialPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(TASKDIALOG_BUTTON)) * structs.Count);
|
|
||||||
var currentPtr = initialPtr;
|
|
||||||
|
|
||||||
foreach (var button in structs)
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr(button, currentPtr, false);
|
|
||||||
currentPtr = (IntPtr)(currentPtr.ToInt64() + Marshal.SizeOf(button));
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialPtr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~TaskDialog()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (disposed) return;
|
|
||||||
disposed = true;
|
|
||||||
|
|
||||||
if (ButtonArray != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(ButtonArray);
|
|
||||||
ButtonArray = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RadioButtonArray != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(RadioButtonArray);
|
|
||||||
RadioButtonArray = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public delegate int PFTASKDIALOGCALLBACK(
|
|
||||||
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,
|
|
||||||
out int pnButton,
|
|
||||||
out int pnRadioButton,
|
|
||||||
[MarshalAs(UnmanagedType.Bool)] 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);
|
|
||||||
|
|
||||||
[DllImport("user32.dll")]
|
|
||||||
public static extern IntPtr SendMessage(
|
|
||||||
IntPtr handle,
|
|
||||||
int message,
|
|
||||||
IntPtr wParam,
|
|
||||||
IntPtr lParam);
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
|
|
||||||
public class TASKDIALOGCONFIG
|
|
||||||
{
|
|
||||||
public uint cbSize;
|
|
||||||
public IntPtr hwndParent;
|
|
||||||
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;
|
|
||||||
public int nDefaultButton;
|
|
||||||
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;
|
|
||||||
public uint cxWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum TASKDIALOG_FLAGS
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
TDF_ENABLE_HYPERLINKS = 1,
|
|
||||||
TDF_USE_HICON_MAIN = 2,
|
|
||||||
TDF_USE_HICON_FOOTER = 4,
|
|
||||||
TDF_ALLOW_DIALOG_CANCELLATION = 8,
|
|
||||||
TDF_USE_COMMAND_LINKS = 16,
|
|
||||||
TDF_USE_COMMAND_LINKS_NO_ICON = 32,
|
|
||||||
TDF_EXPAND_FOOTER_AREA = 64,
|
|
||||||
TDF_EXPANDED_BY_DEFAULT = 128,
|
|
||||||
TDF_VERIFICATION_FLAG_CHECKED = 256,
|
|
||||||
TDF_SHOW_PROGRESS_BAR = 512,
|
|
||||||
TDF_SHOW_MARQUEE_PROGRESS_BAR = 1024,
|
|
||||||
TDF_CALLBACK_TIMER = 2048,
|
|
||||||
TDF_POSITION_RELATIVE_TO_WINDOW = 4096,
|
|
||||||
TDF_RTL_LAYOUT = 8192,
|
|
||||||
TDF_NO_DEFAULT_RADIO_BUTTON = 16384,
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
|
|
||||||
public struct TASKDIALOGCONFIG_ICON_UNION
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public int hMainIcon;
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public int pszIcon;
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public IntPtr spacer;
|
|
||||||
|
|
||||||
public TASKDIALOGCONFIG_ICON_UNION(int i)
|
|
||||||
{
|
|
||||||
this = new TaskDialogNative.TASKDIALOGCONFIG_ICON_UNION();
|
|
||||||
spacer = IntPtr.Zero;
|
|
||||||
pszIcon = 0;
|
|
||||||
hMainIcon = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Unicode)]
|
|
||||||
public struct TASKDIALOG_BUTTON
|
|
||||||
{
|
|
||||||
public int nButtonID;
|
|
||||||
[MarshalAs(UnmanagedType.LPWStr)]
|
|
||||||
public string pszButtonText;
|
|
||||||
|
|
||||||
public TASKDIALOG_BUTTON(int n, string txt)
|
|
||||||
{
|
|
||||||
this = new TaskDialogNative.TASKDIALOG_BUTTON();
|
|
||||||
nButtonID = n;
|
|
||||||
pszButtonText = txt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MsgButtons
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Ok = 1,
|
|
||||||
Yes = 2,
|
|
||||||
No = 4,
|
|
||||||
YesNo = 6,
|
|
||||||
Cancel = 8,
|
|
||||||
OkCancel = 9,
|
|
||||||
YesNoCancel = 14,
|
|
||||||
Retry = 16,
|
|
||||||
RetryCancel = 24,
|
|
||||||
Close = 32,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MsgResult
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
OK,
|
|
||||||
Cancel,
|
|
||||||
Abort,
|
|
||||||
Retry,
|
|
||||||
Ignore,
|
|
||||||
Yes,
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum MsgIcon
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
SecurityShieldGray = 65527,
|
|
||||||
SecuritySuccess = 65528,
|
|
||||||
SecurityError = 65529,
|
|
||||||
SecurityWarning = 65530,
|
|
||||||
SecurityShieldBlue = 65531,
|
|
||||||
Shield = 65532,
|
|
||||||
Info = 65533,
|
|
||||||
Error = 65534,
|
|
||||||
Warning = 65535,
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
input-default-bindings = no
|
|
||||||
input-ar-delay = 500
|
|
||||||
input-ar-rate = 20
|
|
||||||
keep-open = yes
|
|
||||||
keep-open-pause = no
|
|
||||||
osd-playing-msg = '${filename}'
|
|
||||||
script-opts = osc-scalewindowed=1.5
|
|
||||||
screenshot-directory = '~~desktop/'
|
|
||||||
cscale = spline36
|
|
||||||
dscale = spline36
|
|
||||||
scale = spline36
|
|
||||||
hwdec = yes
|
|
||||||
|
|
||||||
[protocol.https]
|
|
||||||
osd-playing-msg = '${media-title}'
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<Window x:Class="mpvnet.AboutWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="About mpv.net"
|
|
||||||
Height="230"
|
|
||||||
Width="400"
|
|
||||||
FontSize="16"
|
|
||||||
ShowInTaskbar="False"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
ResizeMode="NoResize"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI: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 Name="Version" HorizontalAlignment="Center" />
|
|
||||||
<TextBlock Name="mpvVersion" HorizontalAlignment="Center" />
|
|
||||||
<TextBlock HorizontalAlignment="Center" Margin="0,0,0,20">MIT License</TextBlock>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
<Window x:Class="mpvnet.CommandPaletteWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="Command Palette"
|
|
||||||
Height="295"
|
|
||||||
Width="400"
|
|
||||||
FontSize="13"
|
|
||||||
ResizeMode="NoResize"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
Loaded="Window_Loaded">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition></RowDefinition>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<TextBox Name="FilterTextBox"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI: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}"
|
|
||||||
MouseUp="ListView_MouseUp">
|
|
||||||
|
|
||||||
<ListView.ItemContainerStyle>
|
|
||||||
<Style TargetType="ListBoxItem">
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
|
|
||||||
</Style>
|
|
||||||
</ListView.ItemContainerStyle>
|
|
||||||
|
|
||||||
<ListView.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition />
|
|
||||||
<ColumnDefinition />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock Text="{Binding Path}"></TextBlock>
|
|
||||||
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text="{Binding Input}"
|
|
||||||
HorizontalAlignment="Right"></TextBlock>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ListView.ItemTemplate>
|
|
||||||
</ListView>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<Window xmlns:Controls="clr-namespace:Controls" x:Name="ConfWindow1" x:Class="mpvnet.ConfWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="Config Editor"
|
|
||||||
Height="530"
|
|
||||||
Width="700"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
ShowInTaskbar="False"
|
|
||||||
WindowStartupLocation="CenterScreen"
|
|
||||||
Loaded="ConfWindow1_Loaded">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="100" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<Controls:SearchTextBoxUserControl HintText="Find a setting"
|
|
||||||
x:Name="SearchControl"
|
|
||||||
Width="250"
|
|
||||||
Margin="0,20,0,0"
|
|
||||||
Grid.ColumnSpan="2"/>
|
|
||||||
|
|
||||||
<ScrollViewer x:Name="MainScrollViewer"
|
|
||||||
VerticalScrollBarVisibility="Auto"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="0,0,0,10">
|
|
||||||
|
|
||||||
<StackPanel x:Name="MainStackPanel"></StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
|
|
||||||
<StackPanel Margin="20,0,0,0" Grid.Row="1">
|
|
||||||
<ListBox x:Name="FilterListBox"
|
|
||||||
ItemsSource="{Binding FilterStrings}"
|
|
||||||
BorderThickness="0"
|
|
||||||
SelectionChanged="FilterListBox_SelectionChanged"
|
|
||||||
Foreground="{x:Static UI:Theme.Heading}"
|
|
||||||
Background="{x:Static UI:Theme.Background}">
|
|
||||||
|
|
||||||
<ListBox.ItemTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<TextBlock Text="{Binding}" FontSize="16" />
|
|
||||||
</DataTemplate>
|
|
||||||
</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>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<Window x:Class="mpvnet.EverythingWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="Media File Search"
|
|
||||||
FontSize="13"
|
|
||||||
Height="300"
|
|
||||||
Width="600"
|
|
||||||
ResizeMode="NoResize"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
Loaded="Window_Loaded">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition></RowDefinition>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<TextBox Name="FilterTextBox"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
PreviewKeyDown="FilterTextBox_PreviewKeyDown"
|
|
||||||
TextChanged="FilterTextBox_TextChanged"/>
|
|
||||||
|
|
||||||
<ListView Name="ListView"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
Grid.Row="1"
|
|
||||||
MouseUp="ListView_MouseUp"
|
|
||||||
PreviewKeyDown="ListView_PreviewKeyDown">
|
|
||||||
|
|
||||||
<ListView.ItemContainerStyle>
|
|
||||||
<Style TargetType="ListBoxItem">
|
|
||||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
|
|
||||||
</Style>
|
|
||||||
</ListView.ItemContainerStyle>
|
|
||||||
|
|
||||||
</ListView>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<Window xmlns:Controls="clr-namespace:Controls" x:Class="mpvnet.InputWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="Input Editor"
|
|
||||||
Height="500"
|
|
||||||
Width="750"
|
|
||||||
FontSize="13"
|
|
||||||
ShowInTaskbar="False"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
Loaded="Window_Loaded"
|
|
||||||
Closed="Window_Closed">
|
|
||||||
|
|
||||||
<Window.Resources>
|
|
||||||
<Style x:Key="DataGrid_Font_Centering" TargetType="{x:Type DataGridCell}">
|
|
||||||
<Setter Property="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="{x:Type DataGridCell}">
|
|
||||||
<Grid Background="{TemplateBinding Background}">
|
|
||||||
<ContentPresenter VerticalAlignment="Center" />
|
|
||||||
</Grid>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
</Style>
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="Auto" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Controls:SearchTextBoxUserControl
|
|
||||||
HintText="Type ? to get help."
|
|
||||||
x:Name="SearchControl"
|
|
||||||
Width="300"
|
|
||||||
Margin="0,20,0,20"
|
|
||||||
Grid.ColumnSpan="2" />
|
|
||||||
|
|
||||||
<DataGrid x:Name="DataGrid"
|
|
||||||
Grid.Row="1"
|
|
||||||
CommandManager.PreviewCanExecute="DataGrid_PreviewCanExecute"
|
|
||||||
AutoGenerateColumns="False"
|
|
||||||
CellStyle="{StaticResource DataGrid_Font_Centering}" >
|
|
||||||
|
|
||||||
<DataGrid.Columns>
|
|
||||||
<DataGridTextColumn Header="Menu" Binding="{Binding Path}"/>
|
|
||||||
|
|
||||||
<DataGridTemplateColumn Header="Input">
|
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
|
||||||
<DataTemplate>
|
|
||||||
<Button MinHeight="20" Click="ButtonClick">
|
|
||||||
<TextBlock Text="{Binding Input}"></TextBlock>
|
|
||||||
</Button>
|
|
||||||
</DataTemplate>
|
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
|
||||||
</DataGridTemplateColumn>
|
|
||||||
|
|
||||||
<DataGridTextColumn Header="Command" Binding="{Binding Command}" MaxWidth="330" />
|
|
||||||
|
|
||||||
</DataGrid.Columns>
|
|
||||||
</DataGrid>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<Window x:Class="mpvnet.LearnWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="Learn Input"
|
|
||||||
Height="200"
|
|
||||||
Width="400"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
ResizeMode="NoResize"
|
|
||||||
Loaded="Window_Loaded"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
MouseWheel="Window_MouseWheel"
|
|
||||||
MouseUp="Window_MouseUp"
|
|
||||||
MouseDoubleClick="Window_MouseDoubleClick"
|
|
||||||
TextInput="Window_TextInput">
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="*" />
|
|
||||||
<RowDefinition Height="40" />
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
|
|
||||||
<TextBlock x:Name="MenuTextBlock"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="16"></TextBlock>
|
|
||||||
|
|
||||||
<TextBlock x:Name="KeyTextBlock"
|
|
||||||
Grid.Row="1"
|
|
||||||
Grid.ColumnSpan="2"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
HorizontalAlignment="Center"
|
|
||||||
FontSize="16" />
|
|
||||||
|
|
||||||
<Button x:Name="ConfirmButton" Grid.Row="2" Click="ConfirmButton_Click">Confirm</Button>
|
|
||||||
<Button x:Name="ClearButton" Grid.Row="2" Click="ClearButton_Click" Grid.Column="1">Clear</Button>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,385 +0,0 @@
|
|||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Interop;
|
|
||||||
|
|
||||||
using WinForms = System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public partial class LearnWindow : Window
|
|
||||||
{
|
|
||||||
public CommandItem InputItem { get; set; }
|
|
||||||
string NewKey = "";
|
|
||||||
string KeyChar = "";
|
|
||||||
|
|
||||||
public LearnWindow() => InitializeComponent();
|
|
||||||
|
|
||||||
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
|
||||||
{
|
|
||||||
WinForms.Message m = new WinForms.Message();
|
|
||||||
m.HWnd = hwnd;
|
|
||||||
m.Msg = msg;
|
|
||||||
m.WParam = wParam;
|
|
||||||
m.LParam = lParam;
|
|
||||||
ProcessKeyEventArgs(ref m);
|
|
||||||
return m.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnKeyUp(WinForms.KeyEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.KeyCode == WinForms.Keys.ControlKey || e.KeyCode == WinForms.Keys.ShiftKey ||
|
|
||||||
e.KeyCode == WinForms.Keys.Menu || e.KeyCode == WinForms.Keys.None)
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
string text = "";
|
|
||||||
uint charValue = MapVirtualKey((uint)e.KeyCode, 2);
|
|
||||||
|
|
||||||
if (charValue == 0 || (charValue & 1 << 31) == 1 << 31)
|
|
||||||
text = e.KeyCode.ToString().Trim();
|
|
||||||
else
|
|
||||||
try {
|
|
||||||
text = Convert.ToChar(charValue).ToString().ToLower().Trim();
|
|
||||||
} catch {}
|
|
||||||
|
|
||||||
for (int i = 0; i < 13; i++)
|
|
||||||
if ("D" + i == text)
|
|
||||||
text = text.Substring(1);
|
|
||||||
|
|
||||||
switch (e.KeyCode)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
text = KeyChar;
|
|
||||||
wasModified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (text == "#") text = "SHARP";
|
|
||||||
|
|
||||||
if (isAlt && !wasModified)
|
|
||||||
text = "ALT+" + text;
|
|
||||||
|
|
||||||
if (isShift && !wasModified)
|
|
||||||
text = "SHIFT+" + text;
|
|
||||||
|
|
||||||
if (isCtrl && !wasModified)
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
int WM_KEYUP = 0x0101, WM_SYSKEYUP = 0x0105, WM_APPCOMMAND = 0x0319;
|
|
||||||
|
|
||||||
if (m.Msg == WM_KEYUP || m.Msg == WM_SYSKEYUP)
|
|
||||||
OnKeyUp(new WinForms.KeyEventArgs((WinForms.Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys));
|
|
||||||
else if (m.Msg == WM_APPCOMMAND)
|
|
||||||
{
|
|
||||||
var value = (AppCommand)(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
|
|
||||||
source.AddHook(new HwndSourceHook(WndProc));
|
|
||||||
SetKey(InputItem.Input);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
InputItem.Input = NewKey;
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearButton_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
InputItem.Input = "_";
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.Delta > 0)
|
|
||||||
SetKey("WHEEL_UP");
|
|
||||||
else
|
|
||||||
SetKey("WHEEL_DOWN");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
switch (e.ChangedButton)
|
|
||||||
{
|
|
||||||
case MouseButton.Left:
|
|
||||||
if (BlockMBTN_LEFT)
|
|
||||||
BlockMBTN_LEFT = false;
|
|
||||||
else
|
|
||||||
SetKey("MBTN_LEFT");
|
|
||||||
break;
|
|
||||||
case MouseButton.Middle:
|
|
||||||
SetKey("MBTN_MID");
|
|
||||||
break;
|
|
||||||
case MouseButton.XButton1:
|
|
||||||
SetKey("MBTN_BACK");
|
|
||||||
break;
|
|
||||||
case MouseButton.XButton2:
|
|
||||||
SetKey("MBTN_FORWARD");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BlockMBTN_LEFT;
|
|
||||||
|
|
||||||
private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.ChangedButton == MouseButton.Left)
|
|
||||||
{
|
|
||||||
SetKey("MBTN_LEFT_DBL");
|
|
||||||
BlockMBTN_LEFT = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Window_TextInput(object sender, TextCompositionEventArgs e)
|
|
||||||
{
|
|
||||||
KeyChar = e.Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
<UserControl x:Class="Controls.SearchTextBoxUserControl"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800">
|
|
||||||
|
|
||||||
<Grid Name="SearchTextBoxUserControl1"
|
|
||||||
Background="{x:Static UI: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}" />
|
|
||||||
|
|
||||||
<TextBox Name="SearchTextBox"
|
|
||||||
Height="25"
|
|
||||||
Padding="1,2,0,0"
|
|
||||||
BorderThickness="2"
|
|
||||||
Background="Transparent"
|
|
||||||
TextChanged="SearchTextBox_TextChanged"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
CaretBrush="{x:Static UI:Theme.Foreground}" />
|
|
||||||
|
|
||||||
<Button Name="SearchClearButton"
|
|
||||||
Background="Transparent"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
FontFamily="Marlett"
|
|
||||||
FontSize="10"
|
|
||||||
Width="17"
|
|
||||||
Height="17"
|
|
||||||
Margin="2,0,4,0"
|
|
||||||
Visibility="Hidden"
|
|
||||||
Click="SearchClearButton_Click" >r
|
|
||||||
|
|
||||||
<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="Template">
|
|
||||||
<Setter.Value>
|
|
||||||
<ControlTemplate TargetType="Button">
|
|
||||||
<Border BorderThickness="1"
|
|
||||||
BorderBrush="{TemplateBinding Foreground}"
|
|
||||||
SnapsToDevicePixels="True">
|
|
||||||
|
|
||||||
<ContentPresenter HorizontalAlignment="Center"
|
|
||||||
VerticalAlignment="Center" />
|
|
||||||
</Border>
|
|
||||||
</ControlTemplate>
|
|
||||||
</Setter.Value>
|
|
||||||
</Setter>
|
|
||||||
|
|
||||||
<Style.Triggers>
|
|
||||||
<Trigger Property="IsMouseOver" Value="True">
|
|
||||||
<Setter Property="Foreground" Value="{x:Static UI:Theme.Heading}"/>
|
|
||||||
</Trigger>
|
|
||||||
</Style.Triggers>
|
|
||||||
</Style>
|
|
||||||
</Button.Style>
|
|
||||||
</Button>
|
|
||||||
</Grid>
|
|
||||||
</UserControl>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<Window x:Class="mpvnet.SetupWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
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"
|
|
||||||
mc:Ignorable="d"
|
|
||||||
|
|
||||||
Title="mpv.net OS Setup"
|
|
||||||
FontSize="13"
|
|
||||||
Foreground="{x:Static UI:Theme.Foreground}"
|
|
||||||
Background="{x:Static UI:Theme.Background}"
|
|
||||||
SizeToContent="WidthAndHeight"
|
|
||||||
WindowStartupLocation="CenterOwner" >
|
|
||||||
|
|
||||||
<Window.Resources>
|
|
||||||
<Style TargetType="Button">
|
|
||||||
<Setter Property="Padding" Value="10"></Setter>
|
|
||||||
<Setter Property="Margin" Value="5"></Setter>
|
|
||||||
</Style>
|
|
||||||
</Window.Resources>
|
|
||||||
|
|
||||||
<Grid>
|
|
||||||
<StackPanel Margin="10">
|
|
||||||
<Button Name="RegisterVideo" Click="RegisterVideo_Click">Register video file extensions (requires elevated privileges)</Button>
|
|
||||||
<Button Name="RegisterAudio" Click="RegisterAudio_Click">Register audio file extensions (requires elevated privileges)</Button>
|
|
||||||
<Button Name="RegisterImage" Click="RegisterImage_Click">Register image file extensions (requires elevated privileges)</Button>
|
|
||||||
<Button Name="UnregisterFileAssociations" Click="UnregisterFileAssociations_Click">Unregister file extensions (requires elevated privileges)</Button>
|
|
||||||
<Button Name="AddToPathEnvVar" Click="AddToPathEnvVar_Click">Add mpv.net to Path environment variable</Button>
|
|
||||||
<Button Name="RemoveFromPathEnvVar" Click="RemoveFromPathEnvVar_Click">Remove mpv.net from Path environment variable</Button>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Windows;
|
|
||||||
|
|
||||||
using WinForms = System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public partial class SetupWindow : Window
|
|
||||||
{
|
|
||||||
public SetupWindow() => InitializeComponent();
|
|
||||||
|
|
||||||
void RegisterFileAssociations(string value)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var proc = new Process())
|
|
||||||
{
|
|
||||||
proc.StartInfo.FileName = WinForms.Application.ExecutablePath;
|
|
||||||
proc.StartInfo.Arguments = "--reg-file-assoc " + value;
|
|
||||||
proc.StartInfo.Verb = "runas";
|
|
||||||
proc.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Process.Start("ms-settings:defaultapps");
|
|
||||||
} 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");
|
|
||||||
|
|
||||||
private void UnregisterFileAssociations_Click(object sender, RoutedEventArgs e) => RegisterFileAssociations("unreg");
|
|
||||||
|
|
||||||
private void AddToPathEnvVar_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
string var = WinForms.Application.StartupPath + ";";
|
|
||||||
string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);
|
|
||||||
|
|
||||||
if (path.Contains(var))
|
|
||||||
Msg.ShowWarning("Path was already containing mpv.net.");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("Path", var + path, EnvironmentVariableTarget.User);
|
|
||||||
Msg.Show("mpv.net was successfully added to Path.", (var + path).Replace(";","\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveFromPathEnvVar_Click(object sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
string var = WinForms.Application.StartupPath + ";";
|
|
||||||
string path = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.User);
|
|
||||||
|
|
||||||
if (path.Contains(var))
|
|
||||||
{
|
|
||||||
Environment.SetEnvironmentVariable("Path", path.Replace(var, ""), EnvironmentVariableTarget.User);
|
|
||||||
Msg.Show("mpv.net was successfully removed from Path.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Msg.ShowWarning("Path was not containing mpv.net.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,720 +0,0 @@
|
|||||||
|
|
||||||
using System;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using UI;
|
|
||||||
|
|
||||||
namespace mpvnet
|
|
||||||
{
|
|
||||||
public partial class MainForm : Form
|
|
||||||
{
|
|
||||||
public static MainForm Instance { get; set; }
|
|
||||||
public static IntPtr Hwnd { get; set; }
|
|
||||||
public new ContextMenuStripEx ContextMenu { get; set; }
|
|
||||||
Point LastCursorPosChanged;
|
|
||||||
int LastCursorChangedTickCount;
|
|
||||||
int TaskbarButtonCreatedMessage;
|
|
||||||
bool WasShown;
|
|
||||||
Taskbar Taskbar;
|
|
||||||
List<string> RecentFiles;
|
|
||||||
|
|
||||||
public MainForm()
|
|
||||||
{
|
|
||||||
InitializeComponent();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
object recent = RegHelp.GetObject(App.RegPath, "Recent");
|
|
||||||
|
|
||||||
if (recent is string[] r)
|
|
||||||
RecentFiles = new List<string>(r);
|
|
||||||
else
|
|
||||||
RecentFiles = new List<string>();
|
|
||||||
|
|
||||||
Instance = this;
|
|
||||||
Hwnd = Handle;
|
|
||||||
mp.Init();
|
|
||||||
|
|
||||||
mp.Shutdown += Shutdown;
|
|
||||||
mp.VideoSizeChanged += VideoSizeChanged;
|
|
||||||
mp.FileLoaded += FileLoaded;
|
|
||||||
mp.Idle += Idle;
|
|
||||||
mp.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);
|
|
||||||
|
|
||||||
if (mp.GPUAPI != "vulkan")
|
|
||||||
mp.ProcessCommandLine(false);
|
|
||||||
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) => Msg.ShowError(e.ExceptionObject.ToString());
|
|
||||||
Application.ThreadException += (sender, e) => Msg.ShowException(e.Exception);
|
|
||||||
Msg.SupportURL = "https://github.com/stax76/mpv.net#support";
|
|
||||||
Text = "mpv.net " + Application.ProductVersion;
|
|
||||||
TaskbarButtonCreatedMessage = Native.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);
|
|
||||||
|
|
||||||
int targetIndex = mp.Screen;
|
|
||||||
Screen[] screens = Screen.AllScreens;
|
|
||||||
|
|
||||||
if (targetIndex < 0)
|
|
||||||
targetIndex = 0;
|
|
||||||
|
|
||||||
if (targetIndex > screens.Length - 1)
|
|
||||||
targetIndex = screens.Length - 1;
|
|
||||||
|
|
||||||
Screen screen = screens[Array.IndexOf(screens, screens[targetIndex])];
|
|
||||||
Rectangle target = screen.Bounds;
|
|
||||||
Left = target.X + (target.Width - Width) / 2;
|
|
||||||
Top = target.Y + (target.Height - Height) / 2;
|
|
||||||
|
|
||||||
int posX = RegHelp.GetInt(App.RegPath, "PosX");
|
|
||||||
int posY = RegHelp.GetInt(App.RegPath, "PosY");
|
|
||||||
|
|
||||||
if (posX != 0 && posY != 0 && App.RememberPosition)
|
|
||||||
{
|
|
||||||
Left = posX - Width / 2;
|
|
||||||
Top = posY - Height / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (App.Maximized)
|
|
||||||
{
|
|
||||||
SetFormPosAndSize(1, true);
|
|
||||||
WindowState = FormWindowState.Maximized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Msg.ShowException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items);
|
|
||||||
|
|
||||||
void Shutdown() => BeginInvoke(new Action(() => Close()));
|
|
||||||
|
|
||||||
void Idle()
|
|
||||||
{
|
|
||||||
BeginInvoke(new Action(() => Text = "mpv.net " + Application.ProductVersion));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
|
|
||||||
|
|
||||||
void VideoSizeChanged() => BeginInvoke(new Action(() => SetFormPosAndSize()));
|
|
||||||
|
|
||||||
void PropChangeFullscreen(bool value) => BeginInvoke(new Action(() => CycleFullscreen(value)));
|
|
||||||
|
|
||||||
void ContextMenu_Opened(object sender, EventArgs e) => CursorHelp.Show();
|
|
||||||
|
|
||||||
bool IsFullscreen => WindowState == FormWindowState.Maximized && FormBorderStyle == FormBorderStyle.None;
|
|
||||||
|
|
||||||
bool IsMouseInOSC() => PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
|
|
||||||
|
|
||||||
void ContextMenu_Opening(object sender, CancelEventArgs e)
|
|
||||||
{
|
|
||||||
lock (mp.MediaTracks)
|
|
||||||
{
|
|
||||||
MenuItem trackMenuItem = FindMenuItem("Track");
|
|
||||||
|
|
||||||
if (trackMenuItem != null)
|
|
||||||
{
|
|
||||||
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();
|
|
||||||
|
|
||||||
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();
|
|
||||||
trackMenuItem.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vidTracks.Length > 0)
|
|
||||||
trackMenuItem.DropDownItems.Add(new ToolStripSeparator());
|
|
||||||
|
|
||||||
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();
|
|
||||||
trackMenuItem.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (subTracks.Length > 0)
|
|
||||||
trackMenuItem.DropDownItems.Add(new ToolStripSeparator());
|
|
||||||
|
|
||||||
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();
|
|
||||||
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";
|
|
||||||
trackMenuItem.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ediTracks.Length > 0)
|
|
||||||
trackMenuItem.DropDownItems.Add(new ToolStripSeparator());
|
|
||||||
|
|
||||||
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;
|
|
||||||
trackMenuItem.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (mp.Chapters)
|
|
||||||
{
|
|
||||||
MenuItem chaptersMenuItem = FindMenuItem("Chapters");
|
|
||||||
|
|
||||||
if (chaptersMenuItem != null)
|
|
||||||
{
|
|
||||||
chaptersMenuItem.DropDownItems.Clear();
|
|
||||||
|
|
||||||
foreach (var i in mp.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");
|
|
||||||
chaptersMenuItem.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem recent = FindMenuItem("Recent");
|
|
||||||
|
|
||||||
if (recent != null)
|
|
||||||
{
|
|
||||||
recent.DropDownItems.Clear();
|
|
||||||
|
|
||||||
foreach (string path in RecentFiles)
|
|
||||||
MenuItem.Add(recent.DropDownItems, path, () => mp.Load(new[] { path }, true, Control.ModifierKeys.HasFlag(Keys.Control)));
|
|
||||||
|
|
||||||
recent.DropDownItems.Add(new ToolStripSeparator());
|
|
||||||
MenuItem mi = new MenuItem("Clear List");
|
|
||||||
mi.Action = () => RecentFiles.Clear();
|
|
||||||
recent.DropDownItems.Add(mi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem FindMenuItem(string text, ToolStripItemCollection items)
|
|
||||||
{
|
|
||||||
foreach (var item in items)
|
|
||||||
{
|
|
||||||
if (item is MenuItem mi)
|
|
||||||
{
|
|
||||||
if (mi.Text.StartsWith(text) && mi.Text.Trim() == text)
|
|
||||||
return mi;
|
|
||||||
|
|
||||||
if (mi.DropDownItems.Count > 0)
|
|
||||||
{
|
|
||||||
MenuItem val = FindMenuItem(text, mi.DropDownItems);
|
|
||||||
if (val != null) return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WasInitialSizeSet;
|
|
||||||
|
|
||||||
void SetFormPosAndSize(double scale = 1, bool force = false)
|
|
||||||
{
|
|
||||||
if (!force)
|
|
||||||
{
|
|
||||||
if (WindowState == FormWindowState.Maximized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mp.Fullscreen)
|
|
||||||
{
|
|
||||||
CycleFullscreen(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Screen screen = Screen.FromControl(this);
|
|
||||||
int autoFitHeight = Convert.ToInt32(screen.WorkingArea.Height * mp.Autofit);
|
|
||||||
|
|
||||||
if (mp.VideoSize.Height == 0 || mp.VideoSize.Width == 0 ||
|
|
||||||
mp.VideoSize.Width / (float)mp.VideoSize.Height < App.MinimumAspectRatio)
|
|
||||||
|
|
||||||
mp.VideoSize = new Size((int)(autoFitHeight * (16 / 9.0)), autoFitHeight);
|
|
||||||
|
|
||||||
Size size = mp.VideoSize;
|
|
||||||
int height = size.Height;
|
|
||||||
|
|
||||||
if (App.RememberHeight)
|
|
||||||
{
|
|
||||||
if (WasInitialSizeSet)
|
|
||||||
height = ClientSize.Height;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
height = autoFitHeight;
|
|
||||||
WasInitialSizeSet = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
height = Convert.ToInt32(height * scale);
|
|
||||||
int width = Convert.ToInt32(height * size.Width / (double)size.Height);
|
|
||||||
|
|
||||||
if (height > screen.WorkingArea.Height * 0.9)
|
|
||||||
{
|
|
||||||
height = Convert.ToInt32(screen.WorkingArea.Height * 0.9);
|
|
||||||
width = Convert.ToInt32(height * size.Width / (double)size.Height);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (width > screen.WorkingArea.Width * 0.9)
|
|
||||||
{
|
|
||||||
width = Convert.ToInt32(screen.WorkingArea.Width * 0.9);
|
|
||||||
height = Convert.ToInt32(width * size.Height / (double)size.Width);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (height < screen.WorkingArea.Height * mp.AutofitSmaller)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
NativeHelp.AddWindowBorders(Handle, ref rect);
|
|
||||||
int left = middlePos.X - rect.Width / 2;
|
|
||||||
int top = middlePos.Y - rect.Height / 2;
|
|
||||||
|
|
||||||
Screen[] screens = Screen.AllScreens;
|
|
||||||
int minLeft = screens.Select(val => val.WorkingArea.X).Min();
|
|
||||||
int maxRight = screens.Select(val => val.WorkingArea.Right).Max();
|
|
||||||
int minTop = screens.Select(val => val.WorkingArea.Y).Min();
|
|
||||||
int maxBottom = screens.Select(val => val.WorkingArea.Bottom).Max();
|
|
||||||
|
|
||||||
if (left < minLeft)
|
|
||||||
left = minLeft;
|
|
||||||
|
|
||||||
if (left + rect.Width > maxRight)
|
|
||||||
left = maxRight - rect.Width;
|
|
||||||
|
|
||||||
if (top < minTop)
|
|
||||||
top = minTop;
|
|
||||||
|
|
||||||
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 */);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CycleFullscreen(bool enabled)
|
|
||||||
{
|
|
||||||
mp.Fullscreen = enabled;
|
|
||||||
|
|
||||||
if (enabled)
|
|
||||||
{
|
|
||||||
if (WindowState != FormWindowState.Maximized || FormBorderStyle != FormBorderStyle.None)
|
|
||||||
{
|
|
||||||
if (WindowState == FormWindowState.Maximized)
|
|
||||||
WindowState = FormWindowState.Minimized;
|
|
||||||
|
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
|
||||||
WindowState = FormWindowState.Maximized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (WindowState == FormWindowState.Maximized && FormBorderStyle == FormBorderStyle.None)
|
|
||||||
{
|
|
||||||
WindowState = FormWindowState.Normal;
|
|
||||||
|
|
||||||
if (mp.Border)
|
|
||||||
FormBorderStyle = FormBorderStyle.Sizable;
|
|
||||||
else
|
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
|
||||||
|
|
||||||
SetFormPosAndSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildMenu()
|
|
||||||
{
|
|
||||||
string content = File.ReadAllText(mp.InputConfPath);
|
|
||||||
var items = CommandItem.GetItems(content);
|
|
||||||
|
|
||||||
if (!content.Contains("#menu:"))
|
|
||||||
{
|
|
||||||
var defaultItems = CommandItem.GetItems(Properties.Resources.inputConf);
|
|
||||||
|
|
||||||
foreach (CommandItem item in items)
|
|
||||||
foreach (CommandItem defaultItem in defaultItems)
|
|
||||||
if (item.Command == defaultItem.Command)
|
|
||||||
defaultItem.Input = item.Input;
|
|
||||||
|
|
||||||
items = defaultItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (CommandItem item in items)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(item.Path))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
string path = item.Path.Replace("&", "&&");
|
|
||||||
|
|
||||||
MenuItem menuItem = ContextMenu.Add(path, () => {
|
|
||||||
try {
|
|
||||||
mp.command(item.Command);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Msg.ShowException(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (menuItem != null)
|
|
||||||
menuItem.ShortcutKeyDisplayString = item.Input + " ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FileLoaded()
|
|
||||||
{
|
|
||||||
string path = mp.get_property_string("path");
|
|
||||||
|
|
||||||
BeginInvoke(new Action(() => {
|
|
||||||
if (path.Contains("://"))
|
|
||||||
Text = mp.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;
|
|
||||||
ProgressTimer.Interval = interval;
|
|
||||||
UpdateProgressBar();
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (RecentFiles.Contains(path))
|
|
||||||
RecentFiles.Remove(path);
|
|
||||||
|
|
||||||
RecentFiles.Insert(0, path);
|
|
||||||
|
|
||||||
while (RecentFiles.Count > App.RecentCount)
|
|
||||||
RecentFiles.RemoveAt(App.RecentCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override CreateParams CreateParams {
|
|
||||||
get {
|
|
||||||
CreateParams cp = base.CreateParams;
|
|
||||||
cp.Style |= 0x00020000 /* WS_MINIMIZEBOX */;
|
|
||||||
return cp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void WndProc(ref Message m)
|
|
||||||
{
|
|
||||||
//Debug.WriteLine(m);
|
|
||||||
|
|
||||||
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 0x319: // WM_APPCOMMAND
|
|
||||||
if (mp.WindowHandle != IntPtr.Zero)
|
|
||||||
Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
|
|
||||||
break;
|
|
||||||
case 0x0200: // WM_MOUSEMOVE
|
|
||||||
{
|
|
||||||
Point pos = PointToClient(Cursor.Position);
|
|
||||||
mp.command($"mouse {pos.X} {pos.Y}");
|
|
||||||
if (CursorHelp.IsPosDifferent(LastCursorPosChanged)) CursorHelp.Show();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x2a3: // WM_MOUSELEAVE
|
|
||||||
mp.command("mouse 1 1"); // osc won't always auto hide
|
|
||||||
break;
|
|
||||||
case 0x203: // Native.WM.LBUTTONDBLCLK
|
|
||||||
{
|
|
||||||
Point pos = PointToClient(Cursor.Position);
|
|
||||||
mp.command($"mouse {pos.X} {pos.Y} 0 double");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 0x02E0: // WM_DPICHANGED
|
|
||||||
if (!WasShown)
|
|
||||||
break;
|
|
||||||
var r2 = Marshal.PtrToStructure<Native.RECT>(m.LParam);
|
|
||||||
Native.SetWindowPos(Handle, IntPtr.Zero, r2.Left, r2.Top, r2.Width, r2.Height, 0);
|
|
||||||
break;
|
|
||||||
case 0x0214: // WM_SIZING
|
|
||||||
var rc = Marshal.PtrToStructure<Native.RECT>(m.LParam);
|
|
||||||
var r = rc;
|
|
||||||
NativeHelp.SubtractWindowBorders(Handle, ref r);
|
|
||||||
int c_w = r.Right - r.Left, c_h = r.Bottom - r.Top;
|
|
||||||
Size s = mp.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_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());
|
|
||||||
|
|
||||||
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);
|
|
||||||
m.Result = new IntPtr(1);
|
|
||||||
return;
|
|
||||||
case 0x004A: // WM_COPYDATA
|
|
||||||
var copyData = (Native.COPYDATASTRUCT)m.GetLParam(typeof(Native.COPYDATASTRUCT));
|
|
||||||
string[] files = copyData.lpData.Split('\n');
|
|
||||||
string mode = files[0];
|
|
||||||
files = files.Skip(1).ToArray();
|
|
||||||
|
|
||||||
switch (mode)
|
|
||||||
{
|
|
||||||
case "single":
|
|
||||||
mp.Load(files, true, Control.ModifierKeys.HasFlag(Keys.Control));
|
|
||||||
break;
|
|
||||||
case "queue":
|
|
||||||
foreach (string file in files)
|
|
||||||
mp.commandv("loadfile", file, "append");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Activate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.Msg == TaskbarButtonCreatedMessage && mp.TaskbarProgress)
|
|
||||||
{
|
|
||||||
Taskbar = new Taskbar(Handle);
|
|
||||||
ProgressTimer.Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.WndProc(ref m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CursorTimer_Tick(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
|
|
||||||
{
|
|
||||||
LastCursorPosChanged = Control.MousePosition;
|
|
||||||
LastCursorChangedTickCount = Environment.TickCount;
|
|
||||||
}
|
|
||||||
else if (Environment.TickCount - LastCursorChangedTickCount > 1500 &&
|
|
||||||
!IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) &&
|
|
||||||
Form.ActiveForm == this && !ContextMenu.Visible)
|
|
||||||
|
|
||||||
CursorHelp.Hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
private 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value));
|
|
||||||
|
|
||||||
void PropChangeAid(string value) => mp.Aid = value;
|
|
||||||
|
|
||||||
void PropChangeSid(string value) => mp.Sid = value;
|
|
||||||
|
|
||||||
void PropChangeVid(string value) => mp.Vid = value;
|
|
||||||
|
|
||||||
void PropChangeEdition(int value) => mp.Edition = value;
|
|
||||||
|
|
||||||
void PropChangeWindowScale(double value)
|
|
||||||
{
|
|
||||||
if (value != 1)
|
|
||||||
{
|
|
||||||
BeginInvoke(new Action(() => SetFormPosAndSize(value)));
|
|
||||||
mp.command("no-osd set window-scale 1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PropChangeBorder(bool enabled) {
|
|
||||||
mp.Border = enabled;
|
|
||||||
|
|
||||||
BeginInvoke(new Action(() => {
|
|
||||||
if (!IsFullscreen)
|
|
||||||
{
|
|
||||||
if (mp.Border && FormBorderStyle == FormBorderStyle.None)
|
|
||||||
FormBorderStyle = FormBorderStyle.Sizable;
|
|
||||||
|
|
||||||
if (!mp.Border && FormBorderStyle == FormBorderStyle.Sizable)
|
|
||||||
FormBorderStyle = FormBorderStyle.None;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PropChangePause(bool enabled)
|
|
||||||
{
|
|
||||||
if (Taskbar != null && mp.TaskbarProgress)
|
|
||||||
{
|
|
||||||
if (enabled)
|
|
||||||
Taskbar.SetState(TaskbarStates.Paused);
|
|
||||||
else
|
|
||||||
Taskbar.SetState(TaskbarStates.Normal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnLoad(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnLoad(e);
|
|
||||||
|
|
||||||
if (mp.GPUAPI != "vulkan")
|
|
||||||
mp.VideoSizeAutoResetEvent.WaitOne(App.StartThreshold);
|
|
||||||
|
|
||||||
SetFormPosAndSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnShown(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnShown(e);
|
|
||||||
|
|
||||||
if (mp.GPUAPI == "vulkan")
|
|
||||||
mp.ProcessCommandLine(false);
|
|
||||||
|
|
||||||
ToolStripRendererEx.ForegroundColor = Theme.Current.GetWinFormsColor("menu-foreground");
|
|
||||||
ToolStripRendererEx.BackgroundColor = Theme.Current.GetWinFormsColor("menu-background");
|
|
||||||
ToolStripRendererEx.SelectionColor = Theme.Current.GetWinFormsColor("menu-highlight");
|
|
||||||
ToolStripRendererEx.BorderColor = Theme.Current.GetWinFormsColor("menu-border");
|
|
||||||
ToolStripRendererEx.CheckedColor = Theme.Current.GetWinFormsColor("menu-checked");
|
|
||||||
|
|
||||||
BuildMenu();
|
|
||||||
ContextMenuStrip = ContextMenu;
|
|
||||||
WPF.WPF.Init();
|
|
||||||
System.Windows.Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
|
|
||||||
Cursor.Position = new Point(Cursor.Position.X + 1, Cursor.Position.Y);
|
|
||||||
WasShown = true;
|
|
||||||
mp.LoadScripts();
|
|
||||||
Task.Run(() => App.Extension = new Extension());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnResize(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnResize(e);
|
|
||||||
|
|
||||||
if (mp.IsLogoVisible)
|
|
||||||
mp.ShowLogo();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnFormClosing(e);
|
|
||||||
|
|
||||||
if (WindowState == FormWindowState.Normal)
|
|
||||||
{
|
|
||||||
RegHelp.SetObject(App.RegPath, "PosX", Left + Width / 2);
|
|
||||||
RegHelp.SetObject(App.RegPath, "PosY", Top + Height / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
RegHelp.SetObject(App.RegPath, "Recent", RecentFiles.ToArray());
|
|
||||||
|
|
||||||
if (mp.IsQuitNeeded)
|
|
||||||
mp.commandv("quit");
|
|
||||||
|
|
||||||
if (!mp.ShutdownAutoResetEvent.WaitOne(10000))
|
|
||||||
Msg.ShowError("Shutdown thread failed to complete within 10 seconds.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnMouseDown(MouseEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnMouseDown(e);
|
|
||||||
|
|
||||||
if (WindowState == FormWindowState.Normal &&
|
|
||||||
e.Button == MouseButtons.Left &&
|
|
||||||
e.Y < ClientSize.Height * 0.9)
|
|
||||||
{
|
|
||||||
var HTCAPTION = new IntPtr(2);
|
|
||||||
Native.ReleaseCapture();
|
|
||||||
Native.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Width - e.Location.X < 10 && e.Location.Y < 10)
|
|
||||||
mp.commandv("quit");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDragEnter(DragEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnDragEnter(e);
|
|
||||||
|
|
||||||
if (e.Data.GetDataPresent(DataFormats.FileDrop) || e.Data.GetDataPresent(DataFormats.Text))
|
|
||||||
e.Effect = DragDropEffects.Copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDragDrop(DragEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnDragDrop(e);
|
|
||||||
|
|
||||||
if (e.Data.GetDataPresent(DataFormats.FileDrop))
|
|
||||||
mp.Load(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));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnLostFocus(EventArgs e)
|
|
||||||
{
|
|
||||||
base.OnLostFocus(e);
|
|
||||||
CursorHelp.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnKeyDown(KeyEventArgs e)
|
|
||||||
{
|
|
||||||
e.SuppressKeyPress = true; // prevent beep using alt key
|
|
||||||
base.OnKeyDown(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int 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);
|
|
||||||
|
|
||||||
[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 int mpv_set_option(IntPtr mpvHandle, byte[] name, mpv_format format, ref long data);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
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);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int mpv_get_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref double data);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref byte[] data);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int 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);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern int mpv_unobserve_property(IntPtr mpvHandle, UInt64 registered_reply_userdata);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern void mpv_free(IntPtr data);
|
|
||||||
|
|
||||||
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
||||||
public static extern IntPtr mpv_wait_event(IntPtr mpvHandle, double timeout);
|
|
||||||
|
|
||||||
public enum mpv_error
|
|
||||||
{
|
|
||||||
MPV_ERROR_SUCCESS = 0,
|
|
||||||
MPV_ERROR_EVENT_QUEUE_FULL = -1,
|
|
||||||
MPV_ERROR_NOMEM = -2,
|
|
||||||
MPV_ERROR_UNINITIALIZED = -3,
|
|
||||||
MPV_ERROR_INVALID_PARAMETER = -4,
|
|
||||||
MPV_ERROR_OPTION_NOT_FOUND = -5,
|
|
||||||
MPV_ERROR_OPTION_FORMAT = -6,
|
|
||||||
MPV_ERROR_OPTION_ERROR = -7,
|
|
||||||
MPV_ERROR_PROPERTY_NOT_FOUND = -8,
|
|
||||||
MPV_ERROR_PROPERTY_FORMAT = -9,
|
|
||||||
MPV_ERROR_PROPERTY_UNAVAILABLE = -10,
|
|
||||||
MPV_ERROR_PROPERTY_ERROR = -11,
|
|
||||||
MPV_ERROR_COMMAND = -12,
|
|
||||||
MPV_ERROR_LOADING_FAILED = -13,
|
|
||||||
MPV_ERROR_AO_INIT_FAILED = -14,
|
|
||||||
MPV_ERROR_VO_INIT_FAILED = -15,
|
|
||||||
MPV_ERROR_NOTHING_TO_PLAY = -16,
|
|
||||||
MPV_ERROR_UNKNOWN_FORMAT = -17,
|
|
||||||
MPV_ERROR_UNSUPPORTED = -18,
|
|
||||||
MPV_ERROR_NOT_IMPLEMENTED = -19,
|
|
||||||
MPV_ERROR_GENERIC = -20
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum mpv_event_id
|
|
||||||
{
|
|
||||||
MPV_EVENT_NONE = 0,
|
|
||||||
MPV_EVENT_SHUTDOWN = 1,
|
|
||||||
MPV_EVENT_LOG_MESSAGE = 2,
|
|
||||||
MPV_EVENT_GET_PROPERTY_REPLY = 3,
|
|
||||||
MPV_EVENT_SET_PROPERTY_REPLY = 4,
|
|
||||||
MPV_EVENT_COMMAND_REPLY = 5,
|
|
||||||
MPV_EVENT_START_FILE = 6,
|
|
||||||
MPV_EVENT_END_FILE = 7,
|
|
||||||
MPV_EVENT_FILE_LOADED = 8,
|
|
||||||
MPV_EVENT_TRACKS_CHANGED = 9,
|
|
||||||
MPV_EVENT_TRACK_SWITCHED = 10,
|
|
||||||
MPV_EVENT_IDLE = 11,
|
|
||||||
MPV_EVENT_PAUSE = 12,
|
|
||||||
MPV_EVENT_UNPAUSE = 13,
|
|
||||||
MPV_EVENT_TICK = 14,
|
|
||||||
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
|
|
||||||
MPV_EVENT_CLIENT_MESSAGE = 16,
|
|
||||||
MPV_EVENT_VIDEO_RECONFIG = 17,
|
|
||||||
MPV_EVENT_AUDIO_RECONFIG = 18,
|
|
||||||
MPV_EVENT_METADATA_UPDATE = 19,
|
|
||||||
MPV_EVENT_SEEK = 20,
|
|
||||||
MPV_EVENT_PLAYBACK_RESTART = 21,
|
|
||||||
MPV_EVENT_PROPERTY_CHANGE = 22,
|
|
||||||
MPV_EVENT_CHAPTER_CHANGE = 23,
|
|
||||||
MPV_EVENT_QUEUE_OVERFLOW = 24,
|
|
||||||
MPV_EVENT_HOOK = 25
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum mpv_format
|
|
||||||
{
|
|
||||||
MPV_FORMAT_NONE = 0,
|
|
||||||
MPV_FORMAT_STRING = 1,
|
|
||||||
MPV_FORMAT_OSD_STRING = 2,
|
|
||||||
MPV_FORMAT_FLAG = 3,
|
|
||||||
MPV_FORMAT_INT64 = 4,
|
|
||||||
MPV_FORMAT_DOUBLE = 5,
|
|
||||||
MPV_FORMAT_NODE = 6,
|
|
||||||
MPV_FORMAT_NODE_ARRAY = 7,
|
|
||||||
MPV_FORMAT_NODE_MAP = 8,
|
|
||||||
MPV_FORMAT_BYTE_ARRAY = 9
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum mpv_log_level
|
|
||||||
{
|
|
||||||
MPV_LOG_LEVEL_NONE = 0,
|
|
||||||
MPV_LOG_LEVEL_FATAL = 10,
|
|
||||||
MPV_LOG_LEVEL_ERROR = 20,
|
|
||||||
MPV_LOG_LEVEL_WARN = 30,
|
|
||||||
MPV_LOG_LEVEL_INFO = 40,
|
|
||||||
MPV_LOG_LEVEL_V = 50,
|
|
||||||
MPV_LOG_LEVEL_DEBUG = 60,
|
|
||||||
MPV_LOG_LEVEL_TRACE = 70,
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum mpv_end_file_reason
|
|
||||||
{
|
|
||||||
MPV_END_FILE_REASON_EOF = 0,
|
|
||||||
MPV_END_FILE_REASON_STOP = 2,
|
|
||||||
MPV_END_FILE_REASON_QUIT = 3,
|
|
||||||
MPV_END_FILE_REASON_ERROR = 4,
|
|
||||||
MPV_END_FILE_REASON_REDIRECT = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct mpv_event_log_message
|
|
||||||
{
|
|
||||||
public string prefix;
|
|
||||||
public string level;
|
|
||||||
public string text;
|
|
||||||
public mpv_log_level log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct mpv_event
|
|
||||||
{
|
|
||||||
public mpv_event_id event_id;
|
|
||||||
public int error;
|
|
||||||
public UInt64 reply_userdata;
|
|
||||||
public IntPtr data;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct mpv_event_client_message
|
|
||||||
{
|
|
||||||
public int num_args;
|
|
||||||
public IntPtr args;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct mpv_event_property
|
|
||||||
{
|
|
||||||
public string name;
|
|
||||||
public mpv_format format;
|
|
||||||
public IntPtr data;
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
public struct mpv_event_end_file
|
|
||||||
{
|
|
||||||
public int reason;
|
|
||||||
public int error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,941 +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()
|
|
||||||
{
|
|
||||||
LoadLibrary("mpv-1.dll");
|
|
||||||
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 = RegHelp.GetString(App.RegPath, "ConfigFolder");
|
|
||||||
|
|
||||||
if (!Directory.Exists(_ConfigFolder))
|
|
||||||
{
|
|
||||||
string appdataFolder = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv.net\\";
|
|
||||||
string appdataFolderMpv = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\";
|
|
||||||
|
|
||||||
using (TaskDialog<string> td = new TaskDialog<string>())
|
|
||||||
{
|
|
||||||
td.MainInstruction = "Choose a settings folder.";
|
|
||||||
td.AddCommandLink(@"AppData\Roaming\mpv.net", appdataFolder, appdataFolder);
|
|
||||||
td.AddCommandLink(@"AppData\Roaming\mpv", appdataFolderMpv, appdataFolderMpv);
|
|
||||||
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"))
|
|
||||||
RegHelp.SetObject(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 (PathHelp.GetBaseName(LastHistoryPath) != "" && totalMinutes > 1)
|
|
||||||
File.AppendAllText(ConfigFolder + "history.txt", DateTime.Now.ToString().Substring(0, 16) +
|
|
||||||
" " + totalMinutes.ToString().PadLeft(3) + " " + PathHelp.GetBaseName(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
45
setup.iss
@@ -1,45 +0,0 @@
|
|||||||
#define MyAppName "mpv.net"
|
|
||||||
#define MyAppExeName "mpvnet.exe"
|
|
||||||
|
|
||||||
#ifndef arch
|
|
||||||
#define arch "x64"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if arch == "x64"
|
|
||||||
#define MyAppSourceDir "mpv.net\bin\x64"
|
|
||||||
#define MyAppVersion GetFileVersion("mpv.net\bin\x64\mpvnet.exe")
|
|
||||||
#else
|
|
||||||
#define MyAppSourceDir "mpv.net\bin\x86"
|
|
||||||
#define MyAppVersion GetFileVersion("mpv.net\bin\x86\mpvnet.exe")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[Setup]
|
|
||||||
AppId={{9AA2B100-BEF3-44D0-B819-D8FC3C4D557D}}
|
|
||||||
AppName={#MyAppName}
|
|
||||||
AppVersion={#MyAppVersion}
|
|
||||||
AppPublisher=Frank Skare (stax76)
|
|
||||||
#if arch == "x64"
|
|
||||||
ArchitecturesInstallIn64BitMode=x64
|
|
||||||
#endif
|
|
||||||
Compression=lzma2
|
|
||||||
DefaultDirName={commonpf}\{#MyAppName}
|
|
||||||
OutputBaseFilename=mpv.net-setup-{#arch}-{#MyAppVersion}
|
|
||||||
OutputDir={#GetEnv('USERPROFILE')}\Desktop
|
|
||||||
DefaultGroupName={#MyAppName}
|
|
||||||
SetupIconFile=mpv.net\mpvnet.ico
|
|
||||||
UninstallDisplayIcon={app}\{#MyAppExeName}
|
|
||||||
|
|
||||||
[Icons]
|
|
||||||
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
|
|
||||||
|
|
||||||
[Files]
|
|
||||||
Source: "{#MyAppSourceDir}\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
|
|
||||||
Source: "{#MyAppSourceDir}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs; Excludes: "System.Management.Automation.xml"
|
|
||||||
|
|
||||||
[Run]
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "Associate video file extensions?"; Flags: postinstall unchecked runascurrentuser runhidden nowait; Parameters: "--reg-file-assoc video"
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "Associate audio file extensions?"; Flags: postinstall unchecked runascurrentuser runhidden nowait; Parameters: "--reg-file-assoc audio"
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Description: "Associate image file extensions?"; Flags: postinstall unchecked runascurrentuser runhidden nowait; Parameters: "--reg-file-assoc image"
|
|
||||||
|
|
||||||
[UninstallRun]
|
|
||||||
Filename: "{app}\{#MyAppExeName}"; Flags: runascurrentuser runhidden; Parameters: "--reg-file-assoc unreg"
|
|
||||||
@@ -5,4 +5,4 @@
|
|||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||||
</startup>
|
</startup>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Documents;
|
using System.Windows.Documents;
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
|
|
||||||
using Tommy;
|
using Tommy;
|
||||||
|
using mpvnet;
|
||||||
|
|
||||||
namespace DynamicGUI
|
namespace DynamicGUI
|
||||||
{
|
{
|
||||||
@@ -60,10 +61,10 @@ namespace DynamicGUI
|
|||||||
baseSetting.File = setting["file"];
|
baseSetting.File = setting["file"];
|
||||||
baseSetting.Filter = setting["filter"];
|
baseSetting.Filter = setting["filter"];
|
||||||
|
|
||||||
if (setting.HasKey("help")) baseSetting.Help = setting["help"];
|
if (setting.HasKey("help")) baseSetting.Help = setting["help"];
|
||||||
if (setting.HasKey("url")) baseSetting.URL = setting["url"];
|
if (setting.HasKey("url")) baseSetting.URL = setting["url"];
|
||||||
if (setting.HasKey("width")) baseSetting.Width = setting["width"];
|
if (setting.HasKey("width")) baseSetting.Width = setting["width"];
|
||||||
if (setting.HasKey("type")) baseSetting.Type = setting["type"];
|
if (setting.HasKey("type")) baseSetting.Type = setting["type"];
|
||||||
|
|
||||||
settingsList.Add(baseSetting);
|
settingsList.Add(baseSetting);
|
||||||
}
|
}
|
||||||
@@ -73,26 +74,29 @@ namespace DynamicGUI
|
|||||||
|
|
||||||
public class ConfItem
|
public class ConfItem
|
||||||
{
|
{
|
||||||
public string Name { get; set; } = "";
|
|
||||||
public string Value { get; set; } = "";
|
|
||||||
public string Comment { get; set; } = "";
|
public string Comment { get; set; } = "";
|
||||||
public string LineComment { get; set; } = "";
|
|
||||||
public string Section { get; set; } = "";
|
|
||||||
public string File { get; set; } = "";
|
public string File { get; set; } = "";
|
||||||
|
public string LineComment { get; set; } = "";
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public string Section { get; set; } = "";
|
||||||
|
public string Value { get; set; } = "";
|
||||||
|
|
||||||
public bool IsSectionItem { get; set; }
|
public bool IsSectionItem { get; set; }
|
||||||
public SettingBase SettingBase { get; set; }
|
public SettingBase SettingBase { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class SettingBase
|
public abstract class SettingBase
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
|
||||||
public string File { get; set; }
|
|
||||||
public string Value { get; set; }
|
|
||||||
public string Help { get; set; }
|
|
||||||
public string Default { get; set; }
|
public string Default { get; set; }
|
||||||
public string URL { get; set; }
|
public string File { get; set; }
|
||||||
public string Filter { get; set; }
|
public string Filter { get; set; }
|
||||||
|
public string Help { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string StartValue { get; set; }
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
public string URL { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
public ConfItem ConfItem { get; set; }
|
public ConfItem ConfItem { get; set; }
|
||||||
}
|
}
|
||||||
@@ -113,7 +117,7 @@ namespace DynamicGUI
|
|||||||
|
|
||||||
public OptionSetting OptionSetting { get; set; }
|
public OptionSetting OptionSetting { get; set; }
|
||||||
|
|
||||||
private string _Text;
|
string _Text;
|
||||||
|
|
||||||
public string Text
|
public string Text
|
||||||
{
|
{
|
||||||
@@ -144,9 +148,9 @@ namespace DynamicGUI
|
|||||||
|
|
||||||
public class HyperlinkEx : Hyperlink
|
public class HyperlinkEx : Hyperlink
|
||||||
{
|
{
|
||||||
private void HyperLinkEx_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
void HyperLinkEx_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||||
{
|
{
|
||||||
Process.Start(e.Uri.AbsoluteUri);
|
ProcessHelp.ShellExecute(e.Uri.AbsoluteUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetURL(string url)
|
public void SetURL(string url)
|
||||||
73
src/DynamicGUI/OptionSettingControl.xaml
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Name="OptionSettingControl1" x:Class="DynamicGUI.OptionSettingControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
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:local="clr-namespace:DynamicGUI"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800">
|
||||||
|
|
||||||
|
<Grid Margin="20,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="TitleTextBox"
|
||||||
|
FontSize="24"
|
||||||
|
Margin="0,10"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Foreground="{Binding Theme.Heading}"
|
||||||
|
Background="{Binding Theme.Background}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ItemsControl x:Name="ItemsControl">
|
||||||
|
<ItemsControl.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<WrapPanel Orientation="Vertical">
|
||||||
|
<RadioButton
|
||||||
|
x:Name="RadioButton"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
IsChecked="{Binding Checked}"
|
||||||
|
GroupName="{Binding OptionSetting.Name}"
|
||||||
|
Content="{Binding Text}"
|
||||||
|
FontSize="16"
|
||||||
|
FontWeight="Normal"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
Foreground="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Theme.Foreground}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
x:Name="ItemHelpTextBox"
|
||||||
|
TextWrapping="WrapWithOverflow"
|
||||||
|
Text="{Binding Help}"
|
||||||
|
Visibility="{Binding Visibility}"
|
||||||
|
Margin="10,0,0,0" BorderThickness="0"
|
||||||
|
IsReadOnly="True" Padding="7,0,0,0"
|
||||||
|
MinHeight="0"
|
||||||
|
Foreground="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Theme.Foreground2}"
|
||||||
|
Background="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Theme.Background}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</WrapPanel>
|
||||||
|
</DataTemplate>
|
||||||
|
</ItemsControl.ItemTemplate>
|
||||||
|
</ItemsControl>
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
x:Name="HelpTextBox"
|
||||||
|
TextWrapping="WrapWithOverflow"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Margin="0,10,0,0"
|
||||||
|
Foreground="{Binding Theme.Foreground}"
|
||||||
|
Background="{Binding Theme.Background}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
|
||||||
|
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -1,35 +1,49 @@
|
|||||||
using System.Windows;
|
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
using mpvnet;
|
||||||
|
|
||||||
namespace DynamicGUI
|
namespace DynamicGUI
|
||||||
{
|
{
|
||||||
public partial class OptionSettingControl : UserControl, ISettingControl
|
public partial class OptionSettingControl : UserControl, ISettingControl
|
||||||
{
|
{
|
||||||
private OptionSetting OptionSetting;
|
OptionSetting OptionSetting;
|
||||||
|
|
||||||
public OptionSettingControl(OptionSetting optionSetting)
|
public OptionSettingControl(OptionSetting optionSetting)
|
||||||
{
|
{
|
||||||
OptionSetting = optionSetting;
|
OptionSetting = optionSetting;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
DataContext = this;
|
||||||
TitleTextBox.Text = optionSetting.Name;
|
TitleTextBox.Text = optionSetting.Name;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(optionSetting.Help))
|
if (string.IsNullOrEmpty(optionSetting.Help))
|
||||||
HelpTextBox.Visibility = Visibility.Collapsed;
|
HelpTextBox.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
HelpTextBox.Text = optionSetting.Help;
|
HelpTextBox.Text = optionSetting.Help;
|
||||||
ItemsControl.ItemsSource = optionSetting.Options;
|
ItemsControl.ItemsSource = optionSetting.Options;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(optionSetting.URL))
|
if (string.IsNullOrEmpty(optionSetting.URL))
|
||||||
LinkTextBlock.Visibility = Visibility.Collapsed;
|
LinkTextBlock.Visibility = Visibility.Collapsed;
|
||||||
|
|
||||||
Link.SetURL(optionSetting.URL);
|
Link.SetURL(optionSetting.URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _SearchableText;
|
public Theme Theme {
|
||||||
|
get => Theme.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
string _SearchableText;
|
||||||
|
|
||||||
public string SearchableText {
|
public string SearchableText {
|
||||||
get {
|
get {
|
||||||
if (_SearchableText is null)
|
if (_SearchableText is null)
|
||||||
{
|
{
|
||||||
_SearchableText = TitleTextBox.Text + HelpTextBox.Text;
|
_SearchableText = TitleTextBox.Text + HelpTextBox.Text;
|
||||||
|
|
||||||
foreach (var i in OptionSetting.Options)
|
foreach (var i in OptionSetting.Options)
|
||||||
_SearchableText += i.Text + i.Help + i.Name;
|
_SearchableText += i.Text + i.Help + i.Name;
|
||||||
|
|
||||||
_SearchableText = _SearchableText.ToLower();
|
_SearchableText = _SearchableText.ToLower();
|
||||||
}
|
}
|
||||||
return _SearchableText;
|
return _SearchableText;
|
||||||
@@ -39,4 +53,4 @@ namespace DynamicGUI
|
|||||||
public SettingBase SettingBase => OptionSetting;
|
public SettingBase SettingBase => OptionSetting;
|
||||||
public bool Contains(string searchString) => SearchableText.Contains(searchString.ToLower());
|
public bool Contains(string searchString) => SearchableText.Contains(searchString.ToLower());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
65
src/DynamicGUI/StringSettingControl.xaml
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<UserControl
|
||||||
|
x:Name="StringSettingControl1" x:Class="DynamicGUI.StringSettingControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
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:local="clr-namespace:DynamicGUI"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450"
|
||||||
|
d:DesignWidth="800">
|
||||||
|
|
||||||
|
<Grid Margin="20,0">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBox
|
||||||
|
x:Name="TitleTextBox"
|
||||||
|
FontSize="24"
|
||||||
|
Margin="0,10"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Foreground="{Binding Theme.Heading}"
|
||||||
|
Background="{Binding Theme.Background}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Grid Margin="0,0,0,10">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox
|
||||||
|
x:Name="ValueTextBox"
|
||||||
|
Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Width="150"
|
||||||
|
Height="20"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Foreground="{Binding Theme.Foreground}"
|
||||||
|
Background="{Binding Theme.Background}"
|
||||||
|
CaretBrush="{Binding Theme.Foreground}"
|
||||||
|
TextChanged="ValueTextBox_TextChanged"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="Button"
|
||||||
|
Height="20"
|
||||||
|
Grid.Column="1"
|
||||||
|
Margin="5,0,0,0"
|
||||||
|
Width="20"
|
||||||
|
Click="Button_Click"
|
||||||
|
>...</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<TextBox
|
||||||
|
x:Name="HelpTextBox"
|
||||||
|
TextWrapping="WrapWithOverflow"
|
||||||
|
BorderThickness="0"
|
||||||
|
IsReadOnly="True"
|
||||||
|
Foreground="{Binding Theme.Foreground}"
|
||||||
|
Background="{Binding Theme.Background}"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextBlock x:Name="LinkTextBlock" Margin="0,10">
|
||||||
|
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -1,39 +1,51 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics;
|
using mpvnet;
|
||||||
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
using WinForms = System.Windows.Forms;
|
using WinForms = System.Windows.Forms;
|
||||||
|
|
||||||
namespace DynamicGUI
|
namespace DynamicGUI
|
||||||
{
|
{
|
||||||
public partial class StringSettingControl : UserControl, ISettingControl
|
public partial class StringSettingControl : UserControl, ISettingControl
|
||||||
{
|
{
|
||||||
private StringSetting StringSetting;
|
StringSetting StringSetting;
|
||||||
|
|
||||||
public StringSettingControl(StringSetting stringSetting)
|
public StringSettingControl(StringSetting stringSetting)
|
||||||
{
|
{
|
||||||
StringSetting = stringSetting;
|
StringSetting = stringSetting;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
DataContext = this;
|
||||||
TitleTextBox.Text = stringSetting.Name;
|
TitleTextBox.Text = stringSetting.Name;
|
||||||
HelpTextBox.Text = stringSetting.Help;
|
HelpTextBox.Text = stringSetting.Help;
|
||||||
ValueTextBox.Text = StringSetting.Value;
|
ValueTextBox.Text = StringSetting.Value;
|
||||||
|
|
||||||
if (StringSetting.Width > 0)
|
if (StringSetting.Width > 0)
|
||||||
ValueTextBox.Width = StringSetting.Width;
|
ValueTextBox.Width = StringSetting.Width;
|
||||||
|
|
||||||
if (StringSetting.Type != "folder" && StringSetting.Type != "color")
|
if (StringSetting.Type != "folder" && StringSetting.Type != "color")
|
||||||
Button.Visibility = Visibility.Hidden;
|
Button.Visibility = Visibility.Hidden;
|
||||||
|
|
||||||
Link.SetURL(StringSetting.URL);
|
Link.SetURL(StringSetting.URL);
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(stringSetting.URL))
|
if (string.IsNullOrEmpty(stringSetting.URL))
|
||||||
LinkTextBlock.Visibility = Visibility.Collapsed;
|
LinkTextBlock.Visibility = Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _SearchableText;
|
public Theme Theme {
|
||||||
|
get => Theme.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
string _SearchableText;
|
||||||
|
|
||||||
public string SearchableText {
|
public string SearchableText {
|
||||||
get {
|
get {
|
||||||
if (_SearchableText is null)
|
if (_SearchableText is null)
|
||||||
_SearchableText = (TitleTextBox.Text + HelpTextBox.Text +ValueTextBox.Text).ToLower();
|
_SearchableText = (TitleTextBox.Text + HelpTextBox.Text +ValueTextBox.Text).ToLower();
|
||||||
|
|
||||||
return _SearchableText;
|
return _SearchableText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +59,7 @@ namespace DynamicGUI
|
|||||||
set => StringSetting.Value = value;
|
set => StringSetting.Value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Button_Click(object sender, RoutedEventArgs e)
|
void Button_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
switch (StringSetting.Type)
|
switch (StringSetting.Type)
|
||||||
{
|
{
|
||||||
@@ -56,6 +68,7 @@ namespace DynamicGUI
|
|||||||
{
|
{
|
||||||
d.Description = "Choose a folder.";
|
d.Description = "Choose a folder.";
|
||||||
d.SelectedPath = ValueTextBox.Text;
|
d.SelectedPath = ValueTextBox.Text;
|
||||||
|
|
||||||
if (d.ShowDialog() == WinForms.DialogResult.OK)
|
if (d.ShowDialog() == WinForms.DialogResult.OK)
|
||||||
ValueTextBox.Text = d.SelectedPath;
|
ValueTextBox.Text = d.SelectedPath;
|
||||||
}
|
}
|
||||||
@@ -65,7 +78,8 @@ namespace DynamicGUI
|
|||||||
{
|
{
|
||||||
dialog.FullOpen = true;
|
dialog.FullOpen = true;
|
||||||
|
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
if (!string.IsNullOrEmpty(ValueTextBox.Text))
|
if (!string.IsNullOrEmpty(ValueTextBox.Text))
|
||||||
{
|
{
|
||||||
Color col = GetColor(ValueTextBox.Text);
|
Color col = GetColor(ValueTextBox.Text);
|
||||||
@@ -80,7 +94,7 @@ namespace DynamicGUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update();
|
void ValueTextBox_TextChanged(object sender, TextChangedEventArgs e) => Update();
|
||||||
|
|
||||||
Color GetColor(string value)
|
Color GetColor(string value)
|
||||||
{
|
{
|
||||||
@@ -104,9 +118,12 @@ namespace DynamicGUI
|
|||||||
if (StringSetting.Type == "color")
|
if (StringSetting.Type == "color")
|
||||||
{
|
{
|
||||||
Color c = Colors.Transparent;
|
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);
|
ValueTextBox.Background = new SolidColorBrush(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,7 +381,8 @@ namespace Tommy
|
|||||||
|
|
||||||
public override TomlNode this[string key] {
|
public override TomlNode this[string key] {
|
||||||
get {
|
get {
|
||||||
if (RawTable.TryGetValue(key, out var result)) return result;
|
if (RawTable.TryGetValue(key, out var result))
|
||||||
|
return result;
|
||||||
|
|
||||||
var lazy = new TomlLazy(this);
|
var lazy = new TomlLazy(this);
|
||||||
RawTable[key] = lazy;
|
RawTable[key] = lazy;
|
||||||
@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
|
|||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
// associated with an assembly.
|
// associated with an assembly.
|
||||||
[assembly: AssemblyTitle("RatingExtension")]
|
[assembly: AssemblyTitle("RatingExtension")]
|
||||||
[assembly: AssemblyDescription("")]
|
[assembly: AssemblyDescription("RatingExtension")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("Frank Skare (stax76)")]
|
||||||
[assembly: AssemblyProduct("RatingExtension")]
|
[assembly: AssemblyProduct("RatingExtension")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2017-2019 Frank Skare (stax76)")]
|
[assembly: AssemblyCopyright("Copyright (C) 2017-2020 Frank Skare (stax76)")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
135
src/Extensions/RatingExtension/RatingExtension.cs
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
// This extension writes a rating to the filename of rated videos when mpv.net shuts down.
|
||||||
|
|
||||||
|
// The input.conf setup:
|
||||||
|
|
||||||
|
// KP0 script-message rate-file 0 #menu: Extensions > Rating > 0stars
|
||||||
|
// KP1 script-message rate-file 1 #menu: Extensions > Rating > 1stars
|
||||||
|
// KP2 script-message rate-file 2 #menu: Extensions > Rating > 2stars
|
||||||
|
// KP3 script-message rate-file 3 #menu: Extensions > Rating > 3stars
|
||||||
|
// KP4 script-message rate-file 4 #menu: Extensions > Rating > 4stars
|
||||||
|
// KP5 script-message rate-file 5 #menu: Extensions > Rating > 5stars
|
||||||
|
// _ ignore #menu: Extensions > Rating > -
|
||||||
|
// _ script-message rate-file about #menu: Extensions > Rating > About
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Composition;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using Microsoft.VisualBasic.FileIO;
|
||||||
|
|
||||||
|
using mpvnet;
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace RatingExtension // the assembly name must end with 'Extension'!
|
||||||
|
{
|
||||||
|
[Export(typeof(IExtension))]
|
||||||
|
public class RatingExtension : IExtension
|
||||||
|
{
|
||||||
|
//Script script = new Script();
|
||||||
|
|
||||||
|
// dictionory to store the filename and the rating
|
||||||
|
Dictionary<string, int> Dic = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
string FileToDelete;
|
||||||
|
DateTime DeleteTime;
|
||||||
|
|
||||||
|
public RatingExtension() // plugin initialization
|
||||||
|
{
|
||||||
|
Core.ClientMessage += ClientMessage; //handles keys defined in input.conf
|
||||||
|
Core.Shutdown += Shutdown; // handles MPV_EVENT_SHUTDOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles MPV_EVENT_SHUTDOWN
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
foreach (var i in Dic)
|
||||||
|
{
|
||||||
|
string filepath = i.Key;
|
||||||
|
int rating = i.Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string basename = Path.GetFileNameWithoutExtension(filepath);
|
||||||
|
|
||||||
|
for (int x = 0; x < 6; x++)
|
||||||
|
if (basename.Contains(" (" + x + "stars)"))
|
||||||
|
basename = basename.Replace(" (" + x + "stars)", "");
|
||||||
|
|
||||||
|
basename += $" ({rating}stars)";
|
||||||
|
|
||||||
|
string newPath = Path.Combine(Path.GetDirectoryName(filepath),
|
||||||
|
basename + Path.GetExtension(filepath));
|
||||||
|
|
||||||
|
if (filepath.ToLower() != newPath.ToLower())
|
||||||
|
File.Move(filepath, newPath);
|
||||||
|
|
||||||
|
File.SetLastWriteTime(newPath, DateTime.Now);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//handles keys defined in input.conf
|
||||||
|
void ClientMessage(string[] args)
|
||||||
|
{
|
||||||
|
if (args[0] != "rate-file")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (int.TryParse(args[1], out int rating))
|
||||||
|
{
|
||||||
|
string path = Core.get_property_string("path");
|
||||||
|
|
||||||
|
if (!File.Exists(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (rating == 0 || rating == 1)
|
||||||
|
Delete(rating);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Dic[path] = rating;
|
||||||
|
Core.commandv("show-text", $"Rating: {rating}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (args[1] == "about")
|
||||||
|
MessageBox.Show($"This extension writes a rating to the filename of rated videos " +
|
||||||
|
"when mpv.net shuts down." + BR2 +
|
||||||
|
"The input.conf defaults contain key bindings for this extension to set ratings.",
|
||||||
|
"Rating Extension");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Delete(int rating)
|
||||||
|
{
|
||||||
|
if (rating == 0)
|
||||||
|
{
|
||||||
|
FileToDelete = Core.get_property_string("path");
|
||||||
|
DeleteTime = DateTime.Now;
|
||||||
|
Core.commandv("show-text", "Press 1 to delete file", "5000");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TimeSpan ts = DateTime.Now - DeleteTime;
|
||||||
|
string path = Core.get_property_string("path");
|
||||||
|
|
||||||
|
if (FileToDelete == path && ts.TotalSeconds < 5 && File.Exists(FileToDelete))
|
||||||
|
{
|
||||||
|
Core.command("playlist-remove current");
|
||||||
|
int pos = Core.get_property_int("playlist-pos");
|
||||||
|
|
||||||
|
if (pos == -1)
|
||||||
|
{
|
||||||
|
int count = Core.get_property_int("playlist-count");
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
Core.set_property_int("playlist-pos", count - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(2000);
|
||||||
|
FileSystem.DeleteFile(FileToDelete, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,46 +13,33 @@
|
|||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<TargetFrameworkProfile />
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<DebugSymbols>true</DebugSymbols>
|
||||||
<OutputPath>..\..\mpv.net\bin\x86\Extensions\RatingExtension\</OutputPath>
|
<OutputPath>bin\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<LangVersion>7.3</LangVersion>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
|
||||||
<OutputPath>..\..\mpv.net\bin\x86\Extensions\RatingExtension\</OutputPath>
|
<OutputPath>bin\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<PlatformTarget>x86</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<LangVersion>7.3</LangVersion>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
|
||||||
<DebugSymbols>true</DebugSymbols>
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x64\Extensions\RatingExtension\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
|
||||||
<OutputPath>..\..\mpv.net\bin\x64\Extensions\RatingExtension\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<DebugType>pdbonly</DebugType>
|
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Reference Include="Microsoft.VisualBasic" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.ComponentModel.Composition" />
|
<Reference Include="System.ComponentModel.Composition" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Drawing" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
@@ -63,9 +50,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="RatingExtension.cs" />
|
<Compile Include="RatingExtension.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="ScriptDevelopment.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\mpv.net\mpv.net.csproj">
|
<ProjectReference Include="..\..\mpv.net.csproj">
|
||||||
<Project>{1751f378-8edf-4b62-be6d-304c7c287089}</Project>
|
<Project>{1751f378-8edf-4b62-be6d-304c7c287089}</Project>
|
||||||
<Name>mpv.net</Name>
|
<Name>mpv.net</Name>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
42
src/Extensions/RatingExtension/ScriptDevelopment.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
//// This script adds dynamic menu items for profile switching.
|
||||||
|
|
||||||
|
//// In input.conf add a menu item called 'Profiles'
|
||||||
|
|
||||||
|
//using mpvnet;
|
||||||
|
//using System.ComponentModel;
|
||||||
|
//using System.Linq;
|
||||||
|
|
||||||
|
//class Script
|
||||||
|
//{
|
||||||
|
// MainForm MainForm;
|
||||||
|
// CorePlayer Core;
|
||||||
|
|
||||||
|
// public Script()
|
||||||
|
// {
|
||||||
|
// Core = Global.Core;
|
||||||
|
// MainForm = MainForm.Instance;
|
||||||
|
// MainForm.ContextMenu.Opening += ContextMenu_Opening;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// void ContextMenu_Opening(object sender, CancelEventArgs e)
|
||||||
|
// {
|
||||||
|
// MenuItem menuItem = MainForm.FindMenuItem("My Menu");
|
||||||
|
|
||||||
|
// if (menuItem == null)
|
||||||
|
// {
|
||||||
|
// Terminal.WriteError("Profiles menu item not found.", "switch-profile-context-menu.cs");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// menuItem.DropDownItems.Clear();
|
||||||
|
// var editionTracks = Core.MediaTracks.Where(track => track.Type == "e");
|
||||||
|
|
||||||
|
// foreach (int i in new[] {1, 2, 3})
|
||||||
|
// {
|
||||||
|
// MenuItem mi = new MenuItem(i.ToString());
|
||||||
|
// mi.Action = () => { Core.commandv("show-text", i.ToString()); };
|
||||||
|
// menuItem.DropDownItems.Add(mi);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
214
src/Misc/App.cs
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public static class App
|
||||||
|
{
|
||||||
|
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; } = "height-session";
|
||||||
|
|
||||||
|
public static bool RememberWindowPosition { 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;
|
||||||
|
public static bool AutoLoadFolder { get; set; } = true;
|
||||||
|
public static bool Queue { get; set; }
|
||||||
|
public static bool UpdateCheck { get; set; }
|
||||||
|
|
||||||
|
public static int StartThreshold { get; set; } = 1500;
|
||||||
|
public static int RecentCount { get; set; } = 15;
|
||||||
|
|
||||||
|
public static float MinimumAspectRatio { get; set; } = 1.2f;
|
||||||
|
|
||||||
|
public static Extension Extension { get; set; }
|
||||||
|
|
||||||
|
public static bool IsDarkMode {
|
||||||
|
get => (DarkMode == "system" && Sys.IsDarkTheme) || DarkMode == "always";
|
||||||
|
}
|
||||||
|
|
||||||
|
static AppSettings _Settings;
|
||||||
|
|
||||||
|
public static AppSettings Settings {
|
||||||
|
get {
|
||||||
|
if (_Settings == null)
|
||||||
|
_Settings = SettingsManager.Load();
|
||||||
|
|
||||||
|
return _Settings;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
var useless1 = Core.ConfigFolder;
|
||||||
|
var useless2 = Core.Conf;
|
||||||
|
|
||||||
|
foreach (var i in Conf)
|
||||||
|
ProcessProperty(i.Key, i.Value, true);
|
||||||
|
|
||||||
|
if (DebugMode)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string filePath = Core.ConfigFolder + "mpvnet-debug.log";
|
||||||
|
|
||||||
|
if (File.Exists(filePath))
|
||||||
|
File.Delete(filePath);
|
||||||
|
|
||||||
|
Trace.Listeners.Add(new TextWriterTraceListener(filePath));
|
||||||
|
Trace.AutoFlush = true;
|
||||||
|
|
||||||
|
//if (App.DebugMode)
|
||||||
|
// Trace.WriteLine("");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Msg.ShowException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InitTheme();
|
||||||
|
|
||||||
|
Core.Shutdown += Shutdown;
|
||||||
|
Core.Initialized += Initialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InitTheme()
|
||||||
|
{
|
||||||
|
string themeContent = null;
|
||||||
|
|
||||||
|
if (File.Exists(Core.ConfigFolder + "theme.conf"))
|
||||||
|
themeContent = File.ReadAllText(Core.ConfigFolder + "theme.conf");
|
||||||
|
|
||||||
|
Theme.Init(
|
||||||
|
themeContent,
|
||||||
|
Properties.Resources.theme,
|
||||||
|
IsDarkMode ? DarkTheme : LightTheme);
|
||||||
|
|
||||||
|
ToolStripRendererEx.ForegroundColor = Theme.Current.GetWinFormsColor("menu-foreground");
|
||||||
|
ToolStripRendererEx.BackgroundColor = Theme.Current.GetWinFormsColor("menu-background");
|
||||||
|
ToolStripRendererEx.SelectionColor = Theme.Current.GetWinFormsColor("menu-highlight");
|
||||||
|
ToolStripRendererEx.BorderColor = Theme.Current.GetWinFormsColor("menu-border");
|
||||||
|
ToolStripRendererEx.CheckedColor = Theme.Current.GetWinFormsColor("menu-checked");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RunTask(Action action)
|
||||||
|
{
|
||||||
|
Task.Run(() => {
|
||||||
|
try {
|
||||||
|
action.Invoke();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ShowException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string Version {
|
||||||
|
get {
|
||||||
|
return "Copyright (C) 2000-2021 mpv.net/mpv/mplayer\n" +
|
||||||
|
$"mpv.net {Application.ProductVersion} ({File.GetLastWriteTime(Application.ExecutablePath).ToShortDateString()})\n" +
|
||||||
|
$"{Core.get_property_string("mpv-version")} ({File.GetLastWriteTime(Folder.Startup + "mpv-1.dll").ToShortDateString()})\nffmpeg {Core.get_property_string("ffmpeg-version")}\nMIT License";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowException(object obj)
|
||||||
|
{
|
||||||
|
if (IsStartedFromTerminal)
|
||||||
|
Terminal.WriteError(obj.ToString());
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (obj is Exception e)
|
||||||
|
Msg.ShowException(e);
|
||||||
|
else
|
||||||
|
Msg.ShowError(obj.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowError(string title, string msg = null)
|
||||||
|
{
|
||||||
|
if (IsStartedFromTerminal)
|
||||||
|
{
|
||||||
|
if (title != null)
|
||||||
|
Terminal.WriteError(title);
|
||||||
|
|
||||||
|
if (msg != null)
|
||||||
|
Terminal.WriteError(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Msg.ShowError(title, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Initialized()
|
||||||
|
{
|
||||||
|
if (RememberVolume)
|
||||||
|
{
|
||||||
|
Core.set_property_int("volume", Settings.Volume);
|
||||||
|
Core.set_property_string("mute", Settings.Mute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Shutdown()
|
||||||
|
{
|
||||||
|
Settings.Volume = Core.get_property_int("volume");
|
||||||
|
Settings.Mute = Core.get_property_string("mute");
|
||||||
|
|
||||||
|
SettingsManager.Save(Settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (string i in File.ReadAllLines(ConfPath))
|
||||||
|
if (i.Contains("=") && !i.StartsWith("#"))
|
||||||
|
_Conf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
|
||||||
|
}
|
||||||
|
return _Conf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ProcessProperty(string name, string value, bool writeError = false)
|
||||||
|
{
|
||||||
|
switch (name)
|
||||||
|
{
|
||||||
|
case "remember-window-position": RememberWindowPosition = 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": StartSize = value; return true;
|
||||||
|
case "process-instance": ProcessInstance = value; return true;
|
||||||
|
case "dark-mode": DarkMode = value; return true;
|
||||||
|
case "start-threshold": StartThreshold = value.ToInt(); return true;
|
||||||
|
case "recent-count": RecentCount = value.ToInt(); return true;
|
||||||
|
case "minimum-aspect-ratio": MinimumAspectRatio = value.ToFloat(); return true;
|
||||||
|
case "dark-theme": DarkTheme = value.Trim('\'', '"'); return true;
|
||||||
|
case "light-theme": LightTheme = value.Trim('\'', '"'); return true;
|
||||||
|
case "video-file-extensions": CorePlayer.VideoTypes = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
|
||||||
|
case "audio-file-extensions": CorePlayer.AudioTypes = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
|
||||||
|
case "image-file-extensions": CorePlayer.ImageTypes = value.Split(" ,;".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); return true;
|
||||||
|
default:
|
||||||
|
if (writeError)
|
||||||
|
Terminal.WriteError($"unknown mpvnet.conf property: {name}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/Misc/CSharpScriptHost.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.CodeDom.Compiler;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using Microsoft.CSharp;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
class CSharpScriptHost
|
||||||
|
{
|
||||||
|
static List<object> References = new List<object>();
|
||||||
|
|
||||||
|
public static void ExecuteScriptsInFolder(string folder)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(folder))
|
||||||
|
foreach (string file in Directory.GetFiles(folder, "*.cs"))
|
||||||
|
App.RunTask(() => Execute(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Execute(string file)
|
||||||
|
{
|
||||||
|
string code = File.ReadAllText(file);
|
||||||
|
string filename = Path.GetFileNameWithoutExtension(file) + " " + GetMD5(code) + ".dll";
|
||||||
|
string outputFile = Path.Combine(Path.GetTempPath(), filename);
|
||||||
|
|
||||||
|
if (!File.Exists(outputFile))
|
||||||
|
Compile(outputFile, file);
|
||||||
|
|
||||||
|
if (File.Exists(outputFile))
|
||||||
|
{
|
||||||
|
object instance = Assembly.LoadFile(outputFile).CreateInstance("Script");
|
||||||
|
|
||||||
|
if (instance != null)
|
||||||
|
References.Add(instance);
|
||||||
|
else
|
||||||
|
Terminal.WriteError("Failed to initialize script.", outputFile.FileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Compile(string outputFile, string file)
|
||||||
|
{
|
||||||
|
CSharpCodeProvider provider = new CSharpCodeProvider();
|
||||||
|
CompilerParameters parameters = new CompilerParameters();
|
||||||
|
|
||||||
|
string[] dependencies = {
|
||||||
|
Folder.Startup + "mpvnet.exe",
|
||||||
|
"Microsoft.VisualBasic.dll",
|
||||||
|
"System.Core.dll", "System.Data.dll", "System.dll", "System.Drawing.dll", "System.Web.dll",
|
||||||
|
"System.Windows.Forms.dll", "System.Xaml.dll", "System.Xml.dll", "System.Xml.Linq.dll",
|
||||||
|
"WPF\\PresentationCore.dll", "WPF\\PresentationFramework.dll", "WPF\\WindowsBase.dll"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (string i in dependencies)
|
||||||
|
parameters.ReferencedAssemblies.Add(i);
|
||||||
|
|
||||||
|
parameters.OutputAssembly = outputFile;
|
||||||
|
CompilerResults results = provider.CompileAssemblyFromFile(parameters, file);
|
||||||
|
|
||||||
|
var errors = results.Errors.Cast<CompilerError>().Select(i => "Line Number " +
|
||||||
|
i.Line + "\n" + "Error Number: " + i.ErrorNumber + "\n" + i.ErrorText);
|
||||||
|
|
||||||
|
if (errors.Count() > 0)
|
||||||
|
Terminal.WriteError(string.Join(BR2, errors), Path.GetFileName(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetMD5(string code)
|
||||||
|
{
|
||||||
|
using (MD5 md5 = MD5.Create())
|
||||||
|
{
|
||||||
|
byte[] inputBuffer = Encoding.UTF8.GetBytes(code);
|
||||||
|
byte[] hashBuffer = md5.ComputeHash(inputBuffer);
|
||||||
|
return BitConverter.ToString(md5.ComputeHash(inputBuffer)).Replace("-", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
382
src/Misc/Commands.cs
Normal file
@@ -0,0 +1,382 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
using VB = Microsoft.VisualBasic;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public class Commands
|
||||||
|
{
|
||||||
|
public static void Execute(string id, string[] args = null)
|
||||||
|
{
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case "add-files-to-playlist": OpenFiles("append"); break; // deprecated 2019
|
||||||
|
case "cycle-audio": CycleAudio(); break;
|
||||||
|
case "execute-mpv-command": Msg.ShowError("Command was removed, reset input.conf."); break;
|
||||||
|
case "load-audio": LoadAudio(); break;
|
||||||
|
case "load-sub": LoadSubtitle(); break;
|
||||||
|
case "open-conf-folder": ProcessHelp.ShellExecute(Core.ConfigFolder); break;
|
||||||
|
case "open-files": OpenFiles(args); break;
|
||||||
|
case "open-optical-media": Open_DVD_Or_BD_Folder(); break;
|
||||||
|
case "open-url": OpenURL(); break;
|
||||||
|
case "playlist-first": PlaylistFirst(); break;
|
||||||
|
case "playlist-last": PlaylistLast(); break;
|
||||||
|
case "scale-window": ScaleWindow(float.Parse(args[0], CultureInfo.InvariantCulture)); break;
|
||||||
|
case "shell-execute": ProcessHelp.ShellExecute(args[0]); break;
|
||||||
|
case "show-about": ShowDialog(typeof(AboutWindow)); break;
|
||||||
|
case "show-audio-devices": ShowTextWithEditor("audio-device-list", Core.get_property_osd_string("audio-device-list")); break;
|
||||||
|
case "show-command-palette": ShowDialog(typeof(CommandPaletteWindow)); break;
|
||||||
|
case "show-commands": ShowCommands(); break;
|
||||||
|
case "show-conf-editor": ShowDialog(typeof(ConfWindow)); break;
|
||||||
|
case "show-decoders": ShowTextWithEditor("decoder-list", mpvHelp.GetDecoders()); break;
|
||||||
|
case "show-demuxers": ShowTextWithEditor("demuxer-lavf-list", mpvHelp.GetDemuxers()); break;
|
||||||
|
case "show-history": ShowHistory(); break;
|
||||||
|
case "show-info": ShowInfo(); break;
|
||||||
|
case "show-input-editor": ShowDialog(typeof(InputWindow)); break;
|
||||||
|
case "show-keys": ShowTextWithEditor("input-key-list", Core.get_property_string("input-key-list").Replace(",", BR)); break;
|
||||||
|
case "show-media-search": ShowDialog(typeof(EverythingWindow)); break;
|
||||||
|
case "show-playlist": ShowPlaylist(); break;
|
||||||
|
case "show-profiles": ShowTextWithEditor("profile-list", mpvHelp.GetProfiles()); break;
|
||||||
|
case "show-properties": ShowProperties(); break;
|
||||||
|
case "show-protocols": ShowTextWithEditor("protocol-list", mpvHelp.GetProtocols()); break;
|
||||||
|
case "show-setup-dialog": ShowDialog(typeof(SetupWindow)); break;
|
||||||
|
case "show-text": ShowText(args[0], Convert.ToInt32(args[1]), Convert.ToInt32(args[2])); break;
|
||||||
|
case "update-check": UpdateCheck.CheckOnline(true); break;
|
||||||
|
case "window-scale": WindowScale(float.Parse(args[0], CultureInfo.InvariantCulture)); break;
|
||||||
|
|
||||||
|
default: Msg.ShowError($"No command '{id}' found."); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InvokeOnMainThread(Action action) => MainForm.Instance.BeginInvoke(action);
|
||||||
|
|
||||||
|
public static void ShowDialog(Type winType)
|
||||||
|
{
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
Window win = Activator.CreateInstance(winType) as Window;
|
||||||
|
new WindowInteropHelper(win).Owner = MainForm.Instance.Handle;
|
||||||
|
win.ShowDialog();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenFiles(params string[] args)
|
||||||
|
{
|
||||||
|
bool append = Control.ModifierKeys.HasFlag(Keys.Control);
|
||||||
|
bool loadFolder = true;
|
||||||
|
|
||||||
|
foreach (string arg in args)
|
||||||
|
{
|
||||||
|
if (arg == "append") append = true;
|
||||||
|
if (arg == "no-folder") loadFolder = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
using (var d = new OpenFileDialog() { Multiselect = true })
|
||||||
|
if (d.ShowDialog() == DialogResult.OK)
|
||||||
|
Core.LoadFiles(d.FileNames, loadFolder, append);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Open_DVD_Or_BD_Folder()
|
||||||
|
{
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
using (var dialog = new FolderBrowserDialog())
|
||||||
|
{
|
||||||
|
dialog.Description = "Select a DVD or Blu-ray folder.";
|
||||||
|
dialog.ShowNewFolderButton = false;
|
||||||
|
|
||||||
|
if (dialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
Core.command("stop");
|
||||||
|
Thread.Sleep(500);
|
||||||
|
|
||||||
|
if (Directory.Exists(dialog.SelectedPath + "\\BDMV"))
|
||||||
|
{
|
||||||
|
Core.set_property_string("bluray-device", dialog.SelectedPath);
|
||||||
|
Core.LoadFiles(new[] { @"bd://" }, false, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Core.set_property_string("dvd-device", dialog.SelectedPath);
|
||||||
|
Core.LoadFiles(new[] { @"dvd://" }, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(Core.ConfigFolder + "history.txt"))
|
||||||
|
ProcessHelp.ShellExecute(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.") == DialogResult.OK)
|
||||||
|
|
||||||
|
File.WriteAllText(Core.ConfigFolder + "history.txt", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowInfo()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string performer, title, album, genre, date, duration, text = "";
|
||||||
|
long fileSize = 0;
|
||||||
|
string path = Core.get_property_string("path");
|
||||||
|
|
||||||
|
if (path.Contains("://"))
|
||||||
|
path = Core.get_property_string("media-title");
|
||||||
|
|
||||||
|
int width = Core.get_property_int("video-params/w");
|
||||||
|
int height = Core.get_property_int("video-params/h");
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
fileSize = new FileInfo(path).Length;
|
||||||
|
|
||||||
|
if (CorePlayer.AudioTypes.Contains(path.Ext()))
|
||||||
|
{
|
||||||
|
using (MediaInfo mediaInfo = new MediaInfo(path))
|
||||||
|
{
|
||||||
|
performer = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Performer");
|
||||||
|
title = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Title");
|
||||||
|
album = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Album");
|
||||||
|
genre = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Genre");
|
||||||
|
date = mediaInfo.GetInfo(MediaInfoStreamKind.General, "Recorded_Date");
|
||||||
|
duration = mediaInfo.GetInfo(MediaInfoStreamKind.Audio, "Duration/String");
|
||||||
|
|
||||||
|
if (performer != "") text += "Artist: " + performer + "\n";
|
||||||
|
if (title != "") text += "Title: " + title + "\n";
|
||||||
|
if (album != "") text += "Album: " + album + "\n";
|
||||||
|
if (genre != "") text += "Genre: " + genre + "\n";
|
||||||
|
if (date != "") text += "Year: " + date + "\n";
|
||||||
|
if (duration != "") text += "Length: " + duration + "\n";
|
||||||
|
text += "Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n";
|
||||||
|
text += "Type: " + path.Ext().ToUpper();
|
||||||
|
|
||||||
|
Core.commandv("show-text", text, "5000");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (CorePlayer.ImageTypes.Contains(path.Ext()))
|
||||||
|
{
|
||||||
|
using (MediaInfo mediaInfo = new MediaInfo(path))
|
||||||
|
{
|
||||||
|
text =
|
||||||
|
"Width: " + mediaInfo.GetInfo(MediaInfoStreamKind.Image, "Width") + "\n" +
|
||||||
|
"Height: " + mediaInfo.GetInfo(MediaInfoStreamKind.Image, "Height") + "\n" +
|
||||||
|
"Size: " + mediaInfo.GetInfo(MediaInfoStreamKind.General, "FileSize/String") + "\n" +
|
||||||
|
"Type: " + path.Ext().ToUpper();
|
||||||
|
|
||||||
|
Core.commandv("show-text", text, "5000");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) + ":" +
|
||||||
|
FormatTime(position.Seconds) + " / " +
|
||||||
|
FormatTime(duration2.TotalMinutes) + ":" +
|
||||||
|
FormatTime(duration2.Seconds) + "\n" +
|
||||||
|
$"{width} x {height}\n";
|
||||||
|
|
||||||
|
if (fileSize > 0)
|
||||||
|
text += Convert.ToInt32(fileSize / 1024.0 / 1024.0) + " MB\n";
|
||||||
|
|
||||||
|
text += $"{videoFormat}\n{audioCodec}";
|
||||||
|
|
||||||
|
Core.commandv("show-text", text, "5000");
|
||||||
|
string FormatTime(double value) => ((int)value).ToString("00");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
App.ShowException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void OpenURL()
|
||||||
|
{
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
string clipboard = System.Windows.Forms.Clipboard.GetText();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(clipboard) || (!clipboard.Contains("://") && !File.Exists(clipboard)) ||
|
||||||
|
clipboard.Contains("\n"))
|
||||||
|
{
|
||||||
|
App.ShowError("No URL found", "The clipboard does not contain a valid URL or file.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core.LoadFiles(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadSubtitle()
|
||||||
|
{
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
using (var d = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
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 filename in d.FileNames)
|
||||||
|
Core.commandv("sub-add", filename);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void LoadAudio()
|
||||||
|
{
|
||||||
|
InvokeOnMainThread(new Action(() => {
|
||||||
|
using (var d = new OpenFileDialog())
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
Core.commandv("audio-add", i);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void CycleAudio()
|
||||||
|
{
|
||||||
|
MediaTrack[] audioTracks = Core.MediaTracks.Where(track => track.Type == "a").ToArray();
|
||||||
|
int len = audioTracks.Length;
|
||||||
|
|
||||||
|
if (len < 1)
|
||||||
|
{
|
||||||
|
Core.commandv("show-text", "No audio tracks");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int aid = Core.get_property_int("aid");
|
||||||
|
|
||||||
|
if (len > 1)
|
||||||
|
{
|
||||||
|
if (++aid > len)
|
||||||
|
aid = 1;
|
||||||
|
|
||||||
|
Core.commandv("set", "aid", aid.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Core.commandv("show-text", aid + "/" + len + ": " + audioTracks[aid - 1].Text.Substring(3), "5000");
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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");
|
||||||
|
ShowTextWithEditor("command-list", PowerShell.InvokeAndReturnString(code, "json", json));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowProperties()
|
||||||
|
{
|
||||||
|
var props = Core.get_property_string("property-list").Split(',').OrderBy(prop => prop);
|
||||||
|
ShowTextWithEditor("property-list", string.Join(BR, props));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowTextWithEditor(string name, string text)
|
||||||
|
{
|
||||||
|
string file = Path.GetTempPath() + $"\\{name}.txt";
|
||||||
|
File.WriteAllText(file, BR + text.Trim() + BR);
|
||||||
|
ProcessHelp.ShellExecute(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ScaleWindow(float factor) => Core.RaiseScaleWindow(factor);
|
||||||
|
|
||||||
|
public static void WindowScale(float value) => Core.RaiseWindowScale(value);
|
||||||
|
|
||||||
|
public static void ShowText(string text, int duration = 0, int fontSize = 0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (duration == 0)
|
||||||
|
duration = Core.get_property_int("osd-duration");
|
||||||
|
|
||||||
|
if (fontSize == 0)
|
||||||
|
fontSize = Core.get_property_int("osd-font-size");
|
||||||
|
|
||||||
|
Core.command("show-text \"${osd-ass-cc/0}{\\\\fs" + fontSize +
|
||||||
|
"}${osd-ass-cc/1}" + text + "\" " + duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowPlaylist(string[] args = null)
|
||||||
|
{
|
||||||
|
int duration = 5000;
|
||||||
|
|
||||||
|
if (args?.Length == 1)
|
||||||
|
duration = Convert.ToInt32(args[0]);
|
||||||
|
|
||||||
|
var size = Core.get_property_number("osd-font-size");
|
||||||
|
Core.set_property_number("osd-font-size", 40);
|
||||||
|
Core.command("show-text ${playlist} " + duration);
|
||||||
|
|
||||||
|
App.RunTask(() => {
|
||||||
|
Thread.Sleep(6000);
|
||||||
|
Core.set_property_number("osd-font-size", size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1416
src/Misc/CorePlayer.cs
Normal file
@@ -1,10 +1,13 @@
|
|||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.Composition;
|
using System.ComponentModel.Composition;
|
||||||
using System.ComponentModel.Composition.Hosting;
|
using System.ComponentModel.Composition.Hosting;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
namespace mpvnet
|
namespace mpvnet
|
||||||
{
|
{
|
||||||
public class Extension
|
public class Extension
|
||||||
@@ -25,20 +28,23 @@ namespace mpvnet
|
|||||||
{
|
{
|
||||||
string[] knownExtensions = { "RatingExtension", "ScriptingExtension" };
|
string[] knownExtensions = { "RatingExtension", "ScriptingExtension" };
|
||||||
|
|
||||||
foreach (string path in Directory.GetDirectories(dir))
|
foreach (string extDir in Directory.GetDirectories(dir))
|
||||||
{
|
{
|
||||||
if (knownExtensions.Contains(Path.GetFileName(path)))
|
if (knownExtensions.Contains(Path.GetFileName(extDir)))
|
||||||
catalog.Catalogs.Add(new DirectoryCatalog(path, "*Extension.dll"));
|
catalog.Catalogs.Add(new DirectoryCatalog(extDir, Path.GetFileName(extDir) + ".dll"));
|
||||||
else
|
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.");
|
Terminal.WriteError("Failed to load extension:" + BR2 + extDir +
|
||||||
|
BR2 + "Only extensions that ship with mpv.net are allowed in <startup>\\extensions" +
|
||||||
|
BR2 + "User extensions have to use <config folder>\\extensions" +
|
||||||
|
BR2 + "Never 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))
|
if (Directory.Exists(dir))
|
||||||
foreach (string i in Directory.GetDirectories(dir))
|
foreach (string extDir in Directory.GetDirectories(dir))
|
||||||
catalog.Catalogs.Add(new DirectoryCatalog(i, "*Extension.dll"));
|
catalog.Catalogs.Add(new DirectoryCatalog(extDir, Path.GetFileName(extDir) + ".dll"));
|
||||||
|
|
||||||
if (catalog.Catalogs.Count > 0)
|
if (catalog.Catalogs.Count > 0)
|
||||||
{
|
{
|
||||||
@@ -48,7 +54,7 @@ namespace mpvnet
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Msg.ShowException(ex);
|
App.ShowException(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,4 +62,4 @@ namespace mpvnet
|
|||||||
public interface IExtension
|
public interface IExtension
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
116
src/Misc/ExtensionMethods.cs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
public static class TestStringExtension
|
||||||
|
{
|
||||||
|
public static bool ContainsEx(this string instance, string value)
|
||||||
|
{
|
||||||
|
if (instance != null && value != null)
|
||||||
|
return instance.Contains(value);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool StartsWithEx(this string instance, string value)
|
||||||
|
{
|
||||||
|
if (instance != null && value != null)
|
||||||
|
return instance.StartsWith(value);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConvertToStringExtension
|
||||||
|
{
|
||||||
|
public static string ToUpperEx(this string instance)
|
||||||
|
{
|
||||||
|
if (instance != null)
|
||||||
|
return instance.ToUpperInvariant();
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToLowerEx(this string instance)
|
||||||
|
{
|
||||||
|
if (instance != null)
|
||||||
|
return instance.ToLowerInvariant();
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TrimEx(this string instance)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return instance.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ConvertStringExtension
|
||||||
|
{
|
||||||
|
public static int ToInt(this string instance)
|
||||||
|
{
|
||||||
|
int.TryParse(instance, out int result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float ToFloat(this string instance)
|
||||||
|
{
|
||||||
|
float.TryParse(instance.Replace(",", "."), NumberStyles.Float,
|
||||||
|
CultureInfo.InvariantCulture, out float result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PathStringExtension
|
||||||
|
{
|
||||||
|
// return extension with lower case and without dot.
|
||||||
|
public static string Ext(this string instance)
|
||||||
|
{
|
||||||
|
if (instance == null)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return Path.GetExtension(instance).TrimStart('.').ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FileName(this string instance)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instance))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
int index = instance.LastIndexOf('\\');
|
||||||
|
|
||||||
|
if (index > -1)
|
||||||
|
return instance.Substring(index + 1);
|
||||||
|
|
||||||
|
index = instance.LastIndexOf('/');
|
||||||
|
|
||||||
|
if (index > -1)
|
||||||
|
return instance.Substring(index + 1);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure trailing directory separator char
|
||||||
|
public static string AddSep(this string instance)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instance))
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (!instance.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||||
|
instance = instance + Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIdenticalFolder(this string instance, string testFolder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(instance) || string.IsNullOrEmpty(testFolder))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return instance.ToLowerInvariant().AddSep() == testFolder.ToLowerInvariant().AddSep();
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Misc/Global.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public class Global
|
||||||
|
{
|
||||||
|
public static string BR = Environment.NewLine;
|
||||||
|
public static string BR2 = Environment.NewLine + Environment.NewLine;
|
||||||
|
|
||||||
|
public static CorePlayer Core { get; } = new CorePlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
191
src/Misc/GlobalHotkey.cs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
class GlobalHotkey
|
||||||
|
{
|
||||||
|
public static Dictionary<int, string> Commands { get; set; }
|
||||||
|
static int ID;
|
||||||
|
static IntPtr HWND;
|
||||||
|
|
||||||
|
public static void RegisterGlobalHotkeys(IntPtr hwnd)
|
||||||
|
{
|
||||||
|
HWND = hwnd;
|
||||||
|
string path = Core.ConfigFolder + "global-input.conf";
|
||||||
|
|
||||||
|
if (!File.Exists(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (string i in File.ReadAllLines(path))
|
||||||
|
{
|
||||||
|
string line = i.Trim();
|
||||||
|
|
||||||
|
if (line.StartsWith("#") || !line.Contains(" "))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ProcessGlobalHotkeyLine(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ProcessGlobalHotkeyLine(string line)
|
||||||
|
{
|
||||||
|
string key = line.Substring(0, line.IndexOf(" "));
|
||||||
|
string command = line.Substring(line.IndexOf(" ") + 1);
|
||||||
|
string[] parts = key.Split('+');
|
||||||
|
KeyModifiers mod = KeyModifiers.None;
|
||||||
|
int vk;
|
||||||
|
|
||||||
|
for (int i = 0; i < parts.Length - 1; i++)
|
||||||
|
{
|
||||||
|
string umod = parts[i].ToUpper();
|
||||||
|
|
||||||
|
if (umod == "ALT") mod |= KeyModifiers.Alt;
|
||||||
|
if (umod == "CTRL") mod |= KeyModifiers.Ctrl;
|
||||||
|
if (umod == "SHIFT") mod |= KeyModifiers.Shift;
|
||||||
|
if (umod == "WIN") mod |= KeyModifiers.Win;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = parts[parts.Length - 1];
|
||||||
|
|
||||||
|
if (key.Length == 1)
|
||||||
|
{
|
||||||
|
short result = VkKeyScanEx(key[0], GetKeyboardLayout(0));
|
||||||
|
|
||||||
|
int hi = result >> 8;
|
||||||
|
int lo = result & 0xFF;
|
||||||
|
|
||||||
|
if (lo == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vk = lo;
|
||||||
|
|
||||||
|
if ((hi & 1) == 1) mod |= KeyModifiers.Shift;
|
||||||
|
if ((hi & 2) == 2) mod |= KeyModifiers.Ctrl;
|
||||||
|
if ((hi & 4) == 4) mod |= KeyModifiers.Alt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
vk = mpv_to_VK(key);
|
||||||
|
|
||||||
|
if (Commands == null)
|
||||||
|
Commands = new Dictionary<int, string>();
|
||||||
|
|
||||||
|
if (vk > 0)
|
||||||
|
{
|
||||||
|
Commands[ID] = command.Trim();
|
||||||
|
bool success = RegisterHotKey(HWND, ID++, mod, vk);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
Terminal.WriteError(line + ": " + new Win32Exception().Message + "\n", "global-input.conf");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Execute(int id)
|
||||||
|
{
|
||||||
|
if (Commands.ContainsKey(id))
|
||||||
|
Core.command(Commands[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mpv_to_VK(string value)
|
||||||
|
{
|
||||||
|
switch (value.ToUpperEx())
|
||||||
|
{
|
||||||
|
case "NEXT" : return 0xB0; // VK_MEDIA_NEXT_TRACK
|
||||||
|
case "PREV" : return 0xB1; // VK_MEDIA_PREV_TRACK
|
||||||
|
case "STOP" : return 0xB2; // VK_MEDIA_STOP
|
||||||
|
case "PLAYPAUSE" : return 0xB3; // VK_MEDIA_PLAY_PAUSE
|
||||||
|
case "SLEEP" : return 0x5F; // VK_SLEEP
|
||||||
|
case "RIGHT" : return 0x27; // VK_RIGHT
|
||||||
|
case "UP" : return 0x26; // VK_UP
|
||||||
|
case "LEFT" : return 0x25; // VK_LEFT
|
||||||
|
case "DOWN" : return 0x28; // VK_DOWN
|
||||||
|
case "PGUP" : return 0x21; // VK_PRIOR
|
||||||
|
case "PGDWN" : return 0x22; // VK_NEXT
|
||||||
|
case "PAUSE" : return 0x13; // VK_PAUSE
|
||||||
|
case "PRINT" : return 0x2A; // VK_PRINT
|
||||||
|
case "HOME" : return 0x24; // VK_HOME
|
||||||
|
case "INS" : return 0x2D; // VK_INSERT
|
||||||
|
case "KP_INS" : return 0x2D; // VK_INSERT
|
||||||
|
case "DEL" : return 0x2E; // VK_DELETE
|
||||||
|
case "KP_DEL" : return 0x2E; // VK_DELETE
|
||||||
|
case "END" : return 0x23; // VK_END
|
||||||
|
case "F1" : return 0x70; // VK_F1
|
||||||
|
case "F2" : return 0x71; // VK_F2
|
||||||
|
case "F3" : return 0x72; // VK_F3
|
||||||
|
case "F4" : return 0x73; // VK_F4
|
||||||
|
case "F5" : return 0x74; // VK_F5
|
||||||
|
case "F6" : return 0x75; // VK_F6
|
||||||
|
case "F7" : return 0x76; // VK_F7
|
||||||
|
case "F8" : return 0x77; // VK_F8
|
||||||
|
case "F9" : return 0x78; // VK_F9
|
||||||
|
case "F10" : return 0x79; // VK_F10
|
||||||
|
case "F11" : return 0x7A; // VK_F11
|
||||||
|
case "F12" : return 0x7B; // VK_F12
|
||||||
|
case "F13" : return 0x7C; // VK_F13
|
||||||
|
case "F14" : return 0x7D; // VK_F14
|
||||||
|
case "F15" : return 0x7E; // VK_F15
|
||||||
|
case "F16" : return 0x7F; // VK_F16
|
||||||
|
case "F17" : return 0x80; // VK_F17
|
||||||
|
case "F18" : return 0x81; // VK_F18
|
||||||
|
case "F19" : return 0x82; // VK_F19
|
||||||
|
case "F20" : return 0x83; // VK_F20
|
||||||
|
case "F21" : return 0x84; // VK_F21
|
||||||
|
case "F22" : return 0x85; // VK_F22
|
||||||
|
case "F23" : return 0x86; // VK_F23
|
||||||
|
case "F24" : return 0x87; // VK_F24
|
||||||
|
case "ENTER" : return 0x0D; // VK_RETURN
|
||||||
|
case "KP_ENTER" : return 0x0D; // VK_RETURN
|
||||||
|
case "TAB" : return 0x09; // VK_TAB
|
||||||
|
case "MENU" : return 0x5D; // VK_APPS
|
||||||
|
case "CANCEL" : return 0x03; // VK_CANCEL
|
||||||
|
case "BS" : return 0x08; // VK_BACK
|
||||||
|
case "KP_DEC" : return 0x6E; // VK_DECIMAL
|
||||||
|
case "ESC" : return 0x1B; // VK_ESCAPE
|
||||||
|
case "KP0" : return 0x60; // VK_NUMPAD0
|
||||||
|
case "KP1" : return 0x61; // VK_NUMPAD1
|
||||||
|
case "KP2" : return 0x62; // VK_NUMPAD2
|
||||||
|
case "KP3" : return 0x63; // VK_NUMPAD3
|
||||||
|
case "KP4" : return 0x64; // VK_NUMPAD4
|
||||||
|
case "KP5" : return 0x65; // VK_NUMPAD5
|
||||||
|
case "KP6" : return 0x66; // VK_NUMPAD6
|
||||||
|
case "KP7" : return 0x67; // VK_NUMPAD7
|
||||||
|
case "KP8" : return 0x68; // VK_NUMPAD8
|
||||||
|
case "KP9" : return 0x69; // VK_NUMPAD9
|
||||||
|
case "FAVORITES" : return 0xAB; // VK_BROWSER_FAVORITES
|
||||||
|
case "SEARCH" : return 0xAA; // VK_BROWSER_SEARCH
|
||||||
|
case "MAIL" : return 0xB4; // VK_LAUNCH_MAIL
|
||||||
|
case "VOLUME_UP" : return 0xAF; // VK_VOLUME_UP
|
||||||
|
case "VOLUME_DOWN": return 0xAE; // VK_VOLUME_DOWN
|
||||||
|
case "MUTE" : return 0xAD; // VK_VOLUME_MUTE
|
||||||
|
case "SPACE" : return 0x20; // VK_SPACE
|
||||||
|
case "IDEOGRAPHIC_SPACE": return 0x20; // VK_SPACE
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
static extern IntPtr GetKeyboardLayout(uint idThread);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
static extern bool RegisterHotKey(IntPtr hWnd, int id, KeyModifiers fsModifiers, int vk);
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
enum KeyModifiers
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Alt = 1,
|
||||||
|
Ctrl = 2,
|
||||||
|
Shift = 4,
|
||||||
|
Win = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
189
src/Misc/Help.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public static class ProcessHelp
|
||||||
|
{
|
||||||
|
public static void Execute(string file, string arguments = null)
|
||||||
|
{
|
||||||
|
using (Process proc = new Process())
|
||||||
|
{
|
||||||
|
proc.StartInfo.FileName = file;
|
||||||
|
proc.StartInfo.Arguments = arguments;
|
||||||
|
proc.StartInfo.UseShellExecute = false;
|
||||||
|
proc.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShellExecute(string file, string arguments = null)
|
||||||
|
{
|
||||||
|
using (Process proc = new Process())
|
||||||
|
{
|
||||||
|
proc.StartInfo.FileName = file;
|
||||||
|
proc.StartInfo.Arguments = arguments;
|
||||||
|
proc.StartInfo.UseShellExecute = true;
|
||||||
|
proc.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 GetProfiles()
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
return PowerShell.InvokeAndReturnString(code, "json", json).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetDecoders()
|
||||||
|
{
|
||||||
|
string code = @"
|
||||||
|
foreach ($item in ($json | ConvertFrom-Json | foreach { $_ } | sort codec))
|
||||||
|
{
|
||||||
|
$item.codec + ' - ' + $item.description
|
||||||
|
}";
|
||||||
|
|
||||||
|
string json = Core.get_property_string("decoder-list");
|
||||||
|
return PowerShell.InvokeAndReturnString(code, "json", json).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetProtocols()
|
||||||
|
{
|
||||||
|
string list = Core.get_property_string("protocol-list");
|
||||||
|
return string.Join(BR, list.Split(',').OrderBy(a => a));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetDemuxers()
|
||||||
|
{
|
||||||
|
string list = Core.get_property_string("demuxer-lavf-list");
|
||||||
|
return string.Join(BR, list.Split(',').OrderBy(a => a));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RegistryHelp
|
||||||
|
{
|
||||||
|
public static string ApplicationKey { get; } = @"HKCU\Software\" + Application.ProductName;
|
||||||
|
|
||||||
|
public static void SetInt(string name, object value)
|
||||||
|
{
|
||||||
|
SetValue(ApplicationKey, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetString(string name, string value)
|
||||||
|
{
|
||||||
|
SetValue(ApplicationKey, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetValue(string name, object value)
|
||||||
|
{
|
||||||
|
using (RegistryKey regKey = GetRootKey(ApplicationKey).CreateSubKey(ApplicationKey.Substring(5), RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||||
|
regKey.SetValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetValue(string path, string name, object value)
|
||||||
|
{
|
||||||
|
using (RegistryKey regKey = GetRootKey(path).CreateSubKey(path.Substring(5), RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||||
|
regKey.SetValue(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetString(string name, string defaultValue = "")
|
||||||
|
{
|
||||||
|
object value = GetValue(ApplicationKey, name, defaultValue);
|
||||||
|
return !(value is string) ? defaultValue : value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetInt(string name, int defaultValue = 0)
|
||||||
|
{
|
||||||
|
object value = GetValue(ApplicationKey, name, defaultValue);
|
||||||
|
return !(value is int) ? defaultValue : (int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static object GetValue(string name) => GetValue(ApplicationKey, name, null);
|
||||||
|
|
||||||
|
public static object GetValue(string path, string name, object defaultValue = null)
|
||||||
|
{
|
||||||
|
using (RegistryKey regKey = GetRootKey(path).OpenSubKey(path.Substring(5)))
|
||||||
|
return regKey == null ? null : regKey.GetValue(name, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveKey(string path)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false);
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveValue(string path, string name)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
using (RegistryKey regKey = GetRootKey(path).OpenSubKey(path.Substring(5), true))
|
||||||
|
if (regKey != null)
|
||||||
|
regKey.DeleteValue(name, false);
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
static RegistryKey GetRootKey(string path)
|
||||||
|
{
|
||||||
|
switch (path.Substring(0, 4))
|
||||||
|
{
|
||||||
|
case "HKLM": return Registry.LocalMachine;
|
||||||
|
case "HKCU": return Registry.CurrentUser;
|
||||||
|
case "HKCR": return Registry.ClassesRoot;
|
||||||
|
default: throw new Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
231
src/Misc/Misc.cs
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public class Sys
|
||||||
|
{
|
||||||
|
public static bool IsDarkTheme {
|
||||||
|
get {
|
||||||
|
object value = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
|
||||||
|
|
||||||
|
if (value is null)
|
||||||
|
value = 1;
|
||||||
|
|
||||||
|
return (int)value == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StringLogicalComparer : IComparer, IComparer<string>
|
||||||
|
{
|
||||||
|
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern int StrCmpLogical(string x, string y);
|
||||||
|
|
||||||
|
int IComparer_Compare(object x, object y) => StrCmpLogical(x.ToString(), y.ToString());
|
||||||
|
int IComparer.Compare(object x, object y) => IComparer_Compare(x, y);
|
||||||
|
int IComparerOfString_Compare(string x, string y) => StrCmpLogical(x, y);
|
||||||
|
int IComparer<string>.Compare(string x, string y) => IComparerOfString_Compare(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Input
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FileAssociation
|
||||||
|
{
|
||||||
|
static string ExePath = Application.ExecutablePath;
|
||||||
|
static string ExeFilename = Path.GetFileName(Application.ExecutablePath);
|
||||||
|
static string ExeFilenameNoExt = Path.GetFileNameWithoutExtension(Application.ExecutablePath);
|
||||||
|
static string[] Types;
|
||||||
|
|
||||||
|
public static void Register(string[] types)
|
||||||
|
{
|
||||||
|
Types = types;
|
||||||
|
|
||||||
|
RegistryHelp.SetValue(@"HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\" + ExeFilename, null, ExePath);
|
||||||
|
RegistryHelp.SetValue(@"HKCR\Applications\" + ExeFilename, "FriendlyAppName", "mpv.net media player");
|
||||||
|
RegistryHelp.SetValue($@"HKCR\Applications\{ExeFilename}\shell\open\command", null, $"\"{ExePath}\" \"%1\"");
|
||||||
|
RegistryHelp.SetValue(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities", "ApplicationDescription", "mpv.net media player");
|
||||||
|
RegistryHelp.SetValue(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities", "ApplicationName", "mpv.net");
|
||||||
|
RegistryHelp.SetValue(@"HKCR\SystemFileAssociations\video\OpenWithList\" + ExeFilename, null, "");
|
||||||
|
RegistryHelp.SetValue(@"HKCR\SystemFileAssociations\audio\OpenWithList\" + ExeFilename, null, "");
|
||||||
|
RegistryHelp.SetValue(@"HKLM\SOFTWARE\RegisteredApplications", "mpv.net", @"SOFTWARE\Clients\Media\mpv.net\Capabilities");
|
||||||
|
|
||||||
|
foreach (string ext in Types)
|
||||||
|
{
|
||||||
|
RegistryHelp.SetValue($@"HKCR\Applications\{ExeFilename}\SupportedTypes", "." + ext, "");
|
||||||
|
RegistryHelp.SetValue($@"HKCR\" + "." + ext, null, ExeFilenameNoExt + "." + ext);
|
||||||
|
RegistryHelp.SetValue($@"HKCR\" + "." + ext + @"\OpenWithProgIDs", ExeFilenameNoExt + "." + ext, "");
|
||||||
|
|
||||||
|
if (CorePlayer.VideoTypes.Contains(ext))
|
||||||
|
RegistryHelp.SetValue(@"HKCR\" + "." + ext, "PerceivedType", "video");
|
||||||
|
|
||||||
|
if (CorePlayer.AudioTypes.Contains(ext))
|
||||||
|
RegistryHelp.SetValue(@"HKCR\" + "." + ext, "PerceivedType", "audio");
|
||||||
|
|
||||||
|
if (CorePlayer.ImageTypes.Contains(ext))
|
||||||
|
RegistryHelp.SetValue(@"HKCR\" + "." + ext, "PerceivedType", "image");
|
||||||
|
|
||||||
|
RegistryHelp.SetValue($@"HKCR\" + ExeFilenameNoExt + "." + ext + @"\shell\open\command", null, $"\"{ExePath}\" \"%1\"");
|
||||||
|
RegistryHelp.SetValue(@"HKLM\SOFTWARE\Clients\Media\mpv.net\Capabilities\FileAssociations", "." + ext, ExeFilenameNoExt + "." + ext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MediaTrack
|
||||||
|
{
|
||||||
|
public string Text { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int ID { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CommandItem : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
|
public string Path { get; set; } = "";
|
||||||
|
public string Command { get; set; } = "";
|
||||||
|
public string Display { get { return string.IsNullOrEmpty(Path) ? Command : Path; } }
|
||||||
|
|
||||||
|
public CommandItem() { }
|
||||||
|
|
||||||
|
public CommandItem(SerializationInfo info, StreamingContext context) { }
|
||||||
|
|
||||||
|
void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
|
||||||
|
{
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
string _Input = "";
|
||||||
|
|
||||||
|
public string Input {
|
||||||
|
get => _Input;
|
||||||
|
set {
|
||||||
|
_Input = value;
|
||||||
|
NotifyPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableCollection<CommandItem> GetItems(string content)
|
||||||
|
{
|
||||||
|
var items = new ObservableCollection<CommandItem>();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(content))
|
||||||
|
{
|
||||||
|
foreach (string line in content.Split('\r', '\n'))
|
||||||
|
{
|
||||||
|
string val = line.Trim();
|
||||||
|
|
||||||
|
if (val.StartsWith("#"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!val.Contains(" "))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
CommandItem item = new CommandItem();
|
||||||
|
item.Input = val.Substring(0, val.IndexOf(" "));
|
||||||
|
|
||||||
|
if (item.Input == "_")
|
||||||
|
item.Input = "";
|
||||||
|
|
||||||
|
val = val.Substring(val.IndexOf(" ") + 1);
|
||||||
|
|
||||||
|
if (val.Contains("#menu:"))
|
||||||
|
{
|
||||||
|
item.Path = val.Substring(val.IndexOf("#menu:") + 6).Trim();
|
||||||
|
val = val.Substring(0, val.IndexOf("#menu:"));
|
||||||
|
|
||||||
|
if (item.Path.Contains(";"))
|
||||||
|
item.Path = item.Path.Substring(item.Path.IndexOf(";") + 1).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Command = val.Trim();
|
||||||
|
|
||||||
|
if (item.Command == "")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (item.Command.ToLower() == "ignore")
|
||||||
|
item.Command = "";
|
||||||
|
|
||||||
|
items.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ObservableCollection<CommandItem> _Items;
|
||||||
|
|
||||||
|
public static ObservableCollection<CommandItem> Items {
|
||||||
|
get {
|
||||||
|
if (_Items is null)
|
||||||
|
_Items = GetItems(File.ReadAllText(Core.InputConfPath));
|
||||||
|
|
||||||
|
return _Items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Folder
|
||||||
|
{
|
||||||
|
public static string Startup { get; } = Application.StartupPath.AddSep();
|
||||||
|
public static string AppData { get; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData).AddSep();
|
||||||
|
|
||||||
|
public static string CustomSettings {
|
||||||
|
get {
|
||||||
|
string linkFile = Startup + "settings-directory.txt";
|
||||||
|
|
||||||
|
if (File.Exists(linkFile))
|
||||||
|
{
|
||||||
|
string linkTarget = File.ReadAllText(linkFile).Trim();
|
||||||
|
|
||||||
|
if (linkTarget.StartsWithEx("."))
|
||||||
|
linkTarget = Startup + linkTarget;
|
||||||
|
|
||||||
|
if (Directory.Exists(linkTarget))
|
||||||
|
return linkTarget.AddSep();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/Misc/Msg.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
public class Msg
|
||||||
|
{
|
||||||
|
public static void ShowInfo(object title, object content = null)
|
||||||
|
{
|
||||||
|
Show(title, content, MessageBoxIcon.Information);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowError(object title, object content = null)
|
||||||
|
{
|
||||||
|
Show(title, content, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowWarning(object title, object content = null)
|
||||||
|
{
|
||||||
|
Show(title, content, MessageBoxIcon.Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DialogResult ShowQuestion(object title, object content = null,
|
||||||
|
MessageBoxButtons buttons = MessageBoxButtons.OKCancel)
|
||||||
|
{
|
||||||
|
return Show(title, content, MessageBoxIcon.Question, buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ShowException(Exception exception)
|
||||||
|
{
|
||||||
|
Show(exception, null, MessageBoxIcon.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DialogResult Show(object title, object content, MessageBoxIcon icon,
|
||||||
|
MessageBoxButtons buttons = MessageBoxButtons.OK)
|
||||||
|
{
|
||||||
|
string msg = (title?.ToString().TrimEx() + BR2 + content?.ToString().TrimEx()).Trim();
|
||||||
|
return MessageBox.Show(msg, Application.ProductName, buttons, icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
242
src/Misc/PowerShell.cs
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Management.Automation;
|
||||||
|
using System.Management.Automation.Runspaces;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
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[] Arguments { 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> References { 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 (Arguments != null)
|
||||||
|
foreach (string param in Arguments)
|
||||||
|
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)
|
||||||
|
Terminal.Write(output.Read(), Module);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Error_DataReady(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var output = sender as PipelineReader<Object>;
|
||||||
|
|
||||||
|
while (output.Count > 0)
|
||||||
|
Terminal.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 => App.RunTask(() => PropertyChanged.Invoke(name, value)));
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
Core.observe_property_string(name, value => App.RunTask(() => PropertyChanged.Invoke(name, value)));
|
||||||
|
break;
|
||||||
|
case "int": case "integer":
|
||||||
|
Core.observe_property_int(name, value => App.RunTask(() => PropertyChanged.Invoke(name, value)));
|
||||||
|
break;
|
||||||
|
case "float": case "double":
|
||||||
|
Core.observe_property_double(name, value => App.RunTask(() => PropertyChanged.Invoke(name, value)));
|
||||||
|
break;
|
||||||
|
case "nil": case "none": case "native":
|
||||||
|
Core.observe_property(name, () => App.RunTask(() => 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>;
|
||||||
|
Terminal.Write(output[e.Index], Module);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Error_DataAdded(object sender, DataAddedEventArgs e)
|
||||||
|
{
|
||||||
|
var error = sender as PSDataCollection<ErrorRecord>;
|
||||||
|
Terminal.WriteError(error[e.Index], Module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PowerShellException : Exception
|
||||||
|
{
|
||||||
|
public PowerShellException(string message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -20,17 +21,19 @@ namespace mpvnet
|
|||||||
if (App.IsStartedFromTerminal)
|
if (App.IsStartedFromTerminal)
|
||||||
Native.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
|
Native.AttachConsole(-1 /*ATTACH_PARENT_PROCESS*/);
|
||||||
|
|
||||||
if (mp.ConfigFolder == "")
|
|
||||||
return;
|
|
||||||
|
|
||||||
string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();
|
string[] args = Environment.GetCommandLineArgs().Skip(1).ToArray();
|
||||||
|
|
||||||
if (args.Length == 2 && args[0] == "--reg-file-assoc")
|
if (args.Length >= 2 && args[0] == "--reg-file-assoc")
|
||||||
{
|
{
|
||||||
if (args[1] == "audio") FileAssociation.Register(App.AudioTypes);
|
if (args[1] == "audio")
|
||||||
if (args[1] == "video") FileAssociation.Register(App.VideoTypes);
|
FileAssociation.Register(CorePlayer.AudioTypes);
|
||||||
if (args[1] == "image") FileAssociation.Register(App.ImageTypes);
|
else if (args[1] == "video")
|
||||||
if (args[1] == "unreg") FileAssociation.Unregister();
|
FileAssociation.Register(CorePlayer.VideoTypes);
|
||||||
|
else if (args[1] == "image")
|
||||||
|
FileAssociation.Register(CorePlayer.ImageTypes);
|
||||||
|
else
|
||||||
|
FileAssociation.Register(args.Skip(1).ToArray());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,17 +42,22 @@ namespace mpvnet
|
|||||||
|
|
||||||
if ((App.ProcessInstance == "single" || App.ProcessInstance == "queue") && !isFirst)
|
if ((App.ProcessInstance == "single" || App.ProcessInstance == "queue") && !isFirst)
|
||||||
{
|
{
|
||||||
List<string> files = new List<string>();
|
List<string> args2 = new List<string>();
|
||||||
files.Add(App.ProcessInstance);
|
args2.Add(App.ProcessInstance);
|
||||||
|
|
||||||
foreach (string arg in args)
|
foreach (string arg in args)
|
||||||
{
|
{
|
||||||
if (!arg.StartsWith("--") && (arg == "-" || arg.Contains("://") ||
|
if (!arg.StartsWith("--") && (arg == "-" || arg.Contains("://") ||
|
||||||
arg.Contains(":\\") || arg.StartsWith("\\\\")))
|
arg.Contains(":\\") || arg.StartsWith("\\\\")))
|
||||||
|
|
||||||
files.Add(arg);
|
args2.Add(arg);
|
||||||
else if (arg == "--queue")
|
else if (arg == "--queue")
|
||||||
files[0] = "queue";
|
args2[0] = "queue";
|
||||||
|
else if (arg.StartsWith("--command="))
|
||||||
|
{
|
||||||
|
args2[0] = "command";
|
||||||
|
args2.Add(arg.Substring(10));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Process[] procs = Process.GetProcessesByName("mpvnet");
|
Process[] procs = Process.GetProcessesByName("mpvnet");
|
||||||
@@ -62,7 +70,7 @@ namespace mpvnet
|
|||||||
{
|
{
|
||||||
Native.AllowSetForegroundWindow(proc.Id);
|
Native.AllowSetForegroundWindow(proc.Id);
|
||||||
var data = new Native.COPYDATASTRUCT();
|
var data = new Native.COPYDATASTRUCT();
|
||||||
data.lpData = string.Join("\n", files.ToArray());
|
data.lpData = string.Join("\n", args2.ToArray());
|
||||||
data.cbData = data.lpData.Length * 2 + 1;
|
data.cbData = data.lpData.Length * 2 + 1;
|
||||||
Native.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
|
Native.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
|
||||||
mutex.Dispose();
|
mutex.Dispose();
|
||||||
@@ -94,4 +102,4 @@ namespace mpvnet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
56
src/Misc/Settings.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Xml.Serialization;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
[Serializable()]
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public int LastUpdateCheck;
|
||||||
|
public int Volume = 70;
|
||||||
|
public List<string> RecentFiles = new List<string>();
|
||||||
|
public Point WindowLocation;
|
||||||
|
public Point WindowPosition;
|
||||||
|
public Size WindowSize;
|
||||||
|
public string ConfigEditorSearch = "";
|
||||||
|
public string Mute = "no";
|
||||||
|
public string UpdateCheckVersion = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsManager
|
||||||
|
{
|
||||||
|
public static string SettingsFile {
|
||||||
|
get => Core.ConfigFolder + "settings.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AppSettings Load()
|
||||||
|
{
|
||||||
|
if (!File.Exists(SettingsFile))
|
||||||
|
return new AppSettings();
|
||||||
|
|
||||||
|
XmlSerializer serializer = new XmlSerializer(typeof(AppSettings));
|
||||||
|
|
||||||
|
using (FileStream fs = new FileStream(SettingsFile, FileMode.Open))
|
||||||
|
return (AppSettings)serializer.Deserialize(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Save(object obj)
|
||||||
|
{
|
||||||
|
using (XmlTextWriter writer = new XmlTextWriter(SettingsFile, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
writer.Formatting = Formatting.Indented;
|
||||||
|
writer.Indentation = 4;
|
||||||
|
XmlSerializer serializer = new XmlSerializer(obj.GetType());
|
||||||
|
serializer.Serialize(writer, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/Misc/Terminal.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public static class Terminal
|
||||||
|
{
|
||||||
|
static int Padding { get; } = 60;
|
||||||
|
|
||||||
|
public static void WriteError(object obj, string module = "mpv.net")
|
||||||
|
{
|
||||||
|
Write(obj, module, ConsoleColor.DarkRed, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(object obj, string module = "mpv.net")
|
||||||
|
{
|
||||||
|
Write(obj, module, ConsoleColor.Black, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(object obj, string module, ConsoleColor color, bool useDefaultColor)
|
||||||
|
{
|
||||||
|
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 (value.Length < Padding)
|
||||||
|
value = value.PadRight(Padding);
|
||||||
|
|
||||||
|
if (color == ConsoleColor.Red || color == ConsoleColor.DarkRed)
|
||||||
|
Console.Error.WriteLine(value);
|
||||||
|
else
|
||||||
|
Console.WriteLine(value);
|
||||||
|
|
||||||
|
Console.ResetColor();
|
||||||
|
Trace.WriteLine(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace UI
|
namespace mpvnet
|
||||||
{
|
{
|
||||||
public class Theme
|
public class Theme
|
||||||
{
|
{
|
||||||
@@ -14,10 +14,10 @@ namespace UI
|
|||||||
|
|
||||||
public static Theme Current { get; set; }
|
public static Theme Current { get; set; }
|
||||||
|
|
||||||
public static Brush Foreground { get; set; }
|
public Brush Foreground { get; set; }
|
||||||
public static Brush Foreground2 { get; set; }
|
public Brush Foreground2 { get; set; }
|
||||||
public static Brush Background { get; set; }
|
public Brush Background { get; set; }
|
||||||
public static Brush Heading { get; set; }
|
public Brush Heading { get; set; }
|
||||||
|
|
||||||
public System.Drawing.Color GetWinFormsColor(string key)
|
public System.Drawing.Color GetWinFormsColor(string key)
|
||||||
{
|
{
|
||||||
@@ -31,6 +31,8 @@ namespace UI
|
|||||||
|
|
||||||
public static void Init(string customContent, string defaultContent, string activeTheme)
|
public static void Init(string customContent, string defaultContent, string activeTheme)
|
||||||
{
|
{
|
||||||
|
Current = null;
|
||||||
|
|
||||||
DefaultThemes = Load(defaultContent);
|
DefaultThemes = Load(defaultContent);
|
||||||
CustomThemes = Load(customContent);
|
CustomThemes = Load(customContent);
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ namespace UI
|
|||||||
if (!theme.Dictionary.ContainsKey(key))
|
if (!theme.Dictionary.ContainsKey(key))
|
||||||
{
|
{
|
||||||
isKeyMissing = true;
|
isKeyMissing = true;
|
||||||
ConsoleHelp.WriteError($"Theme '{activeTheme}' misses '{key}'");
|
Terminal.WriteError($"Theme '{activeTheme}' misses '{key}'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,10 +67,10 @@ namespace UI
|
|||||||
if (Current == null)
|
if (Current == null)
|
||||||
Current = DefaultThemes[0];
|
Current = DefaultThemes[0];
|
||||||
|
|
||||||
Foreground = Current.GetBrush("foreground");
|
Current.Foreground = Current.GetBrush("foreground");
|
||||||
Foreground2 = Current.GetBrush("foreground2");
|
Current.Foreground2 = Current.GetBrush("foreground2");
|
||||||
Background = Current.GetBrush("background");
|
Current.Background = Current.GetBrush("background");
|
||||||
Heading = Current.GetBrush("heading");
|
Current.Heading = Current.GetBrush("heading");
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Theme> Load(string content)
|
static List<Theme> Load(string content)
|
||||||
@@ -93,4 +95,4 @@ namespace UI
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
84
src/Misc/UpdateCheck.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
using static mpvnet.Global;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
class UpdateCheck
|
||||||
|
{
|
||||||
|
public static void DailyCheck()
|
||||||
|
{
|
||||||
|
if (App.UpdateCheck && App.Settings.LastUpdateCheck != DateTime.Now.DayOfYear)
|
||||||
|
CheckOnline();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async void CheckOnline(bool showUpToDateMessage = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (HttpClient client = new HttpClient())
|
||||||
|
{
|
||||||
|
App.Settings.LastUpdateCheck = DateTime.Now.DayOfYear;
|
||||||
|
client.DefaultRequestHeaders.Add("User-Agent", "mpv.net");
|
||||||
|
var response = await client.GetAsync("https://api.github.com/repos/stax76/mpv.net/releases/latest");
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
string content = await response.Content.ReadAsStringAsync();
|
||||||
|
Match match = Regex.Match(content, @"""mpv\.net-([\d\.]+)-portable\.zip""");
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
{
|
||||||
|
App.ShowError("Update check is currently not available.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Version onlineVersion = Version.Parse(match.Groups[1].Value);
|
||||||
|
Version currentVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||||
|
|
||||||
|
if (onlineVersion <= currentVersion)
|
||||||
|
{
|
||||||
|
if (showUpToDateMessage)
|
||||||
|
Msg.ShowInfo($"{Application.ProductName} is up to date.");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((App.Settings.UpdateCheckVersion != onlineVersion.ToString() ||
|
||||||
|
showUpToDateMessage) && Msg.ShowQuestion(
|
||||||
|
$"New version {onlineVersion} is available, update now?") == DialogResult.OK)
|
||||||
|
{
|
||||||
|
string url = $"https://github.com/stax76/mpv.net/releases/download/{onlineVersion}/mpv.net-{onlineVersion}-portable.zip";
|
||||||
|
|
||||||
|
using (Process proc = new Process())
|
||||||
|
{
|
||||||
|
proc.StartInfo.UseShellExecute = true;
|
||||||
|
proc.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||||
|
proc.StartInfo.FileName = "powershell.exe";
|
||||||
|
proc.StartInfo.Arguments = $"-NoExit -ExecutionPolicy Bypass -File \"{Folder.Startup + "Setup\\update.ps1"}\" \"{url}\" \"{Folder.Startup.TrimEnd(Path.DirectorySeparatorChar)}\"";
|
||||||
|
|
||||||
|
if (Folder.Startup.Contains("Program Files"))
|
||||||
|
proc.StartInfo.Verb = "runas";
|
||||||
|
|
||||||
|
proc.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core.command("quit");
|
||||||
|
}
|
||||||
|
|
||||||
|
App.Settings.UpdateCheckVersion = onlineVersion.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (showUpToDateMessage)
|
||||||
|
App.ShowException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
113
src/Native/MediaInfo.cs
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
public class MediaInfo : IDisposable
|
||||||
|
{
|
||||||
|
IntPtr Handle;
|
||||||
|
|
||||||
|
public MediaInfo(string file)
|
||||||
|
{
|
||||||
|
if ((Handle = MediaInfo_New()) == IntPtr.Zero)
|
||||||
|
throw new Exception("Failed to call MediaInfo_New");
|
||||||
|
|
||||||
|
if (MediaInfo_Open(Handle, file) == 0)
|
||||||
|
throw new Exception("Error MediaInfo_Open");
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetInfo(MediaInfoStreamKind kind, string parameter)
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, kind, 0,
|
||||||
|
parameter, MediaInfoKind.Text, MediaInfoKind.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetCount(MediaInfoStreamKind kind) => MediaInfo_Count_Get(Handle, kind, -1);
|
||||||
|
|
||||||
|
public string GetVideo(int stream, string parameter)
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video,
|
||||||
|
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetAudio(int stream, string parameter)
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Audio,
|
||||||
|
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetText(int stream, string parameter)
|
||||||
|
{
|
||||||
|
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Text,
|
||||||
|
stream, parameter, MediaInfoKind.Text, MediaInfoKind.Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Disposed;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!Disposed)
|
||||||
|
{
|
||||||
|
if (Handle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
MediaInfo_Close(Handle);
|
||||||
|
MediaInfo_Delete(Handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
Disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~MediaInfo()
|
||||||
|
{
|
||||||
|
Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll")]
|
||||||
|
static extern IntPtr MediaInfo_New();
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
||||||
|
static extern int MediaInfo_Open(IntPtr handle, string path);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
||||||
|
static extern IntPtr MediaInfo_Option(IntPtr handle, string option, string value);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll")]
|
||||||
|
static extern IntPtr MediaInfo_Inform(IntPtr handle, int reserved);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll")]
|
||||||
|
static extern int MediaInfo_Close(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll")]
|
||||||
|
static extern void MediaInfo_Delete(IntPtr handle);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
||||||
|
static extern IntPtr MediaInfo_Get(IntPtr handle, MediaInfoStreamKind kind,
|
||||||
|
int stream, string parameter, MediaInfoKind infoKind, MediaInfoKind searchKind);
|
||||||
|
|
||||||
|
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
|
||||||
|
static extern int MediaInfo_Count_Get(IntPtr handle, MediaInfoStreamKind streamKind, int stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MediaInfoStreamKind
|
||||||
|
{
|
||||||
|
General,
|
||||||
|
Video,
|
||||||
|
Audio,
|
||||||
|
Text,
|
||||||
|
Other,
|
||||||
|
Image,
|
||||||
|
Menu,
|
||||||
|
Max,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MediaInfoKind
|
||||||
|
{
|
||||||
|
Name,
|
||||||
|
Text,
|
||||||
|
Measure,
|
||||||
|
Options,
|
||||||
|
NameText,
|
||||||
|
MeasureText,
|
||||||
|
Info,
|
||||||
|
HowTo
|
||||||
|
}
|
||||||
159
src/Native/Native.cs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace mpvnet
|
||||||
|
{
|
||||||
|
public class Native
|
||||||
|
{
|
||||||
|
static Version Windows_10_1607 = new Version(10, 0, 14393); // Windows 10 1607
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern bool AttachConsole(int dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern bool FreeConsole();
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern IntPtr LoadLibrary(string path);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern IntPtr FindWindowEx(
|
||||||
|
IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
||||||
|
public static extern int RegisterWindowMessage(string id);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool AllowSetForegroundWindow(int dwProcessId);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern void ReleaseCapture();
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern int GetDpiForWindow(IntPtr hwnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool AdjustWindowRect(ref RECT lpRect, uint dwStyle, bool bMenu);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool AdjustWindowRectExForDpi(
|
||||||
|
ref RECT lpRect, uint dwStyle, bool bMenu, uint dwExStyle, uint dpi);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool SetWindowPos(
|
||||||
|
IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", EntryPoint = "GetWindowLong")]
|
||||||
|
static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex);
|
||||||
|
|
||||||
|
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
|
||||||
|
static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);
|
||||||
|
|
||||||
|
public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex)
|
||||||
|
{
|
||||||
|
if (IntPtr.Size == 8)
|
||||||
|
return GetWindowLong64(hWnd, nIndex);
|
||||||
|
else
|
||||||
|
return GetWindowLong32(hWnd, nIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("gdi32.dll")]
|
||||||
|
public static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct RECT
|
||||||
|
{
|
||||||
|
public int Left;
|
||||||
|
public int Top;
|
||||||
|
public int Right;
|
||||||
|
public int Bottom;
|
||||||
|
|
||||||
|
public RECT(Rectangle r)
|
||||||
|
{
|
||||||
|
Left = r.Left;
|
||||||
|
Top = r.Top;
|
||||||
|
Right = r.Right;
|
||||||
|
Bottom = r.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RECT(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
Left = left;
|
||||||
|
Top = top;
|
||||||
|
Right = right;
|
||||||
|
Bottom = bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
|
||||||
|
public Size Size => new Size(Right - Left, Bottom - Top);
|
||||||
|
public int Width => Right - Left;
|
||||||
|
public int Height => Bottom - Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct COPYDATASTRUCT
|
||||||
|
{
|
||||||
|
public IntPtr dwData;
|
||||||
|
public int cbData;
|
||||||
|
[MarshalAs(UnmanagedType.LPTStr)]
|
||||||
|
public string lpData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetResizeBorder(int v)
|
||||||
|
{
|
||||||
|
switch (v)
|
||||||
|
{
|
||||||
|
case 1 /* WMSZ_LEFT */ : return 3;
|
||||||
|
case 3 /* WMSZ_TOP */ : return 2;
|
||||||
|
case 2 /* WMSZ_RIGHT */ : return 3;
|
||||||
|
case 6 /* WMSZ_BOTTOM */ : return 2;
|
||||||
|
case 4 /* WMSZ_TOPLEFT */ : return 1;
|
||||||
|
case 5 /* WMSZ_TOPRIGHT */ : return 1;
|
||||||
|
case 7 /* WMSZ_BOTTOMLEFT */ : return 3;
|
||||||
|
case 8 /* WMSZ_BOTTOMRIGHT */ : return 3;
|
||||||
|
default: return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SubtractWindowBorders(IntPtr hwnd, ref RECT rc, int dpi)
|
||||||
|
{
|
||||||
|
RECT r = new RECT(0, 0, 0, 0);
|
||||||
|
AddWindowBorders(hwnd, ref r, dpi);
|
||||||
|
rc.Left -= r.Left;
|
||||||
|
rc.Top -= r.Top;
|
||||||
|
rc.Right -= r.Right;
|
||||||
|
rc.Bottom -= r.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddWindowBorders(IntPtr hwnd, ref RECT rc, int dpi)
|
||||||
|
{
|
||||||
|
uint windowStyle = (uint)GetWindowLong(hwnd, -16); // GWL_STYLE
|
||||||
|
uint windowStyleEx = (uint)GetWindowLong(hwnd, -20); // GWL_EXSTYLE
|
||||||
|
|
||||||
|
if (Environment.OSVersion.Version >= Windows_10_1607)
|
||||||
|
AdjustWindowRectExForDpi(ref rc, windowStyle, false, windowStyleEx, (uint)dpi);
|
||||||
|
else
|
||||||
|
AdjustWindowRect(ref rc, windowStyle, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetDPI(IntPtr hwnd)
|
||||||
|
{
|
||||||
|
if (Environment.OSVersion.Version >= Windows_10_1607 && hwnd != IntPtr.Zero)
|
||||||
|
return GetDpiForWindow(hwnd);
|
||||||
|
else
|
||||||
|
using (Graphics gx = Graphics.FromHwnd(hwnd))
|
||||||
|
return GetDeviceCaps(gx.GetHdc(), 88 /*LOGPIXELSX*/);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
src/Native/StockIcon.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
public class StockIcon
|
||||||
|
{
|
||||||
|
[DllImport("shell32.dll")]
|
||||||
|
public static extern int SHGetStockIconInfo(SHSTOCKICONID siid, SHSTOCKICONFLAGS uFlags, ref SHSTOCKICONINFO info);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
public static extern bool DestroyIcon(IntPtr handle);
|
||||||
|
|
||||||
|
public static IntPtr GetIcon(SHSTOCKICONID identifier, SHSTOCKICONFLAGS flags)
|
||||||
|
{
|
||||||
|
SHSTOCKICONINFO info = new SHSTOCKICONINFO();
|
||||||
|
info.cbSize = Convert.ToUInt32(Marshal.SizeOf(typeof(SHSTOCKICONINFO)));
|
||||||
|
Marshal.ThrowExceptionForHR(SHGetStockIconInfo(identifier, flags, ref info));
|
||||||
|
return info.hIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
||||||
|
public struct SHSTOCKICONINFO
|
||||||
|
{
|
||||||
|
public uint cbSize;
|
||||||
|
public IntPtr hIcon;
|
||||||
|
int iSysImageIndex;
|
||||||
|
int iIcon;
|
||||||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
|
||||||
|
string szPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SHSTOCKICONFLAGS : uint
|
||||||
|
{
|
||||||
|
SHGSI_ICONLOCATION = 0,
|
||||||
|
SHGSI_ICON = 0x000000100,
|
||||||
|
SHGSI_SYSICONINDEX = 0x000004000,
|
||||||
|
SHGSI_LINKOVERLAY = 0x000008000,
|
||||||
|
SHGSI_SELECTED = 0x000010000,
|
||||||
|
SHGSI_LARGEICON = 0x000000000,
|
||||||
|
SHGSI_SMALLICON = 0x000000001,
|
||||||
|
SHGSI_SHELLICONSIZE = 0x000000004
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SHSTOCKICONID : uint
|
||||||
|
{
|
||||||
|
DocumentNotAssociated = 0,
|
||||||
|
DocumentAssociated = 1,
|
||||||
|
Application = 2,
|
||||||
|
Folder = 3,
|
||||||
|
FolderOpen = 4,
|
||||||
|
Drive525 = 5,
|
||||||
|
Drive35 = 6,
|
||||||
|
DriveRemove = 7,
|
||||||
|
DriveFixed = 8,
|
||||||
|
DriveNetwork = 9,
|
||||||
|
DriveNetworkDisabled = 10,
|
||||||
|
DriveCD = 11,
|
||||||
|
DriveRAM = 12,
|
||||||
|
World = 13,
|
||||||
|
Server = 15,
|
||||||
|
Printer = 16,
|
||||||
|
MyNetwork = 17,
|
||||||
|
Find = 22,
|
||||||
|
Help = 23,
|
||||||
|
Share = 28,
|
||||||
|
Link = 29,
|
||||||
|
SlowFile = 30,
|
||||||
|
Recycler = 31,
|
||||||
|
RecyclerFull = 32,
|
||||||
|
MediaCDAudio = 40,
|
||||||
|
Lock = 47,
|
||||||
|
AutoList = 49,
|
||||||
|
PrinterNet = 50,
|
||||||
|
ServerShare = 51,
|
||||||
|
PrinterFax = 52,
|
||||||
|
PrinterFaxNet = 53,
|
||||||
|
PrinterFile = 54,
|
||||||
|
Stack = 55,
|
||||||
|
MediaSVCD = 56,
|
||||||
|
StuffedFolder = 57,
|
||||||
|
DriveUnknown = 58,
|
||||||
|
DriveDVD = 59,
|
||||||
|
MediaDVD = 60,
|
||||||
|
MediaDVDRAM = 61,
|
||||||
|
MediaDVDRW = 62,
|
||||||
|
MediaDVDR = 63,
|
||||||
|
MediaDVDROM = 64,
|
||||||
|
MediaCDAudioPlus = 65,
|
||||||
|
MediaCDRW = 66,
|
||||||
|
MediaCDR = 67,
|
||||||
|
MediaCDBurn = 68,
|
||||||
|
MediaBlankCD = 69,
|
||||||
|
MediaCDROM = 70,
|
||||||
|
AudioFiles = 71,
|
||||||
|
ImageFiles = 72,
|
||||||
|
VideoFiles = 73,
|
||||||
|
MixedFiles = 74,
|
||||||
|
FolderBack = 75,
|
||||||
|
FolderFront = 76,
|
||||||
|
Shield = 77,
|
||||||
|
Warning = 78,
|
||||||
|
Info = 79,
|
||||||
|
Error = 80,
|
||||||
|
Key = 81,
|
||||||
|
Software = 82,
|
||||||
|
Rename = 83,
|
||||||
|
Delete = 84,
|
||||||
|
MediaAudioDVD = 85,
|
||||||
|
MediaMovieDVD = 86,
|
||||||
|
MediaEnhancedCD = 87,
|
||||||
|
MediaEnhancedDVD = 88,
|
||||||
|
MediaHDDVD = 89,
|
||||||
|
MediaBluRay = 90,
|
||||||
|
MediaVCD = 91,
|
||||||
|
MediaDVDPlusR = 92,
|
||||||
|
MediaDVDPlusRW = 93,
|
||||||
|
DesktopPC = 94,
|
||||||
|
MobilePC = 95,
|
||||||
|
Users = 96,
|
||||||
|
MediaSmartMedia = 97,
|
||||||
|
MediaCompactFlash = 98,
|
||||||
|
DeviceCellPhone = 99,
|
||||||
|
DeviceCamera = 100,
|
||||||
|
DeviceVideoCamera = 101,
|
||||||
|
DeviceAudioPlayer = 102,
|
||||||
|
NetworkConnect = 103,
|
||||||
|
Internet = 104,
|
||||||
|
ZipFile = 105,
|
||||||
|
Settings = 106
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
using System;
|
|
||||||
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
public class Taskbar
|
public class Taskbar
|
||||||
{
|
{
|
||||||
private ITaskbarList3 Instance = (ITaskbarList3)new TaskBarCommunication();
|
|
||||||
|
|
||||||
public IntPtr Handle { get; set; }
|
public IntPtr Handle { get; set; }
|
||||||
|
|
||||||
public Taskbar(IntPtr handle) => Handle = handle;
|
public Taskbar(IntPtr handle) => Handle = handle;
|
||||||
|
|
||||||
|
ITaskbarList3 Instance = (ITaskbarList3)new TaskBarCommunication();
|
||||||
|
|
||||||
[ComImportAttribute]
|
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||||
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
[Guid("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF")]
|
||||||
[GuidAttribute("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf")]
|
interface ITaskbarList3
|
||||||
private interface ITaskbarList3
|
|
||||||
{
|
{
|
||||||
// ITaskbarList
|
// ITaskbarList
|
||||||
[PreserveSig] void HrInit();
|
[PreserveSig] void HrInit();
|
||||||
@@ -23,14 +23,16 @@ public class Taskbar
|
|||||||
// ITaskbarList2
|
// ITaskbarList2
|
||||||
[PreserveSig] void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);
|
[PreserveSig] void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen);
|
||||||
// ITaskbarList3
|
// ITaskbarList3
|
||||||
[PreserveSig] void SetProgressValue(IntPtr hwnd, UInt64 ullCompleted, UInt64 ullTotal);
|
[PreserveSig] void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal);
|
||||||
[PreserveSig] void SetProgressState(IntPtr hwnd, TaskbarStates state);
|
[PreserveSig] void SetProgressState(IntPtr hwnd, TaskbarStates state);
|
||||||
}
|
}
|
||||||
|
|
||||||
[ComImportAttribute]
|
[ComImport]
|
||||||
[ClassInterfaceAttribute(ClassInterfaceType.None)]
|
[ClassInterface(ClassInterfaceType.None)]
|
||||||
[GuidAttribute("56FDF344-FD6D-11d0-958A-006097C9A090")]
|
[Guid("56FDF344-FD6D-11d0-958A-006097C9A090")]
|
||||||
private class TaskBarCommunication { }
|
class TaskBarCommunication
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public void SetState(TaskbarStates taskbarState)
|
public void SetState(TaskbarStates taskbarState)
|
||||||
{
|
{
|
||||||
@@ -39,7 +41,7 @@ public class Taskbar
|
|||||||
|
|
||||||
public void SetValue(double progressValue, double progressMax)
|
public void SetValue(double progressValue, double progressMax)
|
||||||
{
|
{
|
||||||
Instance.SetProgressValue(Handle, (UInt64)progressValue, (UInt64)progressMax);
|
Instance.SetProgressValue(Handle, (ulong)progressValue, (ulong)progressMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
237
src/Native/libmpv.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
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 mpv_error mpv_initialize(IntPtr mpvHandle);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern mpv_error mpv_command(IntPtr mpvHandle, IntPtr strings);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
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 mpv_error mpv_command_ret(IntPtr mpvHandle, IntPtr strings, IntPtr node);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void mpv_free_node_contents(IntPtr node);
|
||||||
|
|
||||||
|
[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);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int mpv_set_option_string(IntPtr mpvHandle, byte[] name, byte[] value);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
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 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 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 mpv_error mpv_set_property(IntPtr mpvHandle, byte[] name, mpv_format format, ref long data);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
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, ulong 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, ulong registered_reply_userdata);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern void mpv_free(IntPtr data);
|
||||||
|
|
||||||
|
[DllImport("mpv-1.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern IntPtr mpv_wait_event(IntPtr mpvHandle, double timeout);
|
||||||
|
|
||||||
|
public enum mpv_error
|
||||||
|
{
|
||||||
|
MPV_ERROR_SUCCESS = 0,
|
||||||
|
MPV_ERROR_EVENT_QUEUE_FULL = -1,
|
||||||
|
MPV_ERROR_NOMEM = -2,
|
||||||
|
MPV_ERROR_UNINITIALIZED = -3,
|
||||||
|
MPV_ERROR_INVALID_PARAMETER = -4,
|
||||||
|
MPV_ERROR_OPTION_NOT_FOUND = -5,
|
||||||
|
MPV_ERROR_OPTION_FORMAT = -6,
|
||||||
|
MPV_ERROR_OPTION_ERROR = -7,
|
||||||
|
MPV_ERROR_PROPERTY_NOT_FOUND = -8,
|
||||||
|
MPV_ERROR_PROPERTY_FORMAT = -9,
|
||||||
|
MPV_ERROR_PROPERTY_UNAVAILABLE = -10,
|
||||||
|
MPV_ERROR_PROPERTY_ERROR = -11,
|
||||||
|
MPV_ERROR_COMMAND = -12,
|
||||||
|
MPV_ERROR_LOADING_FAILED = -13,
|
||||||
|
MPV_ERROR_AO_INIT_FAILED = -14,
|
||||||
|
MPV_ERROR_VO_INIT_FAILED = -15,
|
||||||
|
MPV_ERROR_NOTHING_TO_PLAY = -16,
|
||||||
|
MPV_ERROR_UNKNOWN_FORMAT = -17,
|
||||||
|
MPV_ERROR_UNSUPPORTED = -18,
|
||||||
|
MPV_ERROR_NOT_IMPLEMENTED = -19,
|
||||||
|
MPV_ERROR_GENERIC = -20
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum mpv_event_id
|
||||||
|
{
|
||||||
|
MPV_EVENT_NONE = 0,
|
||||||
|
MPV_EVENT_SHUTDOWN = 1,
|
||||||
|
MPV_EVENT_LOG_MESSAGE = 2,
|
||||||
|
MPV_EVENT_GET_PROPERTY_REPLY = 3,
|
||||||
|
MPV_EVENT_SET_PROPERTY_REPLY = 4,
|
||||||
|
MPV_EVENT_COMMAND_REPLY = 5,
|
||||||
|
MPV_EVENT_START_FILE = 6,
|
||||||
|
MPV_EVENT_END_FILE = 7,
|
||||||
|
MPV_EVENT_FILE_LOADED = 8,
|
||||||
|
MPV_EVENT_TRACKS_CHANGED = 9,
|
||||||
|
MPV_EVENT_TRACK_SWITCHED = 10,
|
||||||
|
MPV_EVENT_IDLE = 11,
|
||||||
|
MPV_EVENT_PAUSE = 12,
|
||||||
|
MPV_EVENT_UNPAUSE = 13,
|
||||||
|
MPV_EVENT_TICK = 14,
|
||||||
|
MPV_EVENT_SCRIPT_INPUT_DISPATCH = 15,
|
||||||
|
MPV_EVENT_CLIENT_MESSAGE = 16,
|
||||||
|
MPV_EVENT_VIDEO_RECONFIG = 17,
|
||||||
|
MPV_EVENT_AUDIO_RECONFIG = 18,
|
||||||
|
MPV_EVENT_METADATA_UPDATE = 19,
|
||||||
|
MPV_EVENT_SEEK = 20,
|
||||||
|
MPV_EVENT_PLAYBACK_RESTART = 21,
|
||||||
|
MPV_EVENT_PROPERTY_CHANGE = 22,
|
||||||
|
MPV_EVENT_CHAPTER_CHANGE = 23,
|
||||||
|
MPV_EVENT_QUEUE_OVERFLOW = 24,
|
||||||
|
MPV_EVENT_HOOK = 25
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum mpv_format
|
||||||
|
{
|
||||||
|
MPV_FORMAT_NONE = 0,
|
||||||
|
MPV_FORMAT_STRING = 1,
|
||||||
|
MPV_FORMAT_OSD_STRING = 2,
|
||||||
|
MPV_FORMAT_FLAG = 3,
|
||||||
|
MPV_FORMAT_INT64 = 4,
|
||||||
|
MPV_FORMAT_DOUBLE = 5,
|
||||||
|
MPV_FORMAT_NODE = 6,
|
||||||
|
MPV_FORMAT_NODE_ARRAY = 7,
|
||||||
|
MPV_FORMAT_NODE_MAP = 8,
|
||||||
|
MPV_FORMAT_BYTE_ARRAY = 9
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum mpv_log_level
|
||||||
|
{
|
||||||
|
MPV_LOG_LEVEL_NONE = 0,
|
||||||
|
MPV_LOG_LEVEL_FATAL = 10,
|
||||||
|
MPV_LOG_LEVEL_ERROR = 20,
|
||||||
|
MPV_LOG_LEVEL_WARN = 30,
|
||||||
|
MPV_LOG_LEVEL_INFO = 40,
|
||||||
|
MPV_LOG_LEVEL_V = 50,
|
||||||
|
MPV_LOG_LEVEL_DEBUG = 60,
|
||||||
|
MPV_LOG_LEVEL_TRACE = 70,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum mpv_end_file_reason
|
||||||
|
{
|
||||||
|
MPV_END_FILE_REASON_EOF = 0,
|
||||||
|
MPV_END_FILE_REASON_STOP = 2,
|
||||||
|
MPV_END_FILE_REASON_QUIT = 3,
|
||||||
|
MPV_END_FILE_REASON_ERROR = 4,
|
||||||
|
MPV_END_FILE_REASON_REDIRECT = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct mpv_event_log_message
|
||||||
|
{
|
||||||
|
public IntPtr prefix;
|
||||||
|
public IntPtr level;
|
||||||
|
public IntPtr text;
|
||||||
|
public mpv_log_level log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct mpv_event
|
||||||
|
{
|
||||||
|
public mpv_event_id event_id;
|
||||||
|
public int error;
|
||||||
|
public ulong reply_userdata;
|
||||||
|
public IntPtr data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct mpv_event_client_message
|
||||||
|
{
|
||||||
|
public int num_args;
|
||||||
|
public IntPtr args;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct mpv_event_property
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public mpv_format format;
|
||||||
|
public IntPtr data;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct mpv_event_end_file
|
||||||
|
{
|
||||||
|
public int reason;
|
||||||
|
public int error;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit, Size = 16)]
|
||||||
|
public struct mpv_node
|
||||||
|
{
|
||||||
|
[FieldOffset(0)] public IntPtr str;
|
||||||
|
[FieldOffset(0)] public int flag;
|
||||||
|
[FieldOffset(0)] public long int64;
|
||||||
|
[FieldOffset(0)] public double dbl;
|
||||||
|
[FieldOffset(0)] public IntPtr list;
|
||||||
|
[FieldOffset(0)] public IntPtr ba;
|
||||||
|
[FieldOffset(8)] public mpv_format format;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
@@ -6,11 +6,11 @@ using System.Runtime.InteropServices;
|
|||||||
// set of attributes. Change these attribute values to modify the information
|
// set of attributes. Change these attribute values to modify the information
|
||||||
// associated with an assembly.
|
// associated with an assembly.
|
||||||
[assembly: AssemblyTitle("mpv.net")]
|
[assembly: AssemblyTitle("mpv.net")]
|
||||||
[assembly: AssemblyDescription("media player")]
|
[assembly: AssemblyDescription("mpv based media player")]
|
||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Frank Skare (stax76)")]
|
[assembly: AssemblyCompany("mpv.net/mpv/mplayer")]
|
||||||
[assembly: AssemblyProduct("mpv.net")]
|
[assembly: AssemblyProduct("mpv.net")]
|
||||||
[assembly: AssemblyCopyright("Copyright 2017-2019 Frank Skare (stax76)")]
|
[assembly: AssemblyCopyright("Copyright (C) 2000-2021 mpv.net/mpv/mplayer")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
// by using the '*' as shown below:
|
// by using the '*' as shown below:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("5.4.3.0")]
|
[assembly: AssemblyVersion("5.4.9.0")]
|
||||||
[assembly: AssemblyFileVersion("5.4.3.0")]
|
[assembly: AssemblyFileVersion("5.4.9.0")]
|
||||||
@@ -61,7 +61,8 @@ namespace mpvnet.Properties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to [[settings]]
|
/// Looks up a localized string similar to
|
||||||
|
///[[settings]]
|
||||||
///name = "hwdec"
|
///name = "hwdec"
|
||||||
///file = "mpv"
|
///file = "mpv"
|
||||||
///default = "no"
|
///default = "no"
|
||||||
@@ -69,55 +70,56 @@ namespace mpvnet.Properties {
|
|||||||
///url = "https://mpv.io/manual/master/#options-hwdec"
|
///url = "https://mpv.io/manual/master/#options-hwdec"
|
||||||
///help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:"
|
///help = "Specify the hardware video decoding API that should be used if possible. Whether hardware decoding is actually done depends on the video codec. If hardware decoding is not possible, mpv will fall back on software decoding.\n\nFor more information visit:"
|
||||||
///options = [{ name = "no", help = "always use software decoding" },
|
///options = [{ name = "no", help = "always use software decoding" },
|
||||||
/// { name = "auto", hel [rest of string was truncated]";.
|
/// { name = "auto", h [rest of string was truncated]";.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string confToml {
|
internal static string editor_toml {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("confToml", resourceCulture);
|
return ResourceManager.GetString("editor_toml", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to
|
/// Looks up a localized string similar to
|
||||||
/// # This file defines the key and mouse bindings and the context menu
|
/// # This file defines the key and mouse bindings and the context menu of mpv.net.
|
||||||
/// # 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:
|
/// # A input and config editor can be found in the context menu under 'Settings'.
|
||||||
|
///
|
||||||
|
/// # 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 search easier,
|
||||||
|
/// # if you want to know if 'o' has already a binding you can search for ' o '.
|
||||||
///
|
///
|
||||||
/// # https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt
|
/// # input test mode:
|
||||||
|
/// # mpvnet --input-test
|
||||||
///
|
///
|
||||||
/// # The defaults of mpv can be found at:
|
/// # The [rest of string was truncated]";.
|
||||||
///
|
|
||||||
/// # 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 [rest of string was truncated]";.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string inputConf {
|
internal static string input_conf {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("inputConf", resourceCulture);
|
return ResourceManager.GetString("input_conf", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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-delay = 500
|
||||||
///input-ar-rate = 20
|
///input-ar-rate = 20
|
||||||
///keep-open = yes
|
///keep-open = yes
|
||||||
///keep-open-pause = no
|
///keep-open-pause = no
|
||||||
|
///osd-duration = 2000
|
||||||
///osd-playing-msg = '${filename}'
|
///osd-playing-msg = '${filename}'
|
||||||
///script-opts = osc-scalewindowed=1.5
|
///script-opts = osc-scalewindowed=1.5,osc-hidetimeout=2000,osc-greenandgrumpy=yes,console-scale=1
|
||||||
///screenshot-directory = '~~desktop/'
|
///screenshot-directory = '~~desktop/'
|
||||||
///cscale = spline36
|
|
||||||
///dscale = spline36
|
|
||||||
///scale = spline36
|
|
||||||
///hwdec = yes
|
|
||||||
///
|
///
|
||||||
///[protocol.https]
|
///[protocol.https]
|
||||||
///osd-playing-msg = '${media-title}'.
|
///osd-playing-msg = '${media-title}'
|
||||||
|
///.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string mpvConf {
|
internal static string mpv_conf {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("mpvConf", resourceCulture);
|
return ResourceManager.GetString("mpv_conf", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,27 +133,38 @@ namespace mpvnet.Properties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||||
|
/// </summary>
|
||||||
|
internal static System.Drawing.Bitmap mpvnet_santa {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("mpvnet_santa", resourceCulture);
|
||||||
|
return ((System.Drawing.Bitmap)(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to
|
/// Looks up a localized string similar to
|
||||||
///[dark]
|
///[dark]
|
||||||
///
|
///
|
||||||
///heading = #1E90FF
|
///heading = #3C8CC8
|
||||||
///foreground = #FFFFFF
|
///foreground = #DDDDDD
|
||||||
///foreground2 = #C0C0C0
|
///foreground2 = #AAAAAA
|
||||||
///background = #323232
|
///background = #323232
|
||||||
///
|
///
|
||||||
///menu-foreground = #FFFFFF
|
///menu-foreground = #DDDDDD
|
||||||
///menu-background = #323232
|
///menu-background = #323232
|
||||||
///menu-highlight = #505050
|
///menu-highlight = #505050
|
||||||
///menu-border = #FFFFFF
|
///menu-border = #FFFFFF
|
||||||
///menu-checked = #5A5A5A
|
///menu-checked = #5A5A5A
|
||||||
///
|
///
|
||||||
|
///
|
||||||
///[light]
|
///[light]
|
||||||
///
|
///
|
||||||
///heading = #0068B2
|
///heading = #0068B2
|
||||||
///foreground = #000000
|
///foreground = #000000
|
||||||
///foreground2 = #4C4C4C
|
///foreground2 = #4C4C4C
|
||||||
///background = #F7F7F7
|
///background = #F7F7F7
|
||||||
///
|
///
|
||||||
///menu-foreground = #000000
|
///menu-foreground = #000000
|
||||||
///menu-background = #DFDFDF
|
///menu-background = #DFDFDF
|
||||||
@@ -118,18 +118,21 @@
|
|||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<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">
|
<data name="editor_toml" 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>
|
<value>..\Resources\editor.toml.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="inputConf" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<data name="input_conf" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
<value>..\Resources\inputConf.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
<value>..\Resources\input.conf.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>
|
</data>
|
||||||
<data name="mpvnet" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<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>
|
<value>..\Resources\mpvnet.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="mpvnet_santa" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>..\Resources\mpvnet-santa.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">
|
<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>
|
<value>..\Resources\theme.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||||
</data>
|
</data>
|
||||||