Remove desktop mode.

This commit is contained in:
Ian Walton 2021-04-19 20:07:38 -04:00
parent f0b0c05e0b
commit 929bae24be
25 changed files with 100 additions and 766 deletions

View File

@ -41,9 +41,7 @@ Current Dependencies:
- `pystray` - Provides systray icon. (Optional)
- `tkinter` - Provides GUI for adding servers and viewing logs. (Optional)
- `Jinja2` - Renders HTML for display mirroring. (Optional)
- `pywebview` - Displays HTML for display mirroring or webclient. (Optional)
- `Flask` - Used to serve the webclient in desktop mode. (Optional)
- `Werkzeug` - Used to serve the webclient in desktop mode. (Optional)
- `pywebview` - Displays HTML for display mirroring. (Optional)
- `pypresence` - Used for Discord Rich Presence integration. (Optional)
## Project Overview
@ -78,7 +76,6 @@ Current Dependencies:
- `video_profile.py` - Implements support for shader pack option profiles and related menu items.
- `win_utils.py` - Implements window management workarounds for Windows.
- `display_mirror` - Package that implements the full-screen display mirroring.
- `webclient_view` - Package that implements the webclient UI. (The actual webclient is a [separate repo](https://github.com/iwalton3/jellyfin-web).)
- `integration` - This contains the appstream metadata, icons, and desktop files used in the Flatpak version.
- `default_shader_pack` - This is where the `gen_pkg.sh` script installs the [default-shader-pack](https://github.com/iwalton3/default-shader-pack).

View File

@ -1,11 +1,11 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Jellyfin Desktop"
#define MyAppVersion "1.10.4"
#define MyAppName "Jellyfin MPV Shim"
#define MyAppVersion "2.0.0"
#define MyAppPublisher "Ian Walton"
#define MyAppURL "https://github.com/jellyfin/jellyfin-desktop"
#define MyAppExeName "run-desktop.exe"
#define MyAppURL "https://github.com/jellyfin/jellyfin-mpv-shim"
#define MyAppExeName "run.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
@ -25,7 +25,7 @@ LicenseFile=LICENSE.md
;PrivilegesRequired=lowest
PrivilegesRequiredOverridesAllowed=dialog
OutputDir=dist
OutputBaseFilename=jellyfin-mpv-desktop_version_installer
OutputBaseFilename=jellyfin-mpv-shim_version_installer
SetupIconFile=jellyfin.ico
Compression=lzma
SolidCompression=yes
@ -38,12 +38,11 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "dist\run-desktop\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "dist\run\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autoprograms}\Jellyfin MPV Shim"; Filename: "{app}\{#MyAppExeName}"; Parameters: "--shim"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Run]

View File

@ -1,6 +1,5 @@
include jellyfin_mpv_shim/systray.png
recursive-include jellyfin_mpv_shim/webclient_view/webclient *
recursive-include jellyfin_mpv_shim/integration *
recursive-include jellyfin_mpv_shim/default_shader_pack *
recursive-include jellyfin_mpv_shim/messages *.mo
include jellyfin_mpv_shim/mouse.lua
include jellyfin_mpv_shim/mouse.lua

106
README.md
View File

@ -1,46 +1,36 @@
# Jellyfin Desktop (MPV Shim)
# Jellyfin MPV Shim
[![Current Release](https://img.shields.io/github/release/jellyfin/jellyfin-desktop.svg)](https://github.com/jellyfin/jellyfin-desktop/releases)
[![Current Release](https://img.shields.io/github/release/jellyfin/jellyfin-mpv-shim.svg)](https://github.com/jellyfin/jellyfin-mpv-shim/releases)
[![PyPI](https://img.shields.io/pypi/v/jellyfin-mpv-shim)](https://pypi.org/project/jellyfin-mpv-shim/)
[![Translation Status](https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-desktop/svg-badge.svg)](https://translate.jellyfin.org/projects/jellyfin/jellyfin-desktop/)
[![Build Status](https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/jellyfin.jellyfin-desktop?branchName=master)](https://dev.azure.com/jellyfin-project/jellyfin/_build/latest?definitionId=22&branchName=master)
[![Translation Status](https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-mpv-shim/svg-badge.svg)](https://translate.jellyfin.org/projects/jellyfin/jellyfin-mpv-shim/)
[![Build Status](https://dev.azure.com/jellyfin-project/jellyfin/_apis/build/status/jellyfin.jellyfin-mpv-shim?branchName=master)](https://dev.azure.com/jellyfin-project/jellyfin/_build/latest?definitionId=22&branchName=master)
[![Code Stype](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
Jellyfin Desktop is a fully featured, cross-platform desktop and cast client for Jellyfin.
Jellyfin MPV Shim is a cross-platform cast client for Jellyfin.
It has support for all your advanced media files without transcoding, as well as tons of
features which set it apart from other multimedia clients:
- Direct play most media using MPV.
- Watch videos with friends using SyncPlay.
- Offers both a desktop mode and a shim mode which runs in the background.
- Offers a shim mode which runs in the background.
- The Jellyfin mobile apps can fully control the client.
- [Reconfigure subtitles](https://github.com/jellyfin/jellyfin-desktop#menu) for an entire season at once.
- Supports all of the [MPV keyboard shortcuts](https://github.com/jellyfin/jellyfin-desktop#keyboard-shortcuts).
- Enhance your video with [Shader Packs](https://github.com/jellyfin/jellyfin-desktop#menu) and [SVP Integration](https://github.com/jellyfin/jellyfin-desktop#svp-integration).
- [Reconfigure subtitles](https://github.com/jellyfin/jellyfin-mpv-shim#menu) for an entire season at once.
- Supports all of the [MPV keyboard shortcuts](https://github.com/jellyfin/jellyfin-mpv-shim#keyboard-shortcuts).
- Enhance your video with [Shader Packs](https://github.com/jellyfin/jellyfin-mpv-shim#menu) and [SVP Integration](https://github.com/jellyfin/jellyfin-mpv-shim#svp-integration).
- Optionally share your media activity with friends using Discord Rich Presence.
- Most features, as well as MPV itself, [can be extensively configured](https://github.com/jellyfin/jellyfin-desktop#configuration).
- You can configure the player to use an [external MPV player](https://github.com/jellyfin/jellyfin-desktop#external-mpv) of your choice.
- Enable a chromecast-like experience with [Display Mirroring](https://github.com/jellyfin/jellyfin-desktop#display-mirroring).
- You can [trigger commands to run](https://github.com/jellyfin/jellyfin-desktop#shell-command-triggers) when certain events happen.
- Most features, as well as MPV itself, [can be extensively configured](https://github.com/jellyfin/jellyfin-mpv-shim#configuration).
- You can configure the player to use an [external MPV player](https://github.com/jellyfin/jellyfin-mpv-shim#external-mpv) of your choice.
- Enable a chromecast-like experience with [Display Mirroring](https://github.com/jellyfin/jellyfin-mpv-shim#display-mirroring).
- You can [trigger commands to run](https://github.com/jellyfin/jellyfin-mpv-shim#shell-command-triggers) when certain events happen.
To learn more, keep reading. This README explains everything, including [configuration](https://github.com/jellyfin/jellyfin-desktop#configuration), [tips & tricks](https://github.com/jellyfin/jellyfin-desktop#tips-and-tricks), and [development information](https://github.com/jellyfin/jellyfin-desktop#development).
To learn more, keep reading. This README explains everything, including [configuration](https://github.com/jellyfin/jellyfin-mpv-shim#configuration), [tips & tricks](https://github.com/jellyfin/jellyfin-mpv-shim#tips-and-tricks), and [development information](https://github.com/jellyfin/jellyfin-mpv-shim#development).
## Getting Started
If you are on Windows, simply [download the binary](https://github.com/jellyfin/jellyfin-desktop/releases).
If you are using Linux, you can [install via flathub](https://flathub.org/apps/details/com.github.iwalton3.jellyfin-mpv-shim) or [install via pip](https://github.com/jellyfin/jellyfin-desktop/blob/master/README.md#linux-installation). If you are on macOS, see the [macOS Installation](https://github.com/jellyfin/jellyfin-desktop/blob/master/README.md#osx-installation)
If you are on Windows, simply [download the binary](https://github.com/jellyfin/jellyfin-mpv-shim/releases).
If you are using Linux, you can [install via flathub](https://flathub.org/apps/details/com.github.iwalton3.jellyfin-mpv-shim) or [install via pip](https://github.com/jellyfin/jellyfin-mpv-shim/blob/master/README.md#linux-installation). If you are on macOS, see the [macOS Installation](https://github.com/jellyfin/jellyfin-mpv-shim/blob/master/README.md#osx-installation)
section below.
### Desktop Client
Launch the client. You should see the Jellyfin web app. Log in to your server and use it as normal.
All videos will load in MPV just like MPV Shim.
Please note: The desktop client for Windows contains significantly more files than MPV Shim, so it
is distributed as an installer. It will work without admin rights.
### MPV Shim
To use the client, simply launch it and log into your Jellyfin server. Youll need to enter the
URL to your server, for example `http://server_ip:8096` or `https://secure_domain`. Make sure to
include the subdirectory and port number if applicable. You can then cast your media
@ -64,13 +54,12 @@ bare IP addresses and not specifying the port by default. If you want to connect
Please also note that the on-screen controller for MPV (if available) cannot change the
audio and subtitle track configurations for transcoded media. It also cannot load external
subtitles. You must either [use the menu](https://github.com/jellyfin/jellyfin-desktop#menu) or the application you casted from.
subtitles. You must either [use the menu](https://github.com/jellyfin/jellyfin-mpv-shim#menu) or the application you casted from.
Please note the following issues with controlling SyncPlay:
- The desktop client can join SyncPlay groups from the web app's menu, but not create them.
- If you attempt to join a SyncPlay group when casting to MPV Shim, it will play the media but it will not activate SyncPlay.
- You can, however, proceed to activate SyncPlay [using the menu within MPV](https://github.com/jellyfin/jellyfin-desktop#menu).
- If you would like to create a group or join a group for currently playing media, [use menu within MPV](https://github.com/jellyfin/jellyfin-desktop#menu).
- You can, however, proceed to activate SyncPlay [using the menu within MPV](https://github.com/jellyfin/jellyfin-mpv-shim#menu).
- If you would like to create a group or join a group for currently playing media, [use menu within MPV](https://github.com/jellyfin/jellyfin-mpv-shim#menu).
- SyncPlay as of 10.7.0 is new and kind of fragile. You may need to rejoin or even restart the client. Please report any issues you find.
Music playback sort-of works, but repeat, shuffle, and gapless playback have not been implemented and
@ -336,22 +325,6 @@ need to.
- This could break if you use revert-seek markers or scripts that use it.
- `sync_osd_message` - Write syncplay status messages to OSD. Default: `true`
### Desktop Mode Settings
These settings pertain to the "Desktop" (embedded webview) mode only.
- `enable_desktop` - Use the desktop client. Default: `false`
- You can also use it by running the `jellyfin-mpv-desktop`.
- If you are using the Windows build, you must download the desktop version.
- `desktop_fullscreen` - Run the desktop client in fullscreen. Default: `false`
- `desktop_keep_loc` - Remember the position of the desktop client. Default: `false`
- This has been infamous for causing the window to be positioned off-screen.
- If you enable this and that happens, you can delete `layout.json` to fix it.
- `desktop_keep_size` - Remember the position of the desktop client. Default: `true`
- Set the window size, but not the position. Hopefully won't cause the client to open off-screen.
- `desktop_scale` - Allows changing the scale factor for the desktop app. Default: `1.0`
- This can be useful if you are on a platform that doesn't support HiDPI very well.
### Debugging
These settings assist with debugging. You will often be asked to configure them when reporting an issue.
@ -414,7 +387,7 @@ Determine which screen you would like MPV to show up on.
- If you are on Windows, right click the desktop and select "Display Settings". Take the monitor number and subtract one.
- If you are on Linux, run `xrandr`. The screen number is the number you want. If there is only one proceed to **Option 2**.
Add the following to your `mpv.conf` in the [config directory](https://github.com/jellyfin/jellyfin-desktop#mpv-configuration), replacing `0` with the number from the previous step:
Add the following to your `mpv.conf` in the [config directory](https://github.com/jellyfin/jellyfin-mpv-shim#mpv-configuration), replacing `0` with the number from the previous step:
```
fs=yes
fs-screen=0
@ -442,7 +415,7 @@ LVDS-0 connected 1600x900+1920+180 (normal left inverted right x axis y axis) 30
1600x900 59.98*+
```
If you want MPV to open on VGA-0 for instance, add the following to your `mpv.conf` in the [config directory](https://github.com/jellyfin/jellyfin-desktop#mpv-configuration):
If you want MPV to open on VGA-0 for instance, add the following to your `mpv.conf` in the [config directory](https://github.com/jellyfin/jellyfin-mpv-shim#mpv-configuration):
```
fs=yes
geometry=1920x1080+0+0
@ -539,8 +512,7 @@ The project is dependent on `python-mpv`, `python-mpv-jsonipc`, `pydantic`, and
using Windows and would like mpv to be maximize properly, `pywin32` is also needed. The GUI
component uses `pystray` and `tkinter`, but there is a fallback cli mode. The mirroring dependencies
are `Jinja2` and `pywebview`, along with platform-specific dependencies. (See the installation and building
guides for details on platform-specific dependencies for display mirroring.) The desktop client depends on
`pywebview`, `Flask`, and `Werkzeug`.
guides for details on platform-specific dependencies for display mirroring.)
This project is based Plex MPV Shim, which is based on https://github.com/wnielson/omplex, which
is available under the terms of the MIT License. The project was ported to python3, modified to
@ -560,14 +532,14 @@ If you are on Windows there are additional dependencies. Please see the Windows
1. Install the dependencies: `sudo pip3 install --upgrade python-mpv jellyfin-apiclient-python pystray Jinja2 pywebview python-mpv-jsonipc Flask Werkzeug pypresence pydantic`.
- If you run `./gen_pkg.sh --install`, it will also fetch these for you.
2. Clone this repository: `git clone https://github.com/jellyfin/jellyfin-desktop`
2. Clone this repository: `git clone https://github.com/jellyfin/jellyfin-mpv-shim`
- You can also download a zip build.
3. `cd` to the repository: `cd jellyfin-mpv-shim`
4. Run prepare script: `./gen_pkg.sh`
- To do this manually, download the web client, shader pack, and build the language files.
5. Ensure you have a copy of `libmpv1` or `mpv` available.
6. Install any platform-specific dependencies from the respective install tutorials.
7. You should now be able to run the program with `./run.py` or `./run-desktop.py`. Installation is possible with `sudo pip3 install .`.
7. You should now be able to run the program with `./run.py`. Installation is possible with `sudo pip3 install .`.
- You can also install the package with `./gen_pkg.sh --install`.
### Translation
@ -592,13 +564,6 @@ If you are on Linux, you can install via pip. You'll need [libmpv1](https://gith
```bash
sudo pip3 install --upgrade jellyfin-mpv-shim
```
If you would like the Desktop client (run with `jellyfin-mpv-desktop`), also install:
```
sudo apt install python3-flask python3-webview python3-werkzeug
# -- OR --
sudo pip3 install jellyfin-mpv-shim[desktop]
sudo apt install gir1.2-webkit2-4.0
```
If you would like the GUI and systray features, also install `pystray` and `tkinter`:
```bash
sudo pip3 install pystray
@ -637,13 +602,6 @@ To install the CLI version:
3. Install jellyfin-mpv-shim. `pip3 install --upgrade jellyfin-mpv-shim`
4. Run `jellyfin-mpv-shim`.
If you'd like to install the desktop client (currently requires python from brew):
1. Install brew. ([Instructions](https://brew.sh/))
2. Install python3 and mpv. `brew install python mpv`
3. Install jellyfin-mpv-shim. `pip3 install --upgrade 'jellyfin-mpv-shim[desktop]'`
4. Run `jellyfin-mpv-desktop`.
If you'd like to install the GUI version, you need a working copy of tkinter.
1. Install pyenv. ([Instructions](https://medium.com/python-every-day/python-development-on-macos-with-pyenv-2509c694a808))
@ -666,19 +624,17 @@ the installer. If you'd like to build a 32 bit version, download the 32 bit vers
copy it into a new folder called mpv32. You'll also need [WebBrowserInterop.x86.dll](https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x86.dll?raw=true).
You may also need to edit the batch file for 32 bit builds to point to the right python executable.
1. Install Git for Windows. Open Git Bash and run `git clone https://github.com/jellyfin/jellyfin-desktop; cd jellyfin-desktop`.
1. Install Git for Windows. Open Git Bash and run `git clone https://github.com/jellyfin/jellyfin-mpv-shim; cd jellyfin-mpv-shim`.
- You can update the project later with `git pull`.
2. Install [Python3](https://www.python.org/downloads/) with PATH enabled. Install [7zip](https://ninite.com/7zip/).
3. After installing python3, open `cmd` as admin and run `pip install --upgrade pyinstaller python-mpv jellyfin-apiclient-python pywin32 pystray Jinja2 pywebview[cef] python-mpv-jsonipc Flask Werkzeug pypresence pydantic`.
3. After installing python3, open `cmd` as admin and run `pip install --upgrade pyinstaller python-mpv jellyfin-apiclient-python pywin32 pystray Jinja2 pywebview python-mpv-jsonipc pypresence pydantic`.
- You may need to install pydantic with `SKIP_CYTHON=1 pip install git+https://github.com/samuelcolvin/pydantic.git@v0.32`
- Details: https://github.com/pyinstaller/pyinstaller/issues/4346
4. Download [libmpv](https://sourceforge.net/projects/mpv-player-windows/files/libmpv/).
5. Extract the `mpv-1.dll` from the file and move it to the `jellyfin-mpv-shim` folder.
6. Open a regular `cmd` prompt. Navigate to the `jellyfin-mpv-shim` folder.
7. (Edge Build, disabled by default) Download [WebBrowserInterop.x64.dll](https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x64.dll?raw=true) and [Winforms Webview](https://www.nuget.org/api/v2/package/Microsoft.Toolkit.Forms.UI.Controls.WebView/6.0.0).
8. (Edge Build, disabled by default) Rename the `*.nupkg` to a `*.zip` file and extract `lib\net462\Microsoft.Toolkit.Forms.UI.Controls.WebView.dll` to the project root.
9. (CEF Desktop Client) Copy the folder `AppData\Local\Programs\Python\Python37\Lib\site-packages\cefpython3` to `cef\cefpython3`.
10. Run `./gen_pkg.sh --skip-build` using the Git for Windows console.
- This builds the translation files and downloads the web client and shader packs.
11. Remove `libEGL.dll` from `cef\cefpython3`, as it breaks mpv's glsl support. (How CEF works without this is a mystery to me.)
12. Run `build-win.bat`.
7. Download [WebBrowserInterop.x64.dll](https://github.com/r0x0r/pywebview/blob/master/webview/lib/WebBrowserInterop.x64.dll?raw=true) and [Winforms Webview](https://www.nuget.org/api/v2/package/Microsoft.Toolkit.Forms.UI.Controls.WebView/6.0.0).
8. Rename the `*.nupkg` to a `*.zip` file and extract `lib\net462\Microsoft.Toolkit.Forms.UI.Controls.WebView.dll` to the project root.
9. Run `./gen_pkg.sh --skip-build` using the Git for Windows console.
- This builds the translation files and downloads the shader packs.
10. Run `build-win.bat`.

View File

@ -1,12 +1,12 @@
#!/bin/bash
mkdir -p publish publish/Shim publish/DesktopInstaller publish/DesktopInstallerLegacy publish/DesktopDebug
mkdir -p publish publish/Portable publish/Installer publish/InstallerLegacy publish/Debug
version=$(cat jellyfin_mpv_shim/constants.py | grep '^CLIENT_VERSION' | cut -d '"' -f 2)
if [[ "$1" == "standard" ]]
then
cp dist/jellyfin-mpv-desktop_version_installer.exe publish/DesktopInstaller/jellyfin-mpv-desktop_${version}_installer.exe || exit 1
cp dist/jellyfin-mpv-shim_version_installer.exe publish/Installer/jellyfin-mpv-shim_${version}_installer.exe || exit 1
cp dist/run.exe publish/Shim/jellyfin-mpv-shim_${version}.exe || exit 1
mv dist/run-desktop publish/DesktopDebug/ || exit 1
mv dist/run publish/Debug/ || exit 1
elif [[ "$1" == "legacy" ]]
then
cp dist/jellyfin-mpv-desktop_version_installer.exe publish/DesktopInstallerLegacy/jellyfin-mpv-desktop_${version}_LEGACY32_installer.exe || exit 1
cp dist/jellyfin-mpv-shim_version_installer.exe publish/InstallerLegacy/jellyfin-mpv-shim_${version}_LEGACY32_installer.exe || exit 1
fi

View File

@ -26,9 +26,9 @@ jobs:
displayName: Cache pip packages
- task: Cache@2
inputs:
key: '"bindep 2021-04-13" | "$(Agent.OS)"'
key: '"bindep 2021-04-19" | "$(Agent.OS)"'
restoreKeys: |
"bindep 2021-04-13"
"bindep 2021-04-19"
path: bindep
cacheHitVar: CACHE_RESTORED
displayName: Cache binary dependencies
@ -61,7 +61,7 @@ jobs:
TargetFolder: '.'
displayName: Copy Binary Dependencies
- script: pip install .[all] pywebview[cef]==3.4 pywin32 cefpython3==66.0
- script: pip install .[all] pywebview==3.4 pywin32
displayName: PIP Dependencies
- bash: 'pip uninstall -y pydantic; SKIP_CYTHON=1 pip install --force-reinstall --ignore-installed --no-binary :all: pydantic==0.32'
displayName: Pydantic w/o CYTHON
@ -76,12 +76,12 @@ jobs:
- bash: ./artifacts.sh standard
displayName: Main Artifact Rename
- publish: publish/Shim
artifact: ShimPortable
- publish: publish/DesktopInstaller
artifact: DesktopInstaller
- publish: publish/DesktopDebug
artifact: DesktopDebug
- publish: publish/Portable
artifact: Portable
- publish: publish/Installer
artifact: Installer
- publish: publish/Debug
artifact: Debug
- job: LegacyWindows
pool:
@ -103,9 +103,9 @@ jobs:
displayName: Cache pip packages
- task: Cache@2
inputs:
key: '"bindep 2020-08-22" | "$(Agent.OS)"'
key: '"bindep 2021-04-19" | "$(Agent.OS)"'
restoreKeys: |
"bindep 2020-08-22"
"bindep 2021-04-19"
path: bindep
cacheHitVar: CACHE_RESTORED
displayName: Cache binary dependencies
@ -138,7 +138,7 @@ jobs:
TargetFolder: '.'
displayName: Copy Binary Dependencies
- script: pip install .[all] pywebview[cef]==3.3.3 pywin32
- script: pip install .[all] pywebview==3.3.3 pywin32
displayName: PIP Dependencies x86
- bash: 'pip uninstall -y pydantic; SKIP_CYTHON=1 pip install --force-reinstall --ignore-installed --no-binary :all: pydantic==0.32'
displayName: Pydantic w/o CYTHON x86
@ -151,5 +151,5 @@ jobs:
- bash: ./artifacts.sh legacy
displayName: Legacy Artifact Rename
- publish: publish/DesktopInstallerLegacy
artifact: DesktopInstallerLegacy
- publish: publish/InstallerLegacy
artifact: InstallerLegacy

View File

@ -1,20 +1,8 @@
@echo off
rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD%
rem ATTENTION: This file is broken. PyInstaller still packages the 64 bit version of libmpv for some reason.
rem The desktop version (and corresponding shim shortcut) do work, however.
rem Edge-based build
rem "C:\Program Files (x86)\Python37-32\Scripts\pyinstaller" -w --add-binary "mpv32\mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run-desktop-edge.py
rem CEF-based build
pyinstaller -w --add-binary "mpv32\mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --icon jellyfin.ico run-desktop.py
pyinstaller -w --add-binary "mpv32\mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
if %errorlevel% neq 0 exit /b %errorlevel%
xcopy /E /Y cef32\cefpython3 dist\run-desktop\
xcopy /E /Y mpv32\mpv-1.dll dist\run-desktop\
del dist\run-desktop\run-desktop.exe.manifest
copy hidpi.manifest dist\run-desktop\run-desktop.exe.manifest
rem rd /s /q __pycache__ build
rem "C:\Program Files (x86)\Python37-32\Scripts\pyinstaller" -wF --add-binary "mpv32\mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --exclude-module cefpython3 --icon jellyfin.ico run.py
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Jellyfin MPV Desktop.iss"
xcopy /E /Y mpv32\mpv-1.dll dist\run\
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Jellyfin MPV Shim.iss"
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@ -1,13 +1,5 @@
@echo off
rd /s /q __pycache__ build
rd /s /q dist\run-desktop
rd /s /q dist\run
set PATH=%PATH%;%CD%
rem Edge-based build
rem pyinstaller -c --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run-desktop-edge.py
rem CEF-based build
pyinstaller -c --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --icon jellyfin.ico run-desktop.py
xcopy /E /Y cef\cefpython3 dist\run-desktop\
del dist\run-desktop\run-desktop.exe.manifest
copy hidpi.manifest dist\run-desktop\run-desktop.exe.manifest
rem rd /s /q __pycache__ build
rem pyinstaller -cF --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --exclude-module cefpython3 --icon jellyfin.ico run.py
pyinstaller -c --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py

View File

@ -1,16 +1,10 @@
@echo off
rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD%
rem Edge-based build
rem pyinstaller -w --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run-desktop-edge.py
rem CEF-based build
pyinstaller -w --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\webclient_view\webclient;jellyfin_mpv_shim\webclient_view\webclient" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --icon jellyfin.ico run-desktop.py
pyinstaller -w --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
if %errorlevel% neq 0 exit /b %errorlevel%
xcopy /E /Y cef\cefpython3 dist\run-desktop\
del dist\run-desktop\run-desktop.exe.manifest
copy hidpi.manifest dist\run-desktop\run-desktop.exe.manifest
rd /s /q __pycache__ build
pyinstaller -wF --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --exclude-module cefpython3 --icon jellyfin.ico run.py
pyinstaller -wF --add-binary "mpv-1.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
if %errorlevel% neq 0 exit /b %errorlevel%
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Jellyfin MPV Desktop.iss"
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "Jellyfin MPV Shim.iss"
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@ -59,18 +59,17 @@ elif [[ "$1" == "--gen-fingerprint" ]]
then
(
get_resource_version pyinstaller/pyinstaller
get_resource_version iwalton3/jellyfin-web
get_resource_version iwalton3/default-shader-pack
) | tee az-cache-fingerprint.list
exit 0
fi
# Verify versioning
current_version=$(get_resource_version jellyfin/jellyfin-desktop)
current_version=$(get_resource_version jellyfin/jellyfin-mpv-shim)
current_version=${current_version:1}
constants_version=$(cat jellyfin_mpv_shim/constants.py | grep '^CLIENT_VERSION' | cut -d '"' -f 2)
setup_version=$(grep 'version=' setup.py | cut -d '"' -f 2)
iss_version=$(grep '^#define MyAppVersion' "Jellyfin MPV Desktop.iss" | cut -d '"' -f 2)
iss_version=$(grep '^#define MyAppVersion' "Jellyfin MPV Shim.iss" | cut -d '"' -f 2)
appdata_version=$(grep 'release version="' jellyfin_mpv_shim/integration/com.github.iwalton3.jellyfin-mpv-shim.appdata.xml | \
head -n 1 | cut -d '"' -f 2)
@ -93,31 +92,6 @@ do
msgfmt "$file" -o "${file%.*}.mo"
done
# Download web client
update_web_client="no"
if [[ ! -e "jellyfin_mpv_shim/webclient_view/webclient" ]]
then
update_web_client="yes"
elif [[ -e ".last_wc_version" ]]
then
if [[ "$(get_resource_version iwalton3/jellyfin-web)" != "$(cat .last_wc_version)" ]]
then
update_web_client="yes"
fi
fi
if [[ "$update_web_client" == "yes" ]]
then
echo "Downloading web client..."
wc_version=$(get_resource_version iwalton3/jellyfin-web)
download_compat dist.zip "https://github.com/iwalton3/jellyfin-web/releases/download/$wc_version/dist.zip" "wc"
rm -r jellyfin_mpv_shim/webclient_view/webclient 2> /dev/null
rm -r dist 2> /dev/null
unzip dist.zip > /dev/null && rm dist.zip
mv dist jellyfin_mpv_shim/webclient_view/webclient
echo "$wc_version" > .last_wc_version
fi
# Download default-shader-pack
update_shader_pack="no"
if [[ ! -e "jellyfin_mpv_shim/default_shader_pack" ]]

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<dependency>
<dependentAssembly>
<assemblyIdentity language="*" name="Microsoft.Windows.Common-Controls" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" type="win32" version="6.0.0.0"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"/>
</dependentAssembly>
</dependency>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@ -57,10 +57,6 @@ class Settings(BaseModel):
display_mirroring: bool = False
log_decisions: bool = False
mpv_log_level: str = "info"
enable_desktop: bool = False
desktop_fullscreen: bool = False
desktop_keep_pos: bool = False
desktop_keep_size: bool = True
idle_when_paused: bool = False
stop_idle: bool = False
transcode_to_h265: bool = False
@ -108,7 +104,6 @@ class Settings(BaseModel):
check_updates: bool = True
notify_updates: bool = True
lang: Optional[str] = None
desktop_scale: float = 1.0
discord_presence: bool = False
ignore_ssl_cert: bool = False
menu_mouse: bool = True

View File

@ -1,6 +1,6 @@
APP_NAME = "jellyfin-mpv-shim"
USER_APP_NAME = "Jellyfin MPV Shim"
CLIENT_VERSION = "1.10.4"
CLIENT_VERSION = "2.0.0"
USER_AGENT = "Jellyfin-MPV-Shim/%s" % CLIENT_VERSION
CAPABILITIES = {
"PlayableMediaTypes": "Video",

View File

@ -37,38 +37,18 @@ def bind(event_name: str):
class EventHandler(object):
mirror = None
def __init__(self):
self.it_on_event = None
self.it_event_set = set()
def handle_event(
self,
client: "JellyfinClient_type",
event_name: str,
arguments: dict,
from_web=False,
):
# Pass GeneralCommands to desktop client when no media
# is playing and the event doesn't come from the web client.
if (
event_name == "GeneralCommand"
and not playerManager.is_playing()
and self.it_on_event
and (not from_web or arguments.get("Name") != "DisplayContent")
):
timelineManager.delay_idle()
self.it_on_event("GeneralCommand", arguments)
return
if event_name in bindings:
log.debug("Handled Event {0}: {1}".format(event_name, arguments))
bindings[event_name](self, client, event_name, arguments)
else:
log.debug("Unhandled Event {0}: {1}".format(event_name, arguments))
if self.it_on_event and event_name in self.it_event_set:
self.it_on_event(event_name, arguments)
@bind("Play")
def play_media(self, client: "JellyfinClient_type", _event_name, arguments: dict):
play_command = arguments.get("PlayCommand")

View File

@ -5,14 +5,13 @@
<metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0</project_license>
<name>Jellyfin MPV Shim</name>
<summary>Official Desktop Client for Jellyfin Media Server</summary>
<summary>Cast-only client for Jellyfin Media Server</summary>
<developer_name>Ian Walton</developer_name>
<description>
<p>
Jellyfin MPV Shim is a client for the Jellyfin media server which plays media in the
MPV media player. There is a desktop client mode which displays the Jellyfin Web App,
as well as a less resource-intensive background shim application which opens MPV only
MPV media player. The application runs in the background and opens MPV only
when media is cast to the player. The player supports most file formats, allowing you
to prevent needless transcoding of your media files on the server. The player also has
advanced features, such as bulk subtitle updates and launching commands on events.
@ -30,13 +29,13 @@
<category>Player</category>
</categories>
<url type="homepage">https://github.com/jellyfin/jellyfin-desktop</url>
<url type="help">https://github.com/jellyfin/jellyfin-desktop/blob/master/README.md</url>
<url type="bugtracker">https://github.com/jellyfin/jellyfin-desktop/issues</url>
<url type="homepage">https://github.com/jellyfin/jellyfin-mpv-shim</url>
<url type="help">https://github.com/jellyfin/jellyfin-mpv-shim/blob/master/README.md</url>
<url type="bugtracker">https://github.com/jellyfin/jellyfin-mpv-shim/issues</url>
<screenshots>
<screenshot type="default">
<caption>The desktop client</caption>
<caption>The web app casting to MPV Shim</caption>
<image type="source" width="802" height="602">https://user-images.githubusercontent.com/8078788/78717835-392d2c00-78ef-11ea-9731-7fd4a1d8ebbe.png</image>
</screenshot>
<screenshot>
@ -50,16 +49,21 @@
</screenshots>
<launchable type="desktop-id">com.github.iwalton3.jellyfin-mpv-shim.desktop</launchable>
<launchable type="desktop-id">com.github.iwalton3.jellyfin-mpv-shim.desktop-player.desktop</launchable>
<url type="homepage">https://github.com/jellyfin/jellyfin-desktop</url>
<url type="homepage">https://github.com/jellyfin/jellyfin-mpv-shim</url>
<provides>
<binary>jellyfin-mpv-desktop</binary>
<binary>jellyfin-mpv-shim</binary>
</provides>
<content_rating type="oars-1.1" />
<releases>
<release version="2.0.0" date="2021-04-19">
<description>
<p>This release drops the desktop webview mode.</p>
<p>Please use Jellyfin Media Player instead or cast to the application. You can set MPV Shim as the default target in the web player as of 10.7.0.</p>
</description>
</release>
<release version="1.10.4" date="2021-04-13">
<description>
<p>Changes:</p>

View File

@ -1,6 +1,6 @@
[Desktop Entry]
Name=Jellyfin MPV Shim
Exec=jellyfin-mpv-desktop --shim
Exec=jellyfin-mpv-shim
Type=Application
Icon=com.github.iwalton3.jellyfin-mpv-shim
Categories=Video;AudioVideo;TV;Player

View File

@ -1,6 +0,0 @@
[Desktop Entry]
Name=Jellyfin MPV Desktop
Exec=jellyfin-mpv-desktop
Type=Application
Icon=com.github.iwalton3.jellyfin-mpv-shim
Categories=Video;AudioVideo;TV;Player

View File

@ -17,7 +17,7 @@ log = logging.getLogger("")
logging.getLogger("requests").setLevel(logging.CRITICAL)
def main(desktop: bool = False, cef: bool = False):
def main():
conf_file = conffile.get(APP_NAME, "conf.json")
load_success = settings.load(conf_file)
i18n.configure()
@ -36,14 +36,8 @@ def main(desktop: bool = False, cef: bool = False):
mirror = None
use_gui = False
gui_ready = None
use_webview = desktop or settings.enable_desktop
get_webview = lambda: None
if use_webview:
from .webclient_view import WebviewClient
user_interface = WebviewClient(cef=cef)
get_webview = user_interface.get_webview
elif settings.enable_gui:
get_webview = None
if settings.enable_gui:
try:
from .gui_mgr import user_interface
@ -56,7 +50,7 @@ def main(desktop: bool = False, cef: bool = False):
exc_info=True,
)
if settings.display_mirroring and not use_webview:
if settings.display_mirroring:
try:
from .display_mirror import mirror
@ -89,9 +83,7 @@ def main(desktop: bool = False, cef: bool = False):
log.info("Tip: Open the JSON file in VS Code to see what is wrong.")
try:
if use_webview:
user_interface.run()
elif mirror:
if mirror:
user_interface.stop_callback = mirror.stop
# If the webview runs before the systray icon, it fails.
if use_gui:
@ -113,10 +105,5 @@ def main(desktop: bool = False, cef: bool = False):
user_interface.stop()
def main_desktop(cef: bool = False):
desktop = "--shim" not in sys.argv
main(desktop, cef)
if __name__ == "__main__":
main()

View File

@ -152,7 +152,6 @@ class PlayerManager(object):
self.warned_about_transcode = False
self.fullscreen_disable = False
self.update_check = UpdateChecker(self)
self.on_playstate = None
if is_using_ext_mpv:
mpv_options.update(
@ -440,9 +439,7 @@ class PlayerManager(object):
if settings.log_decisions:
log.debug("Playing: {0}".format(url))
if self.get_webview() is not None and (
settings.display_mirroring or settings.desktop_fullscreen
):
if self.get_webview() is not None and settings.display_mirroring:
# noinspection PyUnresolvedReferences
self.get_webview().hide()
@ -875,10 +872,7 @@ class PlayerManager(object):
and self._video
and not self._player.playback_abort
):
options = self.get_timeline_options()
if self.on_playstate:
self.on_playstate("initial", options, self._video.item)
self._video.client.jellyfin.session_progress(options)
self._video.client.jellyfin.session_progress(self.get_timeline_options())
try:
if self.syncplay.is_enabled():
self.syncplay.sync_playback_time()
@ -887,10 +881,7 @@ class PlayerManager(object):
@synchronous("_tl_lock")
def send_timeline_initial(self):
options = self.get_timeline_options()
if self.on_playstate:
self.on_playstate("initial", options, self._video.item)
self._video.client.jellyfin.session_playing(options)
self._video.client.jellyfin.session_playing(self.get_timeline_options())
@synchronous("_tl_lock")
def send_timeline_stopped(self, finished=False, options=None, client=None):
@ -902,13 +893,9 @@ class PlayerManager(object):
if client is None:
client = self._video.client
if self.on_playstate:
self.on_playstate("stopped", options)
client.jellyfin.session_stop(options)
if self.get_webview() is not None and (
settings.display_mirroring or settings.desktop_fullscreen
):
if self.get_webview() is not None and settings.display_mirroring:
self.get_webview().show()
if discord_presence:

View File

@ -14,7 +14,7 @@ if TYPE_CHECKING:
log = logging.getLogger("update_check")
release_url = "https://github.com/jellyfin/jellyfin-desktop/releases/"
release_url = "https://github.com/jellyfin/jellyfin-mpv-shim/releases/"
release_urls = [
release_url,
"https://github.com/jellyfin/jellyfin-mpv-shim/releases/",

View File

@ -1,436 +0,0 @@
from queue import Empty, Queue
import threading
import time
import urllib.request
from werkzeug.serving import make_server
from flask import Flask, request, jsonify
from time import sleep
import os.path
import json
import base64
# So, most of my use of the webview library is ugly but there's a reason for this!
# Debian's python3-webview package is super old (2.3), pip3's pywebview package is much newer (3.2).
# I would just say "fuck it, install from testing/backports/unstable" except that's not any newer either.
# So instead I'm going to try supporting *both* here, which gets rather ugly because they made very significant changes.
#
# The key differences seem to be:
# 3.2's create_window() returns a Window object immediately, then you need to call start()
# 2.3's create_window() blocks forever effectivelly calling start() itself
#
# 3.2's Window has a .loaded Event that you need to subscribe to notice when the window is ready for input
# 2.3 has a webview_ready() function that blocks until webview is ready (or timeout is passed)
import webview # Python3-webview in Debian, pywebview in pypi
import sys
from threading import Event
import datetime
from ..clients import clientManager
from ..player import playerManager
from ..conf import settings
from ..event_handler import eventHandler
from ..constants import CAPABILITIES, CLIENT_VERSION, USER_APP_NAME, APP_NAME
from ..utils import get_resource
from .. import conffile
import logging
log = logging.getLogger("webclient")
remember_layout = conffile.get(APP_NAME, "layout.json")
def do_not_cache(response):
response.cache_control.no_store = True
if response.cache_control.max_age:
response.cache_control.max_age = None
if response.cache_control.public:
response.cache_control.public = False
# Based on https://stackoverflow.com/questions/15562446/
class Server(threading.Thread):
def __init__(self):
self.srv = None
self.ctx = None
threading.Thread.__init__(self)
def stop(self):
if self.srv is not None:
self.srv.shutdown()
self.join()
def run(self):
app = Flask(
__name__,
static_url_path="",
static_folder=get_resource("webclient_view", "webclient"),
)
pl_event_queue = Queue()
last_server_id = ""
last_user_id = ""
last_user_name = ""
def wrap_playstate(active, playstate=None, item=None):
if playstate is None:
playstate = {
"CanSeek": False,
"IsPaused": False,
"IsMuted": False,
"RepeatMode": "RepeatNone",
}
res = {
"PlayState": playstate,
"AdditionalUsers": [],
"Capabilities": {
"PlayableMediaTypes": CAPABILITIES["PlayableMediaTypes"].split(","),
"SupportedCommands": CAPABILITIES["SupportedCommands"].split(","),
"SupportsMediaControl": True,
"SupportsContentUploading": False,
"SupportsPersistentIdentifier": False,
"SupportsSync": False,
},
"RemoteEndPoint": "0.0.0.0",
"PlayableMediaTypes": CAPABILITIES["PlayableMediaTypes"].split(","),
"Id": settings.client_uuid,
"UserId": last_user_id,
"UserName": last_user_name,
"Client": USER_APP_NAME,
"LastActivityDate": datetime.datetime.utcnow().isoformat(),
"LastPlaybackCheckIn": "0001-01-01T00:00:00.0000000Z",
"DeviceName": settings.player_name,
"DeviceId": settings.client_uuid,
"ApplicationVersion": CLIENT_VERSION,
"IsActive": active,
"SupportsMediaControl": True,
"SupportsRemoteControl": True,
"HasCustomDeviceName": False,
"ServerId": last_server_id,
"SupportedCommands": CAPABILITIES["SupportedCommands"].split(","),
"dest": "player",
}
if "NowPlayingQueue" in playstate:
res["NowPlayingQueue"] = playstate["NowPlayingQueue"]
if "PlaylistItemId" in playstate:
res["PlaylistItemId"] = playstate["PlaylistItemId"]
if item:
res["NowPlayingItem"] = item
return res
def on_playstate(state, payload=None, item=None):
pl_event_queue.put(wrap_playstate(True, payload, item))
if state == "stopped":
pl_event_queue.put(wrap_playstate(False))
def it_on_event(name, event):
server_id = event["ServerId"]
if type(event) is dict and "value" in event and len(event) == 2:
event = event["value"]
pl_event_queue.put(
{
"dest": "ws",
"MessageType": name,
"Data": event,
"ServerId": server_id,
}
)
playerManager.on_playstate = on_playstate
eventHandler.it_on_event = it_on_event
eventHandler.it_event_set = {
"ActivityLogEntry",
"LibraryChanged",
"PackageInstallationCancelled",
"PackageInstallationCompleted",
"PackageInstallationFailed",
"PackageInstalling",
"RefreshProgress",
"RestartRequired",
"ScheduledTasksInfo",
"SeriesTimerCancelled",
"SeriesTimerCancelled",
"SeriesTimerCreated",
"SeriesTimerCreated",
"ServerRestarting",
"ServerShuttingDown",
"Sessions",
"TimerCancelled",
"TimerCreated",
"UserDataChanged",
}
@app.after_request
def add_header(response):
if request.path == "/index.html":
do_not_cache(response)
client_data = base64.b64encode(
json.dumps(
{
"appName": USER_APP_NAME,
"appVersion": CLIENT_VERSION,
"deviceName": settings.player_name,
"deviceId": settings.client_uuid,
}
).encode("ascii")
)
# We need access to this data before we can make an async web call.
replacement = (
b"""<body><script type="application/json" id="clientData">%s</script>"""
% client_data
)
if settings.desktop_scale != 1.0:
f_scale = float(settings.desktop_scale)
replacement = replacement + (
b"""<style>body { zoom: %.2f; }</style>""" % f_scale
)
response.make_sequence()
response.set_data(response.get_data().replace(b"<body>", replacement,))
return response
if not response.cache_control.no_store:
response.cache_control.max_age = 2592000
return response
@app.route("/mpv_shim_session", methods=["POST"])
def mpv_shim_session():
nonlocal last_server_id, last_user_id, last_user_name
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
req = request.json
log.info(
"Recieved session for server: {0}, user: {1}".format(
req["Name"], req["username"]
)
)
if req["Id"] not in clientManager.clients:
is_logged_in = clientManager.connect_client(req)
log.info("Connection was successful.")
else:
is_logged_in = True
log.info("Ignoring as client already exists.")
last_server_id = req["Id"]
last_user_id = req["UserId"]
last_user_name = req["username"]
resp = jsonify({"success": is_logged_in})
resp.status_code = 200 if is_logged_in else 500
do_not_cache(resp)
return resp
@app.route("/mpv_shim_event", methods=["POST"])
def mpv_shim_event():
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
try:
queue_item = pl_event_queue.get(timeout=5)
except Empty:
queue_item = {}
resp = jsonify(queue_item)
resp.status_code = 200
do_not_cache(resp)
return resp
@app.route("/mpv_shim_message", methods=["POST"])
def mpv_shim_message():
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
req = request.json
client = clientManager.clients.get(req["payload"]["ServerId"])
resp = jsonify({})
resp.status_code = 200
do_not_cache(resp)
if client is None:
log.warning("Message recieved but no client available. Ignoring.")
return resp
eventHandler.handle_event(
client, req["name"], req["payload"], from_web=True
)
return resp
@app.route("/mpv_shim_wsmessage", methods=["POST"])
def mpv_shim_wsmessage():
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
req = request.json
client = clientManager.clients.get(req["ServerId"])
resp = jsonify({})
resp.status_code = 200
do_not_cache(resp)
if client is None:
log.warning("Message recieved but no client available. Ignoring.")
return resp
client.wsc.send(req["name"], req.get("payload", ""))
return resp
@app.route("/mpv_shim_teardown", methods=["POST"])
def mpv_shim_teardown():
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
log.info("Client teardown requested.")
clientManager.stop_all_clients()
resp = jsonify({})
resp.status_code = 200
do_not_cache(resp)
return resp
@app.route("/mpv_shim_syncplay_join", methods=["POST"])
def mpv_shim_join():
if request.headers["Content-Type"] != "application/json; charset=UTF-8":
return "Go Away"
req = request.json
client = list(clientManager.clients.values())[0]
playerManager.syncplay.client = client
playerManager.syncplay.join_group(req["GroupId"])
resp = jsonify({})
resp.status_code = 200
do_not_cache(resp)
return resp
self.srv = make_server("127.0.0.1", 18096, app, threaded=True)
self.ctx = app.app_context()
self.ctx.push()
self.srv.serve_forever()
# This makes me rather uncomfortable, but there's no easy way around this other than
# importing display_mirror in helpers. Lambda needed because the 2.3 version of the JS
# api adds an argument even when not used.
class WebviewClient(object):
def __init__(self, cef=False):
self.open_player_menu = lambda: None
self.server = Server()
self.cef = cef
self.webview = None
def start(self):
pass
@staticmethod
def login_servers():
pass
def get_webview(self):
return self.webview
def run(self):
self.server.start()
extra_options = {}
if os.path.exists(remember_layout):
with open(remember_layout) as fh:
layout_options = json.load(fh)
if (
settings.desktop_keep_pos
and layout_options.get("x")
and layout_options.get("y")
):
extra_options["x"] = layout_options["x"]
extra_options["y"] = layout_options["y"]
if (
settings.desktop_keep_size
and layout_options.get("width")
and layout_options.get("height")
):
extra_options["width"] = layout_options["width"]
extra_options["height"] = layout_options["height"]
else:
# Set a reasonable window size
extra_options.update({"width": 1280, "height": 720})
if (
not self.cef
and sys.platform.startswith("win32")
or sys.platform.startswith("cygwin")
):
# I wasted half a day here. Turns out that pywebview does something that
# breaks Jellyfin on Windows in both EdgeHTML and CEF. This kills that.
try:
from webview.platforms import winforms
winforms.BrowserView.EdgeHTML.on_navigation_completed = lambda: None
except Exception:
pass
# Apparently pywebview3 breaks everywhere, not just on Windows.
if self.cef:
try:
from webview.platforms import cef
cef.settings.update({"cache_path": conffile.get(APP_NAME, "cache")})
cef.Browser.initialize = lambda self: None
except Exception:
pass
try:
from webview.platforms import cocoa
def override_cocoa(_self, webview, _nav):
# Add the webview to the window if it's not yet the contentView
i = cocoa.BrowserView.get_instance("webkit", webview)
if i:
if not webview.window():
i.window.setContentView_(webview)
i.window.makeFirstResponder_(webview)
cocoa.BrowserView.BrowserDelegate.webView_didFinishNavigation_ = (
override_cocoa
)
except Exception:
pass
try:
from webview.platforms import gtk
# noinspection PyUnresolvedReferences
def override_gtk(_self, webview, _status):
if not webview.props.opacity:
gtk.glib.idle_add(webview.set_opacity, 1.0)
gtk.BrowserView.on_load_finish = override_gtk
except Exception:
pass
try:
from webview.platforms import qt
qt.BrowserView.on_load_finished = lambda self: None
except Exception:
pass
url = "http://127.0.0.1:18096/index.html"
# Wait until the server is ready.
while True:
try:
urllib.request.urlopen(url)
break
except Exception:
pass
sleep(0.1)
# Webview needs to be run in the MainThread.
window = webview.create_window(
url=url,
title="Jellyfin MPV Desktop",
fullscreen=settings.desktop_fullscreen,
**extra_options
)
if window is not None:
self.webview = window
def handle_close():
x, y = window.x, window.y
extra_options = {
"x": x,
"y": y,
"width": window.width,
"height": window.height,
}
with open(remember_layout, "w") as fh:
json.dump(extra_options, fh)
window.closing += handle_close
if self.cef:
webview.start(gui="cef")
else:
webview.start()
self.server.stop()
def stop(self):
self.server.stop()

View File

@ -1,23 +0,0 @@
#!/usr/bin/env python3
# Newer revisions of python-mpv require mpv-1.dll in the PATH.
import os
import sys
import multiprocessing
if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"):
# Detect if bundled via pyinstaller.
# From: https://stackoverflow.com/questions/404744/
if getattr(sys, "frozen", False):
application_path = getattr(sys, "_MEIPASS")
else:
application_path = os.path.dirname(os.path.abspath(__file__))
os.environ["PATH"] = application_path + os.pathsep + os.environ["PATH"]
from jellyfin_mpv_shim.mpv_shim import main_desktop
if __name__ == "__main__":
# https://stackoverflow.com/questions/24944558/pyinstaller-built-windows-exe-fails-with-multiprocessing
multiprocessing.freeze_support()
main_desktop()

View File

@ -1,23 +0,0 @@
#!/usr/bin/env python3
# Newer revisions of python-mpv require mpv-1.dll in the PATH.
import os
import sys
import multiprocessing
if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"):
# Detect if bundled via pyinstaller.
# From: https://stackoverflow.com/questions/404744/
if getattr(sys, "frozen", False):
application_path = getattr(sys, "_MEIPASS")
else:
application_path = os.path.dirname(os.path.abspath(__file__))
os.environ["PATH"] = application_path + os.pathsep + os.environ["PATH"]
from jellyfin_mpv_shim.mpv_shim import main_desktop
if __name__ == "__main__":
# https://stackoverflow.com/questions/24944558/pyinstaller-built-windows-exe-fails-with-multiprocessing
multiprocessing.freeze_support()
main_desktop(cef=True)

View File

@ -1,20 +1,19 @@
from setuptools import setup
APP = ["/usr/local/bin/jellyfin-mpv-desktop"]
APP = ["/usr/local/bin/jellyfin-mpv-shim"]
OPTIONS = {
"argv_emulation": True,
"iconfile": "jellyfin.icns",
"resources": [
"/usr/local/bin/mpv",
"/usr/local/bin/jellyfin-mpv-shim",
"/usr/local/lib/python3.9/site-packages/jellyfin_mpv_shim/webclient_view",
"/usr/local/bin/jellyfin-mpv-shim"
],
"packages": ["pkg_resources"],
}
setup(
app=APP,
name="Jellyfin MPV Desktop",
name="Jellyfin MPV Shim",
options={"py2app": OPTIONS},
setup_requires=["py2app"],
)

View File

@ -5,18 +5,17 @@ with open("README.md", "r") as fh:
setup(
name="jellyfin-mpv-shim",
version="1.10.4",
version="2.0.0",
author="Ian Walton",
author_email="iwalton3@gmail.com",
description="Cast media from Jellyfin Mobile and Web apps to MPV.",
license="GPLv3",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
url="https://github.com/jellyfin/jellyfin-desktop",
url="https://github.com/jellyfin/jellyfin-mpv-shim",
packages=[
"jellyfin_mpv_shim",
"jellyfin_mpv_shim.display_mirror",
"jellyfin_mpv_shim.webclient_view",
"jellyfin_mpv_shim.display_mirror"
],
package_data={
"jellyfin_mpv_shim.display_mirror": ["*.css", "*.html"],
@ -24,9 +23,7 @@ setup(
},
entry_points={
"console_scripts": [
"jellyfin-mpv-shim=jellyfin_mpv_shim.mpv_shim:main",
"jellyfin-mpv-desktop=jellyfin_mpv_shim.mpv_shim:main_desktop",
"jellyfin-desktop=jellyfin_mpv_shim.mpv_shim:main_desktop",
"jellyfin-mpv-shim=jellyfin_mpv_shim.mpv_shim:main"
]
},
classifiers=[
@ -37,14 +34,11 @@ setup(
extras_require={
"gui": ["pystray", "PIL"],
"mirror": ["Jinja2", "pywebview>=3.3.1"],
"desktop": ["Flask", "pywebview>=3.3.1", "Werkzeug"],
"discord": ["pypresence"],
"all": [
"Jinja2",
"pywebview>=3.3.1",
"pystray",
"Flask",
"Werkzeug",
"pypresence",
],
},