diff --git a/syncplay/constants.py b/syncplay/constants.py index 4762960..883d5a2 100755 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -139,6 +139,8 @@ USER_READY_MIN_VERSION = "1.3.0" SHARED_PLAYLIST_MIN_VERSION = "1.4.0" CHAT_MIN_VERSION = "1.5.0" FEATURE_LIST_MIN_VERSION = "1.5.0" + +IINA_PATHS = ['/Applications/IINA.app/Contents/MacOS/IINA'] MPC_PATHS = [ r"c:\program files (x86)\mpc-hc\mpc-hc.exe", r"c:\program files\mpc-hc\mpc-hc.exe", @@ -176,6 +178,7 @@ VLC_PATHS = [ ] VLC_ICONPATH = "vlc.png" +IINA_ICONPATH = "iina.png" MPLAYER_ICONPATH = "mplayer.png" MPV_ICONPATH = "mpv.png" MPVNET_ICONPATH = "mpvnet.png" @@ -250,6 +253,13 @@ MPV_ARGS = {'force-window': 'yes', 'keep-open-pause': 'yes' } +IINA_PROPERTIES = {'geometry': '25%+100+100', + 'idle': 'yes', + 'hr-seek': 'always', + 'input-terminal': 'no', + 'term-playing-msg': '\nANS_filename=${filename}\nANS_length=${=duration:${=length:0}}\nANS_path=${path}\n', + 'keep-open-pause': 'yes', + } MPV_NEW_VERSION = False MPV_OSC_VISIBILITY_CHANGE_VERSION = False diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index b1daf2e..1d6deed 100755 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -113,7 +113,7 @@ de = { "mpv-version-error": "Syncplay ist nicht kompatibel mit dieser Version von mpv. Bitte benutze eine andere Version (z.B. Git HEAD).", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", # TODO: Translate "player-file-open-error": "Fehler beim Öffnen der Datei durch den Player", - "player-path-error": "Ungültiger Player-Pfad. Unterstützte Player sind: mpv, mpv.net, VLC, MPC-HC, MPC-BE und mplayer2", + "player-path-error": "Ungültiger Player-Pfad. Unterstützte Player sind: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 und IINA", "hostname-empty-error": "Hostname darf nicht leer sein", "empty-error": "{} darf nicht leer sein", # Configuration "media-player-error": "Player-Fehler: \"{}\"", # Error line @@ -124,7 +124,7 @@ de = { "unable-to-start-client-error": "Client kann nicht gestartet werden", - "player-path-config-error": "Player-Pfad ist nicht ordnungsgemäß gesetzt. Unterstützte Player sind: mpv, mpv.net, VLC, MPC-HC, MPC-BE und mplayer2", + "player-path-config-error": "Player-Pfad ist nicht ordnungsgemäß gesetzt. Unterstützte Player sind: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 und IINA", "no-file-path-config-error": "Es muss eine Datei ausgewählt werden, bevor der Player gestartet wird.", "no-hostname-config-error": "Hostname darf nicht leer sein", "invalid-port-config-error": "Port muss gültig sein", @@ -381,7 +381,7 @@ de = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Pfad zum ausgewählten, unterstützten Mediaplayer (mpv, mpv.net, VLC, MPC-HC/BE or mplayer2).", + "executable-path-tooltip": "Pfad zum ausgewählten, unterstützten Mediaplayer (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2, oder IINA).", "media-path-tooltip": "Pfad zum wiederzugebenden Video oder Stream. Notwendig für mplayer2.", "player-arguments-tooltip": "Zusätzliche Kommandozeilenparameter/-schalter für diesen Mediaplayer.", "mediasearcdirectories-arguments-tooltip": "Verzeichnisse, in denen Syncplay nach Mediendateien suchen soll, z.B. wenn du die Click-to-switch-Funktion verwendest. Syncplay wird Unterverzeichnisse rekursiv durchsuchen.", # TODO: Translate Click-to-switch? (or use as name for feature) diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 3cad327..60a26e0 100755 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -113,7 +113,7 @@ en = { "mpv-version-error": "Syncplay is not compatible with this version of mpv. Please use a different version of mpv (e.g. Git HEAD).", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", "player-file-open-error": "Player failed opening file", - "player-path-error": "Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE and mplayer2", + "player-path-error": "Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2, and IINA", "hostname-empty-error": "Hostname can't be empty", "empty-error": "{} can't be empty", # Configuration "media-player-error": "Media player error: \"{}\"", # Error line @@ -124,7 +124,7 @@ en = { "unable-to-start-client-error": "Unable to start client", - "player-path-config-error": "Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE and mplayer2.", + "player-path-config-error": "Player path is not set properly. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2, and IINA.", "no-file-path-config-error": "File must be selected before starting your player", "no-hostname-config-error": "Hostname can't be empty", "invalid-port-config-error": "Port must be valid", @@ -384,7 +384,7 @@ en = { "edit-rooms-tooltip": "Edit room list.", - "executable-path-tooltip": "Location of your chosen supported media player (mpv, mpv.net, VLC, MPC-HC/BE or mplayer2).", + "executable-path-tooltip": "Location of your chosen supported media player (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 or IINA).", "media-path-tooltip": "Location of video or stream to be opened. Necessary for mplayer2.", "player-arguments-tooltip": "Additional command line arguments / switches to pass on to this media player.", "mediasearcdirectories-arguments-tooltip": "Directories where Syncplay will search for media files, e.g. when you are using the click to switch feature. Syncplay will look recursively through sub-folders.", diff --git a/syncplay/messages_es.py b/syncplay/messages_es.py index ba0d04a..3867789 100644 --- a/syncplay/messages_es.py +++ b/syncplay/messages_es.py @@ -113,7 +113,7 @@ es = { "mpv-version-error": "Syncplay no es compatible con esta versión de mpv. Por favor utiliza una versión diferente de mpv (p.ej. Git HEAD).", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", # TODO: Translate "player-file-open-error": "El reproductor falló al abrir el archivo", - "player-path-error": "La ruta del reproductor no está definida correctamente. Los reproductores soportados son: mpv, mpv.net, VLC, MPC-HC, MPC-BE y mplayer2", + "player-path-error": "La ruta del reproductor no está definida correctamente. Los reproductores soportados son: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2, y IINA", "hostname-empty-error": "El nombre del host no puede ser vacío", "empty-error": "{} no puede ser vacío", # Configuration "media-player-error": "Error del reproductor multimedia: \"{}\"", # Error line @@ -124,7 +124,7 @@ es = { "unable-to-start-client-error": "No se logró iniciar el cliente", - "player-path-config-error": "La ruta del reproductor no está definida correctamente. Los reproductores soportados son: mpv, mpv.net, VLC, MPC-HC, MPC-BE y mplayer2.", + "player-path-config-error": "La ruta del reproductor no está definida correctamente. Los reproductores soportados son: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 y IINA.", "no-file-path-config-error": "El archivo debe ser seleccionado antes de iniciar el reproductor", "no-hostname-config-error": "El nombre del host no puede ser vacío", "invalid-port-config-error": "El puerto debe ser válido", @@ -383,7 +383,7 @@ es = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Ubicación de tu reproductor multimedia compatible elegido (mpv, mpv.net, VLC, MPC-HC/BE o mplayer2).", + "executable-path-tooltip": "Ubicación de tu reproductor multimedia compatible elegido (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 o IINA).", "media-path-tooltip": "Ubicación del video o flujo que se abrirá. Necesario para mplayer2.", "player-arguments-tooltip": "Arguementos de línea de comandos adicionales / parámetros para pasar a este reproductor multimedia.", "mediasearcdirectories-arguments-tooltip": "Directorios donde Syncplay buscará archivos de medios, p.ej. cuando estás usando la función \"clic para cambiar\". Syncplay buscará recursivamente a través de las subcarpetas.", diff --git a/syncplay/messages_it.py b/syncplay/messages_it.py index acfe3c4..6a4e9cf 100755 --- a/syncplay/messages_it.py +++ b/syncplay/messages_it.py @@ -113,7 +113,7 @@ it = { "mpv-version-error": "Syncplay non è compatibile con questa versione di mpv. Per favore usa un'altra versione di mpv (es. Git HEAD).", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", # TODO: Translate "player-file-open-error": "Il player non è riuscito ad aprire il file", - "player-path-error": "Il path del player non è configurato correttamente. I player supportati sono: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2", + "player-path-error": "Il path del player non è configurato correttamente. I player supportati sono: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA", "hostname-empty-error": "Il campo hostname non può essere vuoto", "empty-error": "Il campo {} non può esssere vuoto", # Configuration "media-player-error": "Errore media player: \"{}\"", # Error line @@ -124,7 +124,7 @@ it = { "unable-to-start-client-error": "Impossibile avviare il client", - "player-path-config-error": "Il percorso del player non è configurato correttamente. I player supportati sono: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2.", + "player-path-config-error": "Il percorso del player non è configurato correttamente. I player supportati sono: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA.", "no-file-path-config-error": "Deve essere selezionato un file prima di avviare il player", "no-hostname-config-error": "Il campo hostname non può essere vuoto", "invalid-port-config-error": "La porta deve essere valida", @@ -383,7 +383,7 @@ it = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Percorso del media player desiderato (scegliere tra mpv, mpv.net, VLC, MPC-HC/BE or mplayer2).", + "executable-path-tooltip": "Percorso del media player desiderato (scegliere tra mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 o IINA).", "media-path-tooltip": "Percorso del video o stream da aprire. Necessario per mplayer2.", "player-arguments-tooltip": "Argomenti da linea di comando aggiuntivi da passare al media player scelto.", "mediasearcdirectories-arguments-tooltip": "Cartelle dove Syncplay cercherà i file multimediali, es. quando usi la funzione click to switch. Syncplay cercherà anche nelle sottocartelle.", diff --git a/syncplay/messages_pt_BR.py b/syncplay/messages_pt_BR.py index 7155011..45c2d25 100644 --- a/syncplay/messages_pt_BR.py +++ b/syncplay/messages_pt_BR.py @@ -113,7 +113,7 @@ pt_BR = { "mpv-version-error": "O motivo pelo qual o mpv não pode ser iniciado pode ser devido ao uso de argumentos da linha de comando não suportados ou a uma versão não suportada do mpv.", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", # TODO: Translate "player-file-open-error": "O reprodutor falhou ao abrir o arquivo", - "player-path-error": "O caminho até o arquivo executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2", + "player-path-error": "O caminho até o arquivo executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA", "hostname-empty-error": "O endereço do servidor não pode ser vazio", "empty-error": "{} não pode ser vazio", # Configuration "media-player-error": "Erro do reprodutor de mídia: \"{}\"", # Error line @@ -124,7 +124,7 @@ pt_BR = { "unable-to-start-client-error": "Não foi possível iniciar o client", - "player-path-config-error": "O caminho até o arquivo executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2.", + "player-path-config-error": "O caminho até o arquivo executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA.", "no-file-path-config-error": "O arquivo deve ser selecionado antes de iniciar seu reprodutor", "no-hostname-config-error": "O endereço do servidor não pode ser vazio", "invalid-port-config-error": "A porta deve ser válida", @@ -383,7 +383,7 @@ pt_BR = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Localização do seu reprodutor de mídia preferido (mpv, mpv.net, VLC, MPC-HC/BE ou mplayer2).", + "executable-path-tooltip": "Localização do seu reprodutor de mídia preferido (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 ou IINA).", "media-path-tooltip": "Localização do vídeo ou transmissão a ser aberto. Necessário com o mplayer2.", "player-arguments-tooltip": "Argumentos de comando de linha adicionais para serem repassados ao reprodutor de mídia.", "mediasearcdirectories-arguments-tooltip": "Diretório onde o Syncplay vai procurar por arquivos de mídia, por exemplo quando você estiver usando o recurso de clicar para mudar. O Syncplay irá procurar recursivamente pelas subpastas.", diff --git a/syncplay/messages_pt_PT.py b/syncplay/messages_pt_PT.py index 0bf874d..8f9d5e6 100644 --- a/syncplay/messages_pt_PT.py +++ b/syncplay/messages_pt_PT.py @@ -113,7 +113,7 @@ pt_PT = { "mpv-version-error": "O Syncplay não é compatível com esta versão do mpv. Por favor, use uma versão diferente do mpv (por exemplo, Git HEAD).", "mpv-failed-advice": "O motivo pelo qual o mpv não pode ser iniciado pode ser devido ao uso de argumentos da linha de comando não suportados ou a uma versão não suportada do mpv.", # TODO: Translate "player-file-open-error": "O reprodutor falhou ao abrir o ficheiro", - "player-path-error": "O caminho até o ficheiro executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2", + "player-path-error": "O caminho até o ficheiro executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA", "hostname-empty-error": "O endereço do servidor não pode ser vazio", "empty-error": "{} não pode ser vazio", # Configuration "media-player-error": "Erro do reprodutor de mídia: \"{}\"", # Error line @@ -124,7 +124,7 @@ pt_PT = { "unable-to-start-client-error": "Não foi possível iniciar o client", - "player-path-config-error": "O caminho até o ficheiro executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE e mplayer2.", + "player-path-config-error": "O caminho até o ficheiro executável do reprodutor não está configurado corretamente. Os reprodutores suportados são: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 e IINA.", "no-file-path-config-error": "O ficheiro deve ser selecionado antes de iniciar seu reprodutor", "no-hostname-config-error": "O endereço do servidor não pode ser vazio", "invalid-port-config-error": "A porta deve ser válida", @@ -382,7 +382,7 @@ pt_PT = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Localização do seu reprodutor de mídia preferido (mpv, mpv.net, VLC, MPC-HC/BE ou mplayer2).", + "executable-path-tooltip": "Localização do seu reprodutor de mídia preferido (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2 ou IINA).", "media-path-tooltip": "Localização do vídeo ou transmissão a ser aberto. Necessário com o mplayer2.", "player-arguments-tooltip": "Argumentos de comando de linha adicionais para serem repassados ao reprodutor de mídia.", "mediasearcdirectories-arguments-tooltip": "Pasta onde o Syncplay vai procurar por ficheiros de mídia, por exemplo quando você estiver usando o recurso de clicar para mudar. O Syncplay irá procurar recursivamente pelas subpastas.", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 6c48650..3341b24 100755 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -113,7 +113,7 @@ ru = { "mpv-version-error": "Syncplay не совместим с данной версией mpv. Пожалуйста, используйте другую версию mpv (лучше свежайшую).", "mpv-failed-advice": "The reason mpv cannot start may be due to the use of unsupported command line arguments or an unsupported version of mpv.", # TODO: Translate "player-file-open-error": "Проигрыватель не может открыть файл.", - "player-path-error": "Путь к проигрывателю задан неверно. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE and mplayer2.", # TODO: Translate last sentence + "player-path-error": "Путь к проигрывателю задан неверно. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 and IINA.", # TODO: Translate last sentence "hostname-empty-error": "Имя пользователя не может быть пустым.", "empty-error": "{} не может быть пустым.", # Configuration "media-player-error": "Ошибка проигрывателя: \"{}\"", # Error line @@ -124,7 +124,7 @@ ru = { "unable-to-start-client-error": "Невозможно запустить клиент", - "player-path-config-error": "Путь к проигрывателю установлен неверно. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE and mplayer2", # To do: Translate end + "player-path-config-error": "Путь к проигрывателю установлен неверно. Supported players are: mpv, mpv.net, VLC, MPC-HC, MPC-BE, mplayer2 and IINA", # To do: Translate end "no-file-path-config-error": "Файл должен быть указан до включения проигрывателя", "no-hostname-config-error": "Имя сервера не может быть пустым", "invalid-port-config-error": "Неверный номер порта", @@ -386,7 +386,7 @@ ru = { "edit-rooms-tooltip": "Edit room list.", # TO DO: Translate - "executable-path-tooltip": "Расположение Вашего видеопроигрывателя (mpv, mpv.net, VLC, MPC-HC/BE или mplayer2).", + "executable-path-tooltip": "Расположение Вашего видеопроигрывателя (mpv, mpv.net, VLC, MPC-HC/BE, mplayer2, или IINA).", "media-path-tooltip": "Расположение видеофайла или потока для просмотра. Обязательно для mplayer2.", # TODO: Confirm translation "player-arguments-tooltip": "Передавать дополнительные аргументы командной строки этому проигрывателю.", "mediasearcdirectories-arguments-tooltip": "Папки, где Syncplay будет искать медиа файлы, включая подпапки.", diff --git a/syncplay/players/__init__.py b/syncplay/players/__init__.py index 8c812db..90fa73b 100755 --- a/syncplay/players/__init__.py +++ b/syncplay/players/__init__.py @@ -12,7 +12,12 @@ try: except ImportError: from syncplay.players.basePlayer import DummyPlayer MpcBePlayer = DummyPlayer +try: + from syncplay.players.iina import IinaPlayer +except ImportError: + from syncplay.players.basePlayer import DummyPlayer + IinaPlayer = DummyPlayer def getAvailablePlayers(): - return [MPCHCAPIPlayer, MpvPlayer, MpvnetPlayer, VlcPlayer, MpcBePlayer, MplayerPlayer] + return [MPCHCAPIPlayer, MpvPlayer, MpvnetPlayer, VlcPlayer, MpcBePlayer, MplayerPlayer, IinaPlayer] diff --git a/syncplay/players/iina.py b/syncplay/players/iina.py new file mode 100644 index 0000000..12ee2c3 --- /dev/null +++ b/syncplay/players/iina.py @@ -0,0 +1,88 @@ +import os +from syncplay import constants +from syncplay.utils import findResourcePath +from syncplay.players.mpv import MpvPlayer +from syncplay.players.ipc_iina import IINA + +class IinaPlayer(MpvPlayer): + + @staticmethod + def run(client, playerPath, filePath, args): + constants.MPV_NEW_VERSION = True + constants.MPV_OSC_VISIBILITY_CHANGE_VERSION = True + return IinaPlayer(client, IinaPlayer.getExpandedPath(playerPath), filePath, args) + + @staticmethod + def getStartupArgs(userArgs): + args = {} + if userArgs: + for argToAdd in userArgs: + if argToAdd.startswith('--'): + argToAdd = argToAdd[2:] + elif argToAdd.startswith('-'): + argToAdd = argToAdd[1:] + if argToAdd.strip() == "": + continue + if "=" in argToAdd: + (argName, argValue) = argToAdd.split("=", 1) + else: + argName = argToAdd + argValue = "yes" + args[argName] = argValue + return args + + @staticmethod + def getDefaultPlayerPathsList(): + l = [] + for path in constants.IINA_PATHS: + p = IinaPlayer.getExpandedPath(path) + if p: + l.append(p) + return l + + @staticmethod + def isValidPlayerPath(path): + if "iina-cli" in path or "iina-cli" in IinaPlayer.getExpandedPath(path): + return True + return False + + @staticmethod + def getExpandedPath(playerPath): + if "iina-cli" in playerPath: + pass + elif "IINA.app/Contents/MacOS/IINA" in playerPath: + playerPath = os.path.join(os.path.dirname(playerPath), "iina-cli") + + if os.access(playerPath, os.X_OK): + return playerPath + for path in os.environ['PATH'].split(':'): + path = os.path.join(os.path.realpath(path), playerPath) + if os.access(path, os.X_OK): + return path + return playerPath + + @staticmethod + def getIconPath(path): + return constants.IINA_ICONPATH + + def __init__(self, client, playerPath, filePath, args): + from twisted.internet import reactor + self.reactor = reactor + self._client = client + self._set_defaults() + + self._playerIPCHandler = IINA + self._create_listener(playerPath, filePath, args) + + def _preparePlayer(self): + for key, value in constants.IINA_PROPERTIES.items(): + self._setProperty(key, value) + self._listener.sendLine(["load-script", findResourcePath("syncplayintf.lua")]) + super()._preparePlayer() + + def _onFileUpdate(self): + # do not show file info for our placeholder image in Syncplay UI + if self._filename == "iina-bkg.png": + return + else: + super()._onFileUpdate() diff --git a/syncplay/players/ipc_iina.py b/syncplay/players/ipc_iina.py new file mode 100755 index 0000000..a91e8ff --- /dev/null +++ b/syncplay/players/ipc_iina.py @@ -0,0 +1,56 @@ +import os.path +import subprocess +import time + +from syncplay.players.python_mpv_jsonipc.python_mpv_jsonipc import log, MPV, MPVError, MPVProcess +from syncplay.utils import resourcespath + +class IINA(MPV): + """The main IINA interface class. Use this to control the MPV player instantiated by IINA.""" + + def _start_mpv(self, ipc_socket, mpv_location, **kwargs): + # Attempt to start IINA 3 times. + for i in range(3): + try: + self.mpv_process = IINAProcess(ipc_socket, mpv_location, **kwargs) + break + except MPVError: + log.warning("IINA start failed.", exc_info=1) + continue + else: + raise MPVError("IINA process retry limit reached.") + +class IINAProcess(MPVProcess): + """ + Manages an IINA process, ensuring the socket or pipe is available. (Internal) + """ + + def _start_process(self, ipc_socket, args, env): + self.process = subprocess.Popen(args, env=env) + ipc_exists = False + for _ in range(100): # Give IINA 10 seconds to start. + time.sleep(0.1) + self.process.poll() + if os.path.exists(ipc_socket): + ipc_exists = True + log.debug("Found IINA socket.") + break + if self.process.returncode != 0: # iina-cli returns immediately after its start + log.error("IINA failed with returncode {0}.".format(self.process.returncode)) + break + else: + self.process.terminate() + raise MPVError("IINA start timed out.") + + if not ipc_exists or self.process.returncode != 0: + self.process.terminate() + raise MPVError("IINA not started.") + + def _get_arglist(self, exec_location, **kwargs): + args = [exec_location] + args.append('--no-stdin') + args.append(resourcespath + 'iina-bkg.png') + self._set_default(kwargs, "mpv-input-ipc-server", self.ipc_socket) + args.extend("--{0}={1}".format(v[0].replace("_", "-"), self._mpv_fmt(v[1])) + for v in kwargs.items()) + return args diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index 3971f83..cebc63d 100755 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -504,6 +504,12 @@ class MpvPlayer(BasePlayer): from twisted.internet import reactor self.reactor = reactor self._client = client + self._set_defaults() + + self._playerIPCHandler = MPV + self._create_listener(playerPath, filePath, args) + + def _set_defaults(self): self._paused = None self._position = 0.0 self._duration = None @@ -513,8 +519,10 @@ class MpvPlayer(BasePlayer): self.lastLoadedTime = None self.fileLoaded = False self.delayedFilePath = None + + def _create_listener(self, playerPath, filePath, args): try: - self._listener = self.__Listener(self, playerPath, filePath, args) + self._listener = self.__Listener(self, self._playerIPCHandler, playerPath, filePath, args) except ValueError: self._client.ui.showMessage(getMessage("mplayer-file-required-notification")) self._client.ui.showMessage(getMessage("mplayer-file-required-notification/example")) @@ -549,7 +557,8 @@ class MpvPlayer(BasePlayer): self.lineReceived(text) class __Listener(threading.Thread): - def __init__(self, playerController, playerPath, filePath, args): + def __init__(self, playerController, playerIPCHandler, playerPath, filePath, args): + self.playerIPCHandler = playerIPCHandler self.playerPath = playerPath self.mpv_arguments = playerController.getStartupArgs(args) self.mpv_running = True @@ -594,7 +603,7 @@ class MpvPlayer(BasePlayer): env['PYTHONPATH'] = pythonPath try: socket = self.mpv_arguments.get('input-ipc-server') - self.mpvpipe = MPV(mpv_location=self.playerPath, ipc_socket=socket, loglevel="info", log_handler=self.__playerController.mpv_log_handler, quit_callback=self.stop_client, **self.mpv_arguments) + self.mpvpipe = self.playerIPCHandler(mpv_location=self.playerPath, ipc_socket=socket, loglevel="info", log_handler=self.__playerController.mpv_log_handler, quit_callback=self.stop_client, env=env, **self.mpv_arguments) except Exception as e: self.quitReason = getMessage("media-player-error").format(str(e)) + " " + getMessage("mpv-failed-advice") self.__playerController.reactor.callFromThread(self.__playerController._client.ui.showErrorMessage, self.quitReason, True) diff --git a/syncplay/players/python_mpv_jsonipc/python_mpv_jsonipc.py b/syncplay/players/python_mpv_jsonipc/python_mpv_jsonipc.py index 993606b..64b3dc2 100644 --- a/syncplay/players/python_mpv_jsonipc/python_mpv_jsonipc.py +++ b/syncplay/players/python_mpv_jsonipc/python_mpv_jsonipc.py @@ -182,7 +182,7 @@ class MPVProcess: """ Manages an MPV process, ensuring the socket or pipe is available. (Internal) """ - def __init__(self, ipc_socket, mpv_location=None, **kwargs): + def __init__(self, ipc_socket, mpv_location=None, env=None, **kwargs): """ Create and start the MPV process. Will block until socket/pipe is available. @@ -198,7 +198,6 @@ class MPVProcess: mpv_location = "mpv" log.debug("Staring MPV from {0}.".format(mpv_location)) - ipc_socket_name = ipc_socket if os.name == 'nt': ipc_socket = "\\\\.\\pipe\\" + ipc_socket @@ -207,13 +206,11 @@ class MPVProcess: log.debug("Using IPC socket {0} for MPV.".format(ipc_socket)) self.ipc_socket = ipc_socket - args = [mpv_location] - self._set_default(kwargs, "idle", True) - self._set_default(kwargs, "input_ipc_server", ipc_socket_name) - self._set_default(kwargs, "input_terminal", False) - self._set_default(kwargs, "terminal", False) - args.extend("--{0}={1}".format(v[0].replace("_", "-"), self._mpv_fmt(v[1])) - for v in kwargs.items()) + args = self._get_arglist(mpv_location, **kwargs) + + self._start_process(ipc_socket, args, env=env) + + def _start_process(self, ipc_socket, args, env): self.process = subprocess.Popen(args) ipc_exists = False for _ in range(100): # Give MPV 10 seconds to start. @@ -238,6 +235,16 @@ class MPVProcess: if key not in prop_dict: prop_dict[key] = value + def _get_arglist(self, exec_location, **kwargs): + args = [exec_location] + self._set_default(kwargs, "idle", True) + self._set_default(kwargs, "input_ipc_server", self.ipc_socket) + self._set_default(kwargs, "input_terminal", False) + self._set_default(kwargs, "terminal", False) + args.extend("--{0}={1}".format(v[0].replace("_", "-"), self._mpv_fmt(v[1])) + for v in kwargs.items()) + return args + def _mpv_fmt(self, data): if data == True: return "yes" @@ -405,16 +412,7 @@ class MPV: ipc_socket = "/tmp/{0}".format(rand_file) if start_mpv: - # Attempt to start MPV 3 times. - for i in range(3): - try: - self.mpv_process = MPVProcess(ipc_socket, mpv_location, **kwargs) - break - except MPVError: - log.warning("MPV start failed.", exc_info=1) - continue - else: - raise MPVError("MPV process retry limit reached.") + self._start_mpv(ipc_socket, mpv_location, **kwargs) self.mpv_inter = MPVInter(ipc_socket, self._callback, self._quit_callback) self.properties = set(x.replace("-", "_") for x in self.command("get_property", "property-list")) @@ -451,6 +449,18 @@ class MPV: if len(args) == 2 and args[0] == "custom-bind": self.event_handler.put_task(self.key_bindings[args[1]]) + def _start_mpv(self, ipc_socket, mpv_location, **kwargs): + # Attempt to start MPV 3 times. + for i in range(3): + try: + self.mpv_process = MPVProcess(ipc_socket, mpv_location, **kwargs) + break + except MPVError: + log.warning("MPV start failed.", exc_info=1) + continue + else: + raise MPVError("MPV process retry limit reached.") + def _quit_callback(self): """ Internal handler for quit events. diff --git a/syncplay/resources/IINA.png b/syncplay/resources/IINA.png new file mode 100644 index 0000000..525fb55 Binary files /dev/null and b/syncplay/resources/IINA.png differ diff --git a/syncplay/resources/iina-bkg.png b/syncplay/resources/iina-bkg.png new file mode 100755 index 0000000..a96f81c Binary files /dev/null and b/syncplay/resources/iina-bkg.png differ