mirror of
https://github.com/jellyfin/jellyfin-mpv-shim.git
synced 2024-11-22 21:49:41 +00:00
Linter warnings.
This commit is contained in:
parent
1e104a8c86
commit
21ca9d1087
@ -1,4 +1,3 @@
|
||||
import logging
|
||||
import threading
|
||||
|
||||
from .player import playerManager
|
||||
@ -18,7 +17,7 @@ class ActionThread(threading.Thread):
|
||||
def run(self):
|
||||
force_next = False
|
||||
while not self.halt:
|
||||
if (playerManager._player and playerManager._video) or force_next:
|
||||
if playerManager.is_active() or force_next:
|
||||
playerManager.update()
|
||||
|
||||
force_next = False
|
||||
|
@ -29,11 +29,11 @@ def render_message(message, show_text):
|
||||
|
||||
def process_series(mode, player, m_raid=None, m_rsid=None):
|
||||
messages.clear()
|
||||
media = player._video
|
||||
media = player.get_video()
|
||||
client = media.client
|
||||
show_text = player._player.show_text
|
||||
show_text = player.show_text
|
||||
c_aid, c_sid = None, None
|
||||
c_pid = player._video.media_source.get("Id")
|
||||
c_pid = media.media_source.get("Id")
|
||||
|
||||
success_ct = 0
|
||||
partial_ct = 0
|
||||
|
@ -1,4 +1,3 @@
|
||||
import time
|
||||
from .clients import clientManager
|
||||
|
||||
|
||||
@ -7,7 +6,8 @@ class UserInterface(object):
|
||||
self.open_player_menu = lambda: None
|
||||
self.stop = lambda: None
|
||||
|
||||
def login_servers(self):
|
||||
@staticmethod
|
||||
def login_servers():
|
||||
clientManager.cli_connect()
|
||||
|
||||
def start(self):
|
||||
@ -17,4 +17,4 @@ class UserInterface(object):
|
||||
pass
|
||||
|
||||
|
||||
userInterface = UserInterface()
|
||||
user_interface = UserInterface()
|
||||
|
@ -57,7 +57,8 @@ class ClientManager(object):
|
||||
else:
|
||||
log.warning(_("Adding server failed."))
|
||||
|
||||
def client_factory(self):
|
||||
@staticmethod
|
||||
def client_factory():
|
||||
client = JellyfinClient(allow_multiple_clients=True)
|
||||
client.config.data["app.default"] = True
|
||||
client.config.app(
|
||||
@ -208,7 +209,7 @@ class ClientManager(object):
|
||||
if uuid is None and server is not None:
|
||||
uuid = server["uuid"]
|
||||
|
||||
if not uuid in self.clients:
|
||||
if uuid not in self.clients:
|
||||
return
|
||||
|
||||
if server is not None:
|
||||
|
@ -4,7 +4,7 @@ import sys
|
||||
import getpass
|
||||
|
||||
# If no platform is matched, use the current directory.
|
||||
_confdir = lambda app: ""
|
||||
_confdir = None
|
||||
username = getpass.getuser()
|
||||
|
||||
|
||||
@ -47,8 +47,10 @@ for i, arg in enumerate(sys.argv):
|
||||
def confdir(app):
|
||||
if custom_config is not None:
|
||||
return custom_config
|
||||
else:
|
||||
elif _confdir is not None:
|
||||
return _confdir(app)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def get(app, conf_file, create=False):
|
||||
|
@ -15,13 +15,13 @@ import jinja2 # python3-jinja2 in Debian, Jinja2 in pypi
|
||||
# 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
|
||||
|
||||
from ..clients import clientManager
|
||||
from ..utils import get_text
|
||||
from ..i18n import _
|
||||
from . import helpers
|
||||
|
||||
# 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.
|
||||
# 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.
|
||||
helpers.on_escape = lambda _x=None: load_idle()
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ class DisplayMirror(object):
|
||||
def get_webview(self):
|
||||
return self.webview
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def run(self):
|
||||
# Webview needs to be run in the MainThread.
|
||||
|
||||
@ -56,15 +57,19 @@ class DisplayMirror(object):
|
||||
self.webview = window
|
||||
|
||||
# 3.2's .loaded event runs every time a new DOM is loaded as well, so not suitable for this purpose
|
||||
# However, 3.2's load_html waits for the DOM to be ready, so we can completely skip waiting for that ourselves.
|
||||
# However, 3.2's load_html waits for the DOM to be ready, so we can completely skip
|
||||
# waiting for that ourselves.
|
||||
threading.Thread(target=load_idle).start()
|
||||
|
||||
webview.start()
|
||||
|
||||
def stop(self):
|
||||
webview.destroy_window()
|
||||
if hasattr(webview, "destroy_window"):
|
||||
getattr(webview, "destroy_window")()
|
||||
else:
|
||||
self.webview.destroy()
|
||||
|
||||
def DisplayContent(self, client, arguments):
|
||||
def display_content(self, client, arguments):
|
||||
item = client.jellyfin.get_item(arguments["Arguments"]["ItemId"])
|
||||
html = get_html(server_address=client.config.data["auth.server"], item=item)
|
||||
self.display_window.load_html(html)
|
||||
@ -110,9 +115,7 @@ def get_html(server_address=None, item=None):
|
||||
), # FIME: Mention the player_name here
|
||||
}
|
||||
|
||||
jinja_vars.update(
|
||||
{"jellyfin_css": get_text("display_mirror", "jellyfin.css"),}
|
||||
)
|
||||
jinja_vars.update({"jellyfin_css": get_text("display_mirror", "jellyfin.css")})
|
||||
|
||||
try:
|
||||
tpl = jinja2.Template(get_text("display_mirror", "index.html"))
|
||||
|
@ -5,9 +5,11 @@ import math
|
||||
from ..clients import clientManager
|
||||
|
||||
# This started as a copy of some useful functions from jellyfin-chromecast's helpers.js and translated them to Python.
|
||||
# Only reason their not put straight into __init__.py is to keep the same logical separation that jellyfin-chromecast has.
|
||||
# Only reason their not put straight into __init__.py is to keep the same logical separation that
|
||||
# jellyfin-chromecast has.
|
||||
#
|
||||
# I've since added some extra functions, and completely reworked some of the old ones such that it's not directly compatible.
|
||||
# I've since added some extra functions, and completely reworked some of the old ones such that it's
|
||||
# not directly compatible.
|
||||
#
|
||||
# Should this stuff be in jellyfin_apiclient_python instead?
|
||||
# Is this stuff in there already?
|
||||
@ -15,6 +17,7 @@ from ..clients import clientManager
|
||||
# FIXME: A lot of this could be done so much better with format-strings
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyPep8Naming
|
||||
def getUrl(serverAddress, name):
|
||||
|
||||
if not name:
|
||||
@ -27,6 +30,7 @@ def getUrl(serverAddress, name):
|
||||
return url
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyPep8Naming
|
||||
def getBackdropUrl(item, serverAddress):
|
||||
if item.get("BackdropImageTags"):
|
||||
return getUrl(
|
||||
@ -48,6 +52,7 @@ def getBackdropUrl(item, serverAddress):
|
||||
return None
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyPep8Naming
|
||||
def getLogoUrl(item, serverAddress):
|
||||
if item.get("ImageTags", {}).get("Logo", None):
|
||||
return getUrl(
|
||||
@ -66,6 +71,7 @@ def getLogoUrl(item, serverAddress):
|
||||
return None
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyPep8Naming
|
||||
def getPrimaryImageUrl(item, serverAddress):
|
||||
if item.get("AlbumPrimaryImageTag"):
|
||||
return getUrl(
|
||||
@ -92,6 +98,7 @@ def getPrimaryImageUrl(item, serverAddress):
|
||||
return None
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def getDisplayName(item):
|
||||
name = item.get("EpisodeTitle", item.get("Name"))
|
||||
|
||||
@ -114,6 +121,7 @@ def getDisplayName(item):
|
||||
return name
|
||||
|
||||
|
||||
# noinspection PyPep8Naming
|
||||
def getRatingHtml(item):
|
||||
html = ""
|
||||
|
||||
@ -156,6 +164,7 @@ def __convert_jf_str_datetime(jf_string):
|
||||
return datetime.datetime.strptime(jf_string.partition(".")[0], "%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
|
||||
# noinspection PyPep8Naming,PyPep8Naming,PyPep8Naming
|
||||
def getMiscInfoHtml(item):
|
||||
# FIXME: Flake8 is complaining this function is too complex.
|
||||
# I agree, this needs to be cleaned up, a lot.
|
||||
@ -172,7 +181,7 @@ def getMiscInfoHtml(item):
|
||||
if item.get("StartDate"):
|
||||
date = __convert_jf_str_datetime(item["StartDate"])
|
||||
text = date.strftime("%x")
|
||||
miscInfo.push(text)
|
||||
miscInfo.append(text)
|
||||
|
||||
if item["Type"] != "Recording":
|
||||
pass
|
||||
@ -201,7 +210,7 @@ def getMiscInfoHtml(item):
|
||||
if item["Type"] == "Audio":
|
||||
# FIXME
|
||||
raise Exception("Haven't translated this to Python yet")
|
||||
miscInfo.append(datetime.getDisplayRunningTime(item["RunTimeTicks"]))
|
||||
# miscInfo.append(datetime.getDisplayRunningTime(item["RunTimeTicks"]))
|
||||
else:
|
||||
# Using math.ceil instead of round because I want the minutes rounded *up* specifically,
|
||||
# mostly because '1min' makes more sense than '0min' for a 1-59sec clip
|
||||
@ -222,9 +231,11 @@ def getMiscInfoHtml(item):
|
||||
return " ".join(miscInfo)
|
||||
|
||||
|
||||
# For some reason the webview 2.3 js api will send a positional argument of None when there are no arguments being passed in.
|
||||
# This really long argument name is here to catch that and hopefully not eat other intentional arguments.
|
||||
def getRandomBackdropUrl(positional_arg_that_is_never_used=None, **params):
|
||||
# For some reason the webview 2.3 js api will send a positional argument of None when there are no
|
||||
# arguments being passed in. This really long argument name is here to catch that and hopefully not
|
||||
# eat other intentional arguments.
|
||||
# noinspection PyPep8Naming
|
||||
def getRandomBackdropUrl(_positional_arg_that_is_never_used=None, **params):
|
||||
# This function is to get 1 random item, so ignore those arguments
|
||||
params["SortBy"] = "Random"
|
||||
params["Limit"] = 1
|
||||
|
@ -1,7 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
from .utils import plex_color_to_mpv
|
||||
from .conf import settings
|
||||
from .media import Media
|
||||
from .player import playerManager
|
||||
@ -41,9 +40,9 @@ class EventHandler(object):
|
||||
log.debug("Unhandled Event {0}: {1}".format(event_name, arguments))
|
||||
|
||||
@bind("Play")
|
||||
def play_media(self, client, event_name, arguments):
|
||||
def play_media(self, client, _event_name, arguments):
|
||||
play_command = arguments.get("PlayCommand")
|
||||
if not playerManager._video:
|
||||
if not playerManager.has_video():
|
||||
play_command = "PlayNow"
|
||||
|
||||
if play_command == "PlayNow":
|
||||
@ -70,22 +69,22 @@ class EventHandler(object):
|
||||
if settings.pre_media_cmd:
|
||||
os.system(settings.pre_media_cmd)
|
||||
playerManager.play(video, offset)
|
||||
timelineManager.SendTimeline()
|
||||
timelineManager.send_timeline()
|
||||
if arguments.get("SyncPlayGroup") is not None:
|
||||
playerManager.syncplay.join_group(arguments["SyncPlayGroup"])
|
||||
elif play_command == "PlayLast":
|
||||
playerManager._video.parent.insert_items(
|
||||
playerManager.get_video().parent.insert_items(
|
||||
arguments.get("ItemIds"), append=True
|
||||
)
|
||||
playerManager.upd_player_hide()
|
||||
elif play_command == "PlayNext":
|
||||
playerManager._video.parent.insert_items(
|
||||
playerManager.get_video().parent.insert_items(
|
||||
arguments.get("ItemIds"), append=False
|
||||
)
|
||||
playerManager.upd_player_hide()
|
||||
|
||||
@bind("GeneralCommand")
|
||||
def general_command(self, client, event_name, arguments):
|
||||
def general_command(self, client, _event_name, arguments):
|
||||
command = arguments.get("Name")
|
||||
if command == "SetVolume":
|
||||
# There is currently a bug that causes this to be spammed, so we
|
||||
@ -100,7 +99,7 @@ class EventHandler(object):
|
||||
# If you have an idle command set, this will delay it.
|
||||
timelineManager.delay_idle()
|
||||
if self.mirror:
|
||||
self.mirror.DisplayContent(client, arguments)
|
||||
self.mirror.display_content(client, arguments)
|
||||
elif command in (
|
||||
"Back",
|
||||
"Select",
|
||||
@ -121,7 +120,7 @@ class EventHandler(object):
|
||||
playerManager.toggle_fullscreen()
|
||||
|
||||
@bind("Playstate")
|
||||
def play_state(self, client, event_name, arguments):
|
||||
def play_state(self, _client, _event_name, arguments):
|
||||
command = arguments.get("Command")
|
||||
if command == "PlayPause":
|
||||
playerManager.toggle_pause()
|
||||
@ -141,17 +140,17 @@ class EventHandler(object):
|
||||
)
|
||||
|
||||
@bind("PlayPause")
|
||||
def pausePlay(self, client, event_name, arguments):
|
||||
def pause_play(self, _client, _event_name, _arguments):
|
||||
playerManager.toggle_pause()
|
||||
timelineManager.SendTimeline()
|
||||
timelineManager.send_timeline()
|
||||
|
||||
@bind("SyncPlayGroupUpdate")
|
||||
def sync_play_group_update(self, client, event_name, arguments):
|
||||
def sync_play_group_update(self, client, _event_name, arguments):
|
||||
playerManager.syncplay.client = client
|
||||
playerManager.syncplay.process_group_update(arguments)
|
||||
|
||||
@bind("SyncPlayCommand")
|
||||
def sync_play_command(self, client, event_name, arguments):
|
||||
def sync_play_command(self, client, _event_name, arguments):
|
||||
playerManager.syncplay.client = client
|
||||
playerManager.syncplay.process_command(arguments)
|
||||
|
||||
|
@ -6,7 +6,6 @@ import threading
|
||||
import sys
|
||||
import logging
|
||||
import queue
|
||||
import os.path
|
||||
|
||||
from .constants import USER_APP_NAME, APP_NAME
|
||||
from .conffile import confdir
|
||||
@ -19,6 +18,8 @@ log = logging.getLogger("gui_mgr")
|
||||
|
||||
# From https://stackoverflow.com/questions/6631299/
|
||||
# This is for opening the config directory.
|
||||
|
||||
|
||||
def _show_file_darwin(path):
|
||||
subprocess.Popen(["open", path])
|
||||
|
||||
@ -82,6 +83,9 @@ root_logger.addHandler(guiHandler)
|
||||
class LoggerWindow(threading.Thread):
|
||||
def __init__(self):
|
||||
self.dead = False
|
||||
self.queue = None
|
||||
self.r_queue = None
|
||||
self.process = None
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
@ -104,7 +108,7 @@ class LoggerWindow(threading.Thread):
|
||||
def handle(self, action, params=None):
|
||||
self.queue.put((action, params))
|
||||
|
||||
def stop(self, is_source=False):
|
||||
def stop(self):
|
||||
self.r_queue.put(("die", None))
|
||||
|
||||
def _die(self):
|
||||
@ -118,6 +122,9 @@ class LoggerWindowProcess(Process):
|
||||
def __init__(self, queue, r_queue):
|
||||
self.queue = queue
|
||||
self.r_queue = r_queue
|
||||
self.tk = None
|
||||
self.root = None
|
||||
self.text = None
|
||||
Process.__init__(self)
|
||||
|
||||
def update(self):
|
||||
@ -141,7 +148,6 @@ class LoggerWindowProcess(Process):
|
||||
|
||||
def run(self):
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox
|
||||
|
||||
self.tk = tk
|
||||
root = tk.Tk()
|
||||
@ -164,6 +170,9 @@ class PreferencesWindow(threading.Thread):
|
||||
def __init__(self):
|
||||
self.dead = False
|
||||
self.dead_trigger = threading.Event()
|
||||
self.queue = None
|
||||
self.r_queue = None
|
||||
self.process = None
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
def run(self):
|
||||
@ -194,7 +203,7 @@ class PreferencesWindow(threading.Thread):
|
||||
def handle(self, action, params=None):
|
||||
self.queue.put((action, params))
|
||||
|
||||
def stop(self, is_source=False):
|
||||
def stop(self):
|
||||
self.r_queue.put(("die", None))
|
||||
|
||||
def block_until_close(self):
|
||||
@ -211,6 +220,18 @@ class PreferencesWindowProcess(Process):
|
||||
def __init__(self, queue, r_queue):
|
||||
self.queue = queue
|
||||
self.r_queue = r_queue
|
||||
self.servers = None
|
||||
self.server_ids = None
|
||||
self.tk = None
|
||||
self.messagebox = None
|
||||
self.root = None
|
||||
self.serverList = None
|
||||
self.current_uuid = None
|
||||
self.servername = None
|
||||
self.username = None
|
||||
self.password = None
|
||||
self.add_button = None
|
||||
self.remove_button = None
|
||||
Process.__init__(self)
|
||||
|
||||
def update(self):
|
||||
@ -266,7 +287,7 @@ class PreferencesWindowProcess(Process):
|
||||
self.serverList = tk.StringVar(value=[])
|
||||
self.current_uuid = None
|
||||
|
||||
def serverSelect(_x):
|
||||
def server_select(_x):
|
||||
idxs = serverlist.curselection()
|
||||
if len(idxs) == 1:
|
||||
self.current_uuid = self.server_ids[idxs[0]]
|
||||
@ -322,7 +343,7 @@ class PreferencesWindowProcess(Process):
|
||||
close_button = ttk.Button(c, text=_("Close"), command=close)
|
||||
close_button.grid(column=2, row=4, pady=10, sticky=(tk.E, tk.S))
|
||||
|
||||
serverlist.bind("<<ListboxSelect>>", serverSelect)
|
||||
serverlist.bind("<<ListboxSelect>>", server_select)
|
||||
self.update()
|
||||
root.mainloop()
|
||||
self.r_queue.put(("die", None))
|
||||
@ -348,6 +369,8 @@ class UserInterface(threading.Thread):
|
||||
self.preferences_window = None
|
||||
self.stop_callback = None
|
||||
self.gui_ready = None
|
||||
self.r_queue = None
|
||||
self.process = None
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
@ -398,7 +421,8 @@ class UserInterface(threading.Thread):
|
||||
if self.gui_ready:
|
||||
self.gui_ready.set()
|
||||
|
||||
def open_config_brs(self):
|
||||
@staticmethod
|
||||
def open_config_brs():
|
||||
if open_config:
|
||||
open_config()
|
||||
else:
|
||||
@ -408,6 +432,7 @@ class UserInterface(threading.Thread):
|
||||
class STrayProcess(Process):
|
||||
def __init__(self, r_queue):
|
||||
self.r_queue = r_queue
|
||||
self.icon_stop = None
|
||||
Process.__init__(self)
|
||||
|
||||
def run(self):
|
||||
@ -447,4 +472,4 @@ class STrayProcess(Process):
|
||||
self.r_queue.put(("die", None))
|
||||
|
||||
|
||||
userInterface = UserInterface()
|
||||
user_interface = UserInterface()
|
||||
|
@ -1,5 +1,4 @@
|
||||
import gettext
|
||||
import builtins
|
||||
import locale
|
||||
|
||||
from .conf import settings
|
||||
|
@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import uuid
|
||||
import urllib.parse
|
||||
import os.path
|
||||
import re
|
||||
@ -55,7 +54,7 @@ class Video(object):
|
||||
self.audio_uid[index] = stream["Index"]
|
||||
self.audio_seq[stream["Index"]] = index
|
||||
|
||||
if stream.get("IsExternal") == False:
|
||||
if not stream.get("IsExternal"):
|
||||
index += 1
|
||||
|
||||
index = 1
|
||||
@ -74,7 +73,7 @@ class Video(object):
|
||||
elif sub.get("DeliveryMethod") == "Encode":
|
||||
self.subtitle_enc.add(sub["Index"])
|
||||
|
||||
if sub.get("IsExternal") == False:
|
||||
if not sub.get("IsExternal"):
|
||||
index += 1
|
||||
|
||||
user_aid = self.media_source.get("DefaultAudioStreamIndex")
|
||||
@ -140,7 +139,7 @@ class Video(object):
|
||||
self.client.config.data["app.device_id"]
|
||||
)
|
||||
|
||||
def _get_url_from_source(self, source):
|
||||
def _get_url_from_source(self):
|
||||
# Only use Direct Paths if:
|
||||
# - The media source supports direct paths.
|
||||
# - Direct paths are enabled in the config.
|
||||
@ -216,11 +215,9 @@ class Video(object):
|
||||
log.warning("Preferred media source is unplayable.")
|
||||
return selected
|
||||
|
||||
def get_playback_url(
|
||||
self, offset=0, video_bitrate=None, force_transcode=False, force_bitrate=False
|
||||
):
|
||||
def get_playback_url(self, video_bitrate=None, force_transcode=False):
|
||||
"""
|
||||
Returns the URL to use for the trancoded file.
|
||||
Returns the URL to use for the transcoded file.
|
||||
"""
|
||||
self.terminate_transcode()
|
||||
|
||||
@ -239,7 +236,7 @@ class Video(object):
|
||||
|
||||
self.media_source = self.get_best_media_source(self.srcid)
|
||||
self.map_streams()
|
||||
url = self._get_url_from_source(self.media_source)
|
||||
url = self._get_url_from_source()
|
||||
|
||||
# If there are more media sources and the default one fails, try all of them.
|
||||
if url is None and len(self.playback_info["MediaSources"]) > 1:
|
||||
@ -248,7 +245,7 @@ class Video(object):
|
||||
if media_source["Id"] != self.srcid:
|
||||
self.media_source = media_source
|
||||
self.map_streams()
|
||||
url = self._get_url_from_source(self.media_source)
|
||||
url = self._get_url_from_source()
|
||||
if url is not None:
|
||||
break
|
||||
|
||||
|
@ -52,8 +52,8 @@ if "und" in lang_filter:
|
||||
|
||||
|
||||
class OSDMenu(object):
|
||||
def __init__(self, playerManager):
|
||||
self.playerManager = playerManager
|
||||
def __init__(self, player_manager, player):
|
||||
self.playerManager = player_manager
|
||||
|
||||
self.is_menu_shown = False
|
||||
self.menu_title = ""
|
||||
@ -62,21 +62,23 @@ class OSDMenu(object):
|
||||
self.menu_selection = 0
|
||||
self.menu_tmp = None
|
||||
self.mouse_back = False
|
||||
self.original_osd_color = playerManager._player.osd_back_color
|
||||
self.original_osd_size = playerManager._player.osd_font_size
|
||||
(
|
||||
self.original_osd_color,
|
||||
self.original_osd_size,
|
||||
) = player_manager.get_osd_settings()
|
||||
|
||||
self.profile_menu = None
|
||||
self.profile_manager = None
|
||||
if settings.shader_pack_enable:
|
||||
try:
|
||||
self.profile_manager = VideoProfileManager(self, playerManager)
|
||||
self.profile_manager = VideoProfileManager(self, player_manager, player)
|
||||
self.profile_menu = self.profile_manager.menu_action
|
||||
except Exception:
|
||||
log.error("Could not load profile manager.", exc_info=True)
|
||||
|
||||
self.svp_menu = None
|
||||
try:
|
||||
self.svp_menu = SVPManager(self, playerManager)
|
||||
self.svp_menu = SVPManager(self, player_manager)
|
||||
except Exception:
|
||||
log.error("Could not load SVP integration.", exc_info=True)
|
||||
|
||||
@ -101,7 +103,7 @@ class OSDMenu(object):
|
||||
fmt = "\n **{0}**"
|
||||
menu_text += fmt.format(item[0])
|
||||
|
||||
self.playerManager._player.show_text(menu_text, 2 ** 30, 1)
|
||||
self.playerManager.show_text(menu_text, 2 ** 30, 1)
|
||||
|
||||
def mouse_select(self, idx):
|
||||
if idx < 0 or idx > len(self.menu_list):
|
||||
@ -115,20 +117,16 @@ class OSDMenu(object):
|
||||
|
||||
def show_menu(self):
|
||||
self.is_menu_shown = True
|
||||
player = self.playerManager._player
|
||||
player.osd_back_color = "#CC333333"
|
||||
player.osd_font_size = 40
|
||||
self.playerManager.set_osd_settings("#CC333333", 40)
|
||||
|
||||
if hasattr(player, "osc"):
|
||||
player.osc = False
|
||||
|
||||
player.command("script-message", "shim-menu-enable", "True")
|
||||
self.playerManager.enable_osc(False)
|
||||
self.playerManager.capture_mouse(True)
|
||||
|
||||
self.menu_title = _("Main Menu")
|
||||
self.menu_selection = 0
|
||||
self.mouse_back = False
|
||||
|
||||
if self.playerManager._video and not player.playback_abort:
|
||||
if self.playerManager.is_playing():
|
||||
self.menu_list = [
|
||||
(_("Change Audio"), self.change_audio_menu),
|
||||
(_("Change Subtitles"), self.change_subtitle_menu),
|
||||
@ -149,7 +147,7 @@ class OSDMenu(object):
|
||||
self.menu_list.append(
|
||||
(_("Change Video Playback Profile"), self.profile_menu)
|
||||
)
|
||||
if self.playerManager._video.parent.is_tv:
|
||||
if self.playerManager.get_video().parent.is_tv:
|
||||
self.menu_list.append(
|
||||
(
|
||||
_("Auto Set Audio/Subtitles (Entire Series)"),
|
||||
@ -182,40 +180,31 @@ class OSDMenu(object):
|
||||
# Wait until the menu renders to pause.
|
||||
time.sleep(0.2)
|
||||
|
||||
if player.playback_abort:
|
||||
player.force_window = True
|
||||
player.keep_open = True
|
||||
player.play("")
|
||||
if settings.fullscreen:
|
||||
player.fs = True
|
||||
if self.playerManager.playback_is_aborted():
|
||||
self.playerManager.force_window(True)
|
||||
else:
|
||||
if not self.playerManager.syncplay.is_enabled():
|
||||
player.pause = True
|
||||
self.playerManager.set_paused(True)
|
||||
|
||||
def hide_menu(self):
|
||||
player = self.playerManager._player
|
||||
if self.is_menu_shown:
|
||||
player.osd_back_color = self.original_osd_color
|
||||
player.osd_font_size = self.original_osd_size
|
||||
player.show_text("", 0, 0)
|
||||
player.force_window = False
|
||||
player.keep_open = False
|
||||
self.playerManager.set_osd_settings(
|
||||
self.original_osd_color, self.original_osd_size
|
||||
)
|
||||
self.playerManager.show_text("", 0, 0)
|
||||
|
||||
if hasattr(player, "osc"):
|
||||
player.osc = settings.enable_osc
|
||||
self.playerManager.enable_osc(True)
|
||||
self.playerManager.capture_mouse(False)
|
||||
self.playerManager.force_window(False)
|
||||
|
||||
player.command("script-message", "shim-menu-enable", "False")
|
||||
|
||||
if player.playback_abort:
|
||||
player.play("")
|
||||
else:
|
||||
if not self.playerManager.playback_is_aborted():
|
||||
if not self.playerManager.syncplay.is_enabled():
|
||||
player.pause = False
|
||||
self.playerManager.set_paused(False)
|
||||
|
||||
self.is_menu_shown = False
|
||||
|
||||
def screenshot(self):
|
||||
self.playerManager._player.show_text("", 0, 0)
|
||||
self.playerManager.show_text("", 0, 0)
|
||||
time.sleep(0.5)
|
||||
self.playerManager.screenshot()
|
||||
self.hide_menu()
|
||||
@ -266,10 +255,10 @@ class OSDMenu(object):
|
||||
def change_audio_menu(self):
|
||||
self.put_menu(_("Select Audio Track"))
|
||||
|
||||
selected_aid = self.playerManager._video.aid
|
||||
selected_aid = self.playerManager.get_video().aid
|
||||
audio_streams = [
|
||||
s
|
||||
for s in self.playerManager._video.media_source["MediaStreams"]
|
||||
for s in self.playerManager.get_video().media_source["MediaStreams"]
|
||||
if s.get("Type") == "Audio"
|
||||
]
|
||||
for i, audio_track in enumerate(audio_streams):
|
||||
@ -303,10 +292,10 @@ class OSDMenu(object):
|
||||
def change_subtitle_menu(self):
|
||||
self.put_menu(_("Select Subtitle Track"))
|
||||
|
||||
selected_sid = self.playerManager._video.sid
|
||||
selected_sid = self.playerManager.get_video().sid
|
||||
subtitle_streams = [
|
||||
s
|
||||
for s in self.playerManager._video.media_source["MediaStreams"]
|
||||
for s in self.playerManager.get_video().media_source["MediaStreams"]
|
||||
if s.get("Type") == "Subtitle"
|
||||
]
|
||||
self.menu_list.append([_("None"), self.change_subtitle_menu_handle, -1])
|
||||
@ -335,11 +324,11 @@ class OSDMenu(object):
|
||||
def change_transcode_quality_handle(self):
|
||||
bitrate = self.menu_list[self.menu_selection][2]
|
||||
if bitrate == "none":
|
||||
self.playerManager._video.set_trs_override(None, False)
|
||||
self.playerManager.get_video().set_trs_override(None, False)
|
||||
elif bitrate == "max":
|
||||
self.playerManager._video.set_trs_override(None, True)
|
||||
self.playerManager.get_video().set_trs_override(None, True)
|
||||
else:
|
||||
self.playerManager._video.set_trs_override(bitrate, True)
|
||||
self.playerManager.get_video().set_trs_override(bitrate, True)
|
||||
|
||||
self.menu_action("back")
|
||||
self.playerManager.put_task(self.playerManager.restart_playback)
|
||||
@ -356,7 +345,7 @@ class OSDMenu(object):
|
||||
self.menu_list.append((item[0], handle, item[1]))
|
||||
|
||||
self.menu_selection = 7
|
||||
cur_bitrate = self.playerManager._video.get_transcode_bitrate()
|
||||
cur_bitrate = self.playerManager.get_video().get_transcode_bitrate()
|
||||
for i, option in enumerate(self.menu_list):
|
||||
if cur_bitrate == option[2]:
|
||||
self.menu_selection = i
|
||||
@ -433,7 +422,8 @@ class OSDMenu(object):
|
||||
if settings.remote_kbps == item[1]:
|
||||
self.menu_selection = i
|
||||
|
||||
def get_subtitle_color(self, color):
|
||||
@staticmethod
|
||||
def get_subtitle_color(color):
|
||||
if color in HEX_TO_COLOR:
|
||||
return HEX_TO_COLOR[color]
|
||||
else:
|
||||
@ -450,7 +440,7 @@ class OSDMenu(object):
|
||||
self.menu_action("back")
|
||||
self.video_preferences_menu()
|
||||
|
||||
if self.playerManager._video.is_transcode:
|
||||
if self.playerManager.get_video().is_transcode:
|
||||
if setting_name == "subtitle_size":
|
||||
self.playerManager.put_task(self.playerManager.update_subtitle_visuals)
|
||||
else:
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
import multiprocessing
|
||||
from threading import Event
|
||||
|
||||
@ -33,23 +32,24 @@ def main(desktop=False, cef=False):
|
||||
if sys.platform.startswith("darwin"):
|
||||
multiprocessing.set_start_method("forkserver")
|
||||
|
||||
userInterface = None
|
||||
user_interface = None
|
||||
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
|
||||
|
||||
userInterface = WebviewClient(cef=cef)
|
||||
get_webview = userInterface.get_webview
|
||||
user_interface = WebviewClient(cef=cef)
|
||||
get_webview = user_interface.get_webview
|
||||
elif settings.enable_gui:
|
||||
try:
|
||||
from .gui_mgr import userInterface
|
||||
from .gui_mgr import user_interface
|
||||
|
||||
use_gui = True
|
||||
gui_ready = Event()
|
||||
userInterface.gui_ready = gui_ready
|
||||
user_interface.gui_ready = gui_ready
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Cannot load GUI. Falling back to command line interface.",
|
||||
@ -62,10 +62,11 @@ def main(desktop=False, cef=False):
|
||||
|
||||
get_webview = mirror.get_webview
|
||||
except ImportError:
|
||||
mirror = None
|
||||
log.warning("Cannot load display mirror.", exc_info=True)
|
||||
|
||||
if not userInterface:
|
||||
from .cli_mgr import userInterface
|
||||
if not user_interface:
|
||||
from .cli_mgr import user_interface
|
||||
|
||||
from .player import playerManager
|
||||
from .action_thread import actionThread
|
||||
@ -78,23 +79,23 @@ def main(desktop=False, cef=False):
|
||||
actionThread.start()
|
||||
playerManager.action_trigger = actionThread.trigger
|
||||
playerManager.get_webview = get_webview
|
||||
userInterface.open_player_menu = playerManager.menu.show_menu
|
||||
user_interface.open_player_menu = playerManager.menu.show_menu
|
||||
eventHandler.mirror = mirror
|
||||
userInterface.start()
|
||||
userInterface.login_servers()
|
||||
user_interface.start()
|
||||
user_interface.login_servers()
|
||||
|
||||
try:
|
||||
if use_webview:
|
||||
userInterface.run()
|
||||
user_interface.run()
|
||||
elif mirror:
|
||||
userInterface.stop_callback = mirror.stop
|
||||
user_interface.stop_callback = mirror.stop
|
||||
# If the webview runs before the systray icon, it fails.
|
||||
if use_gui:
|
||||
gui_ready.wait()
|
||||
mirror.run()
|
||||
else:
|
||||
halt = Event()
|
||||
userInterface.stop_callback = halt.set
|
||||
user_interface.stop_callback = halt.set
|
||||
try:
|
||||
halt.wait()
|
||||
except KeyboardInterrupt:
|
||||
@ -105,7 +106,7 @@ def main(desktop=False, cef=False):
|
||||
timelineManager.stop()
|
||||
actionThread.stop()
|
||||
clientManager.stop()
|
||||
userInterface.stop()
|
||||
user_interface.stop()
|
||||
|
||||
|
||||
def main_desktop(cef=False):
|
||||
|
@ -1,7 +1,6 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import urllib.parse
|
||||
import time
|
||||
|
||||
from threading import RLock, Lock, Event
|
||||
@ -34,6 +33,7 @@ python_mpv_available = True
|
||||
is_using_ext_mpv = False
|
||||
if not settings.mpv_ext:
|
||||
try:
|
||||
# noinspection PyPackageRequirements
|
||||
import mpv
|
||||
|
||||
log.info("Using libmpv1 playback backend.")
|
||||
@ -106,6 +106,7 @@ if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"):
|
||||
# the event thread, which would cause deadlock if they run there.
|
||||
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
class PlayerManager(object):
|
||||
"""
|
||||
The underlying player is thread safe, however, locks are used in this
|
||||
@ -161,7 +162,7 @@ class PlayerManager(object):
|
||||
loglevel=settings.mpv_log_level,
|
||||
**mpv_options
|
||||
)
|
||||
self.menu = OSDMenu(self)
|
||||
self.menu = OSDMenu(self, self._player)
|
||||
self.syncplay = SyncPlayManager(self)
|
||||
|
||||
if hasattr(self._player, "osc"):
|
||||
@ -307,13 +308,13 @@ class PlayerManager(object):
|
||||
|
||||
# Fires at the end.
|
||||
@self._player.property_observer("playback-abort")
|
||||
def handle_end_idle(name, value):
|
||||
def handle_end_idle(_name, value):
|
||||
if self._video and value:
|
||||
has_lock = self._finished_lock.acquire(False)
|
||||
self.put_task(self.finished_callback, has_lock)
|
||||
|
||||
@self._player.property_observer("seeking")
|
||||
def handle_seeking(name, value):
|
||||
def handle_seeking(_name, value):
|
||||
if self.syncplay.is_enabled():
|
||||
play_time = self._player.playback_time
|
||||
if (
|
||||
@ -404,6 +405,7 @@ class PlayerManager(object):
|
||||
if self.get_webview() is not None and (
|
||||
settings.display_mirroring or settings.desktop_fullscreen
|
||||
):
|
||||
# noinspection PyUnresolvedReferences
|
||||
self.get_webview().hide()
|
||||
|
||||
self._player.play(self.url)
|
||||
@ -457,7 +459,8 @@ class PlayerManager(object):
|
||||
1,
|
||||
)
|
||||
|
||||
def exec_stop_cmd(self):
|
||||
@staticmethod
|
||||
def exec_stop_cmd():
|
||||
if settings.stop_cmd:
|
||||
os.system(settings.stop_cmd)
|
||||
|
||||
@ -678,6 +681,11 @@ class PlayerManager(object):
|
||||
self._player.fs = not self._player.fs
|
||||
self.fullscreen_disable = not self._player.fs
|
||||
|
||||
@synchronous("_lock")
|
||||
def set_fullscreen(self, enabled):
|
||||
self._player.fs = enabled
|
||||
self.fullscreen_disable = not enabled
|
||||
|
||||
@synchronous("_lock")
|
||||
def set_mute(self, mute):
|
||||
self._player.mute = mute
|
||||
@ -825,5 +833,68 @@ class PlayerManager(object):
|
||||
seek_right = custom_prefs.get("skipForwardLength") or 30000
|
||||
return -int(seek_left) / 1000, int(seek_right) / 1000
|
||||
|
||||
# Wrappers to avoid private access
|
||||
def is_active(self):
|
||||
return bool(self._player and self._video)
|
||||
|
||||
def is_playing(self):
|
||||
return bool(self._video and not self._player.playback_abort)
|
||||
|
||||
def has_video(self):
|
||||
return self._video is not None
|
||||
|
||||
def get_video(self):
|
||||
return self._video
|
||||
|
||||
def show_text(self, text, duration, level=1):
|
||||
self._player.show_text(text, duration, level)
|
||||
|
||||
def get_osd_settings(self):
|
||||
return self._player.osd_back_color, self._player.osd_font_size
|
||||
|
||||
def set_osd_settings(self, back_color, font_size):
|
||||
self._player.osd_back_color = back_color
|
||||
self._player.osd_font_size = font_size
|
||||
|
||||
def enable_osc(self, enabled):
|
||||
if hasattr(self._player, "osc"):
|
||||
self._player.osc = enabled
|
||||
|
||||
def capture_mouse(self, enabled):
|
||||
self._player.command(
|
||||
"script-message", "shim-menu-enable", "True" if enabled else "False"
|
||||
)
|
||||
|
||||
def playback_is_aborted(self):
|
||||
return self._player.playback_abort
|
||||
|
||||
def force_window(self, enabled):
|
||||
if enabled:
|
||||
self._player.force_window = True
|
||||
self._player.keep_open = True
|
||||
self._player.play("")
|
||||
if settings.fullscreen:
|
||||
self._player.fs = True
|
||||
else:
|
||||
self._player.force_window = False
|
||||
self._player.keep_open = False
|
||||
if self._player.playback_abort:
|
||||
self._player.play("")
|
||||
|
||||
def add_ipc(self, ipc_name):
|
||||
self._player.input_ipc_server = ipc_name
|
||||
|
||||
def get_current_client(self):
|
||||
return self._video.client
|
||||
|
||||
def get_time(self):
|
||||
return self._player.playback_time
|
||||
|
||||
def get_speed(self):
|
||||
return self._player.speed
|
||||
|
||||
def set_speed(self, speed):
|
||||
self._player.speed = speed
|
||||
|
||||
|
||||
playerManager = PlayerManager()
|
||||
|
@ -14,7 +14,7 @@ def list_request(path):
|
||||
try:
|
||||
response = urllib.request.urlopen(settings.svp_url + "?" + path)
|
||||
return response.read().decode("utf-8").replace("\r\n", "\n").split("\n")
|
||||
except urllib.error.URLError as ex:
|
||||
except urllib.error.URLError:
|
||||
log.error("Could not reach SVP API server.", exc_info=True)
|
||||
return None
|
||||
|
||||
@ -99,7 +99,7 @@ def set_disabled(disabled):
|
||||
|
||||
|
||||
class SVPManager:
|
||||
def __init__(self, menu, playerManager):
|
||||
def __init__(self, menu, player_manager):
|
||||
self.menu = menu
|
||||
|
||||
if settings.svp_enable:
|
||||
@ -113,14 +113,15 @@ class SVPManager:
|
||||
socket = "/tmp/mpvsocket"
|
||||
|
||||
# This actually *adds* another ipc server.
|
||||
playerManager._player.input_ipc_server = socket
|
||||
player_manager.add_ipc(socket)
|
||||
|
||||
if settings.svp_enable and not is_svp_alive():
|
||||
log.error(
|
||||
"SVP is not reachable. Please make sure you have the API enabled."
|
||||
)
|
||||
|
||||
def is_available(self):
|
||||
@staticmethod
|
||||
def is_available():
|
||||
if not settings.svp_enable:
|
||||
return False
|
||||
if not is_svp_alive():
|
||||
|
@ -4,7 +4,6 @@ import os
|
||||
from datetime import datetime, timedelta
|
||||
from .media import Media
|
||||
from .i18n import _
|
||||
from time import sleep
|
||||
|
||||
# This is based on: https://github.com/jellyfin/jellyfin-web/blob/master/src/components/syncPlay/syncPlayManager.js
|
||||
|
||||
@ -53,7 +52,6 @@ def set_timeout(ms: float, callback, *args):
|
||||
class SyncPlayManager:
|
||||
def __init__(self, manager):
|
||||
self.playerManager = manager
|
||||
self.player = manager._player
|
||||
self.menu = manager.menu
|
||||
|
||||
self.sync_enabled = False
|
||||
@ -106,7 +104,7 @@ class SyncPlayManager:
|
||||
self.last_sync_time = current_time
|
||||
play_at_time = self.last_command["When"]
|
||||
|
||||
current_position_ticks = int(self.player.playback_time * seconds_in_ticks)
|
||||
current_position_ticks = int(self.playerManager.get_time() * seconds_in_ticks)
|
||||
server_position_ticks = (
|
||||
self.last_command["PositionTicks"]
|
||||
+ ((current_time - play_at_time) + self.time_offset).total_seconds()
|
||||
@ -132,14 +130,14 @@ class SyncPlayManager:
|
||||
# Speed To Sync Method
|
||||
speed = 1 + diff_ms / settings.sync_speed_time
|
||||
|
||||
self.player.speed = speed
|
||||
self.playerManager.set_speed(speed)
|
||||
self.sync_enabled = False
|
||||
self.attempts += 1
|
||||
log.info("SyncPlay Speed to Sync rate: {0}".format(speed))
|
||||
self.player_message(_("SpeedToSync (x{0})").format(speed))
|
||||
|
||||
def callback():
|
||||
self.player.speed = 1
|
||||
self.playerManager.set_speed(1)
|
||||
self.sync_enabled = True
|
||||
|
||||
set_timeout(settings.sync_speed_time, callback)
|
||||
@ -187,7 +185,7 @@ class SyncPlayManager:
|
||||
log.error("Syncplay ping reporting failed.", exc_info=True)
|
||||
|
||||
def enable_sync_play(self, start_time, from_server):
|
||||
self.playback_rate = self.player.speed
|
||||
self.playback_rate = self.playerManager.get_speed()
|
||||
self.enabled_at = start_time
|
||||
self.enable_speed_sync = True
|
||||
|
||||
@ -200,7 +198,7 @@ class SyncPlayManager:
|
||||
self.ready = False
|
||||
self.notify_sync_ready = True
|
||||
|
||||
self.client = self.playerManager._video.client
|
||||
self.client = self.playerManager.get_current_client()
|
||||
timesync = self.client.timesync
|
||||
if self.timesync is not None and timesync is not self.timesync:
|
||||
self.timesync.remove_subscriber(self.on_timesync_update)
|
||||
@ -215,7 +213,7 @@ class SyncPlayManager:
|
||||
self.player_message(_("SyncPlay enabled."))
|
||||
|
||||
def disable_sync_play(self, from_server):
|
||||
self.player.speed = self.playback_rate
|
||||
self.playerManager.set_speed(self.playback_rate)
|
||||
|
||||
self.enabled_at = None
|
||||
self.ready = False
|
||||
@ -332,7 +330,7 @@ class SyncPlayManager:
|
||||
|
||||
def prepare_session(self, group_id, session_data):
|
||||
play_command = session_data.get("PlayCommand")
|
||||
if not self.playerManager._video:
|
||||
if not self.playerManager.has_video():
|
||||
play_command = "PlayNow"
|
||||
|
||||
seq = session_data.get("StartIndex")
|
||||
@ -349,12 +347,12 @@ class SyncPlayManager:
|
||||
)
|
||||
|
||||
if (
|
||||
self.playerManager._video
|
||||
and self.playerManager._video.item_id == session_data["ItemIds"][0]
|
||||
self.playerManager.has_video()
|
||||
and self.playerManager.get_video().item_id == session_data["ItemIds"][0]
|
||||
and play_command == "PlayNow"
|
||||
):
|
||||
# We assume the video is already available.
|
||||
self.playerManager._video.parent = media
|
||||
self.playerManager.get_video().parent = media
|
||||
log.info("Syncplay Session Prepare: {0} {1}".format(group_id, session_data))
|
||||
self.local_seek(
|
||||
(session_data.get("PositionTicks", 0) or 0) / seconds_in_ticks
|
||||
@ -378,12 +376,12 @@ class SyncPlayManager:
|
||||
self.join_group(group_id)
|
||||
self.playerManager.timeline_handle()
|
||||
elif play_command == "PlayLast":
|
||||
self.playerManager._video.parent.insert_items(
|
||||
self.playerManager.get_video().parent.insert_items(
|
||||
session_data.get("ItemIds"), append=True
|
||||
)
|
||||
self.playerManager.upd_player_hide()
|
||||
elif play_command == "PlayNext":
|
||||
self.playerManager._video.parent.insert_items(
|
||||
self.playerManager.get_video().parent.insert_items(
|
||||
session_data.get("ItemIds"), append=False
|
||||
)
|
||||
self.playerManager.upd_player_hide()
|
||||
@ -392,7 +390,7 @@ class SyncPlayManager:
|
||||
# Messages overwrite menu, so they are ignored.
|
||||
if not self.menu.is_menu_shown:
|
||||
if settings.sync_osd_message:
|
||||
self.player.show_text(message)
|
||||
self.playerManager.show_text(message)
|
||||
else:
|
||||
log.info("SyncPlay Message: {0}".format(message))
|
||||
else:
|
||||
@ -466,7 +464,7 @@ class SyncPlayManager:
|
||||
self.sync_timeout()
|
||||
|
||||
self.sync_enabled = False
|
||||
self.player.speed = 1
|
||||
self.playerManager.set_speed(1)
|
||||
|
||||
def play_request(self):
|
||||
self.client.jellyfin.play_sync_play()
|
||||
@ -506,7 +504,7 @@ class SyncPlayManager:
|
||||
self.client.jellyfin.new_sync_play()
|
||||
|
||||
def menu_action(self):
|
||||
self.client = self.playerManager._video.client
|
||||
self.client = self.playerManager.get_current_client()
|
||||
|
||||
selected = 0
|
||||
offset = 1
|
||||
@ -516,7 +514,9 @@ class SyncPlayManager:
|
||||
if not self.is_enabled():
|
||||
offset = 2
|
||||
group_option_list.append((_("New Group"), self.menu_create_group, None))
|
||||
groups = self.client.jellyfin.get_sync_play(self.playerManager._video.item_id)
|
||||
groups = self.client.jellyfin.get_sync_play(
|
||||
self.playerManager.get_video().item_id
|
||||
)
|
||||
for i, group in enumerate(groups):
|
||||
group_option_list.append(
|
||||
(group["PlayingItemName"], self.menu_join_group, group)
|
||||
|
@ -1,13 +1,12 @@
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import os
|
||||
|
||||
import jellyfin_apiclient_python.exceptions
|
||||
|
||||
from .conf import settings
|
||||
from .player import playerManager
|
||||
from .utils import Timer, mpv_color_to_plex
|
||||
from .utils import Timer
|
||||
|
||||
log = logging.getLogger("timeline")
|
||||
|
||||
@ -27,19 +26,16 @@ class TimelineManager(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
while not self.halt:
|
||||
if (
|
||||
playerManager._player
|
||||
and playerManager._video
|
||||
and (not settings.idle_when_paused or not playerManager.is_paused())
|
||||
if playerManager.is_active() and (
|
||||
not settings.idle_when_paused or not playerManager.is_paused()
|
||||
):
|
||||
self.SendTimeline()
|
||||
self.send_timeline()
|
||||
self.delay_idle()
|
||||
force_next = False
|
||||
if self.idleTimer.elapsed() > settings.idle_cmd_delay and not self.is_idle:
|
||||
if (
|
||||
settings.idle_when_paused
|
||||
and settings.stop_idle
|
||||
and playerManager._video
|
||||
and playerManager.has_video()
|
||||
):
|
||||
playerManager.stop()
|
||||
if settings.idle_cmd:
|
||||
@ -52,7 +48,8 @@ class TimelineManager(threading.Thread):
|
||||
self.idleTimer.restart()
|
||||
self.is_idle = False
|
||||
|
||||
def SendTimeline(self):
|
||||
@staticmethod
|
||||
def send_timeline():
|
||||
try:
|
||||
# Send_timeline sometimes (once every couple hours) gets a 404 response from Jellyfin.
|
||||
# Without this try/except that would cause this entire thread to crash keeping it from self-healing.
|
||||
|
@ -14,8 +14,8 @@ one_day = 86400
|
||||
|
||||
|
||||
class UpdateChecker:
|
||||
def __init__(self, playerManager):
|
||||
self.playerManager = playerManager
|
||||
def __init__(self, player_manager):
|
||||
self.playerManager = player_manager
|
||||
self.has_notified = False
|
||||
self.new_version = None
|
||||
self.last_check = None
|
||||
@ -49,7 +49,7 @@ class UpdateChecker:
|
||||
if not self.has_notified and settings.notify_updates:
|
||||
self.has_notified = True
|
||||
log.info("Update Available: {0}".format(self.new_version))
|
||||
self.playerManager._player.show_text(
|
||||
self.playerManager.show_text(
|
||||
_(
|
||||
"MPV Shim v{0} Update Available\nOpen menu (press c) for details."
|
||||
).format(self.new_version),
|
||||
@ -58,7 +58,7 @@ class UpdateChecker:
|
||||
)
|
||||
|
||||
def open(self):
|
||||
self.playerManager._player.command("set", "fullscreen", "no")
|
||||
self.playerManager.set_fullscreen(False)
|
||||
try:
|
||||
webbrowser.open(release_url + "latest")
|
||||
except Exception:
|
||||
|
@ -21,12 +21,12 @@ seq_num_lock = Lock()
|
||||
|
||||
class Timer(object):
|
||||
def __init__(self):
|
||||
self.restart()
|
||||
self.started = datetime.now()
|
||||
|
||||
def restart(self):
|
||||
self.started = datetime.now()
|
||||
|
||||
def elapsedMs(self):
|
||||
def elapsed_ms(self):
|
||||
return self.elapsed() * 1e3
|
||||
|
||||
def elapsed(self):
|
||||
@ -222,7 +222,7 @@ def get_resource(*path):
|
||||
# Detect if bundled via pyinstaller.
|
||||
# From: https://stackoverflow.com/questions/404744/
|
||||
if getattr(sys, "_MEIPASS", False):
|
||||
application_path = os.path.join(sys._MEIPASS, "jellyfin_mpv_shim")
|
||||
application_path = os.path.join(getattr(sys, "_MEIPASS"), "jellyfin_mpv_shim")
|
||||
else:
|
||||
application_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
@ -7,7 +7,6 @@ import logging
|
||||
import os.path
|
||||
import shutil
|
||||
import json
|
||||
import time
|
||||
|
||||
profile_name_translation = {
|
||||
"Generic (FSRCNNX)": _("Generic (FSRCNNX)"),
|
||||
@ -34,17 +33,16 @@ class MPVSettingError(Exception):
|
||||
|
||||
|
||||
class VideoProfileManager:
|
||||
def __init__(self, menu, playerManager):
|
||||
def __init__(self, menu, player_manager, player):
|
||||
self.menu = menu
|
||||
self.playerManager = playerManager
|
||||
self.playerManager = player_manager
|
||||
self.used_settings = set()
|
||||
self.current_profile = None
|
||||
self.player = player
|
||||
|
||||
self.load_shader_pack()
|
||||
|
||||
def load_shader_pack(self):
|
||||
shader_pack_builtin = get_resource("default_shader_pack")
|
||||
|
||||
# Load shader pack
|
||||
self.shader_pack = shader_pack_builtin
|
||||
if settings.shader_pack_custom:
|
||||
self.shader_pack = conffile.get(APP_NAME, "shader_pack")
|
||||
@ -71,7 +69,7 @@ class VideoProfileManager:
|
||||
if key in self.defaults or key in self.revert_ignore:
|
||||
continue
|
||||
try:
|
||||
self.defaults[key] = getattr(self.playerManager._player, key)
|
||||
self.defaults[key] = getattr(self.player, key)
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Your MPV does not support setting {0} used in shader pack.".format(
|
||||
@ -123,25 +121,25 @@ class VideoProfileManager:
|
||||
if (key, value) in already_set:
|
||||
continue
|
||||
log.debug("Set MPV setting {0} to {1}".format(key, value))
|
||||
setattr(self.playerManager._player, key, value)
|
||||
setattr(self.player, key, value)
|
||||
already_set.add((key, value))
|
||||
|
||||
# Apply Shaders
|
||||
log.debug("Set shaders: {0}".format(shaders_to_apply))
|
||||
self.playerManager._player.glsl_shaders = shaders_to_apply
|
||||
self.player.glsl_shaders = shaders_to_apply
|
||||
self.current_profile = profile_name
|
||||
return True
|
||||
except MPVSettingError as ex:
|
||||
except MPVSettingError:
|
||||
log.error("Could not apply shader profile.", exc_info=True)
|
||||
return False
|
||||
|
||||
def unload_profile(self):
|
||||
log.info("Unloading shader profile.")
|
||||
self.playerManager._player.glsl_shaders = []
|
||||
self.player.glsl_shaders = []
|
||||
for setting in self.used_settings:
|
||||
value = self.defaults[setting]
|
||||
try:
|
||||
setattr(self.playerManager._player, setting, value)
|
||||
setattr(self.player, setting, value)
|
||||
except Exception:
|
||||
log.warning(
|
||||
"Default setting {0} value {1} is invalid.".format(setting, value)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import threading
|
||||
import urllib.request
|
||||
from datetime import date
|
||||
from werkzeug.serving import make_server
|
||||
from flask import Flask, request, jsonify
|
||||
from time import sleep
|
||||
@ -45,6 +44,7 @@ def do_not_cache(response):
|
||||
class Server(threading.Thread):
|
||||
def __init__(self):
|
||||
self.srv = None
|
||||
self.ctx = None
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
@ -134,8 +134,9 @@ class Server(threading.Thread):
|
||||
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.
|
||||
# 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
|
||||
@ -146,7 +147,8 @@ class WebviewClient(object):
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def login_servers(self):
|
||||
@staticmethod
|
||||
def login_servers():
|
||||
success = clientManager.try_connect()
|
||||
if success:
|
||||
loaded.set()
|
||||
@ -204,7 +206,7 @@ class WebviewClient(object):
|
||||
try:
|
||||
from webview.platforms import cocoa
|
||||
|
||||
def override_cocoa(self, webview, nav):
|
||||
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)
|
||||
|
||||
@ -221,7 +223,8 @@ class WebviewClient(object):
|
||||
try:
|
||||
from webview.platforms import gtk
|
||||
|
||||
def override_gtk(self, webview, status):
|
||||
# noinspection PyUnresolvedReferences
|
||||
def override_gtk(_self, webview, _status):
|
||||
if not webview.props.opacity:
|
||||
gtk.glib.idle_add(webview.set_opacity, 1.0)
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
# noinspection PyUnresolvedReferences,PyPackageRequirements
|
||||
import win32gui
|
||||
import logging
|
||||
|
||||
log = logging.getLogger("win_utils")
|
||||
|
||||
|
||||
def windowEnumerationHandler(hwnd, top_windows):
|
||||
def window_enumeration_handler(hwnd, top_windows):
|
||||
top_windows.append((hwnd, win32gui.GetWindowText(hwnd)))
|
||||
|
||||
|
||||
@ -15,7 +16,7 @@ def raise_mpv():
|
||||
try:
|
||||
top_windows = []
|
||||
fg_win = win32gui.GetForegroundWindow()
|
||||
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
|
||||
win32gui.EnumWindows(window_enumeration_handler, top_windows)
|
||||
for i in top_windows:
|
||||
if " - mpv" in i[1].lower():
|
||||
if i[0] != fg_win:
|
||||
@ -30,7 +31,7 @@ def raise_mpv():
|
||||
def mirror_act(state, name="Jellyfin MPV Shim Mirror"):
|
||||
try:
|
||||
top_windows = []
|
||||
win32gui.EnumWindows(windowEnumerationHandler, top_windows)
|
||||
win32gui.EnumWindows(window_enumeration_handler, top_windows)
|
||||
for i in top_windows:
|
||||
if name in i[1]:
|
||||
print(i)
|
||||
|
@ -9,7 +9,7 @@ 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 = sys._MEIPASS
|
||||
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"]
|
||||
|
@ -9,7 +9,7 @@ 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 = sys._MEIPASS
|
||||
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"]
|
||||
|
2
run.py
2
run.py
@ -9,7 +9,7 @@ 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 = sys._MEIPASS
|
||||
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"]
|
||||
|
Loading…
Reference in New Issue
Block a user