def __init__(self, playerController, playerPath, filePath, args): self.playerPath = playerPath self.mpv_arguments = playerController.getStartupArgs(args) self.mpv_running = True self.sendQueue = [] self.readyToSend = True self.lastSendTime = None self.lastNotReadyTime = None self.__playerController = playerController if not self.__playerController._client._config["chatOutputEnabled"]: self.__playerController.alertOSDSupported = False self.__playerController.chatOSDSupported = False if self.__playerController.getPlayerPathErrors(playerPath, filePath): raise ValueError() if filePath and '://' not in filePath: if not os.path.isfile(filePath) and 'PWD' in os.environ: filePath = os.environ['PWD'] + os.path.sep + filePath filePath = os.path.realpath(filePath) if filePath: self.__playerController.delayedFilePath = filePath # At least mpv may output escape sequences which result in syncplay # trying to parse something like # "\x1b[?1l\x1b>ANS_filename=blah.mkv". Work around this by # unsetting TERM. env = os.environ.copy() if 'TERM' in env: del env['TERM'] # On macOS, youtube-dl requires system python to run. Set the environment # to allow that version of python to be executed in the mpv subprocess. if isMacOS(): try: pythonLibs = subprocess.check_output(['/usr/bin/python', '-E', '-c', 'import sys; print(sys.path)'], text=True, env=dict()) pythonLibs = ast.literal_eval(pythonLibs) pythonPath = ':'.join(pythonLibs[1:]) except: pythonPath = None if pythonPath is not None: env['PATH'] = '/usr/bin:/usr/local/bin' 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) 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) self.__playerController.drop() self.__process = self.mpvpipe #self.mpvpipe.show_text("HELLO WORLD!", 1000) threading.Thread.__init__(self, name="MPV Listener")
class __Listener(threading.Thread): def __init__(self, playerController, playerPath, filePath, args): self.playerPath = playerPath self.mpv_arguments = playerController.getStartupArgs(args) self.mpv_running = True self.sendQueue = [] self.readyToSend = True self.lastSendTime = None self.lastNotReadyTime = None self.__playerController = playerController if not self.__playerController._client._config["chatOutputEnabled"]: self.__playerController.alertOSDSupported = False self.__playerController.chatOSDSupported = False if self.__playerController.getPlayerPathErrors( playerPath, filePath): raise ValueError() if filePath and '://' not in filePath: if not os.path.isfile(filePath) and 'PWD' in os.environ: filePath = os.environ['PWD'] + os.path.sep + filePath filePath = os.path.realpath(filePath) if filePath: self.__playerController.delayedFilePath = filePath # At least mpv may output escape sequences which result in syncplay # trying to parse something like # "\x1b[?1l\x1b>ANS_filename=blah.mkv". Work around this by # unsetting TERM. env = os.environ.copy() if 'TERM' in env: del env['TERM'] # On macOS, youtube-dl requires system python to run. Set the environment # to allow that version of python to be executed in the mpv subprocess. if isMacOS(): try: pythonLibs = subprocess.check_output([ '/usr/bin/python', '-E', '-c', 'import sys; print(sys.path)' ], text=True, env=dict()) pythonLibs = ast.literal_eval(pythonLibs) pythonPath = ':'.join(pythonLibs[1:]) except: pythonPath = None if pythonPath is not None: env['PATH'] = '/usr/bin:/usr/local/bin' env['PYTHONPATH'] = pythonPath try: self.mpvpipe = MPV( mpv_location=self.playerPath, loglevel="info", log_handler=self.__playerController.mpv_log_handler, quit_callback=self.stop_client, **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) self.__playerController.drop() self.__process = self.mpvpipe #self.mpvpipe.show_text("HELLO WORLD!", 1000) threading.Thread.__init__(self, name="MPV Listener") def __getCwd(self, filePath, env): if not filePath: return None if os.path.isfile(filePath): cwd = os.path.dirname(filePath) elif 'HOME' in env: cwd = env['HOME'] elif 'APPDATA' in env: cwd = env['APPDATA'] else: cwd = None return cwd def run(self): pass def sendChat(self, message): if message: if message[:1] == "/" and message != "/": command = message[1:] if command and command[:1] == "/": message = message[1:] else: self.__playerController.reactor.callFromThread( self.__playerController._client.ui.executeCommand, command) return self.__playerController.reactor.callFromThread( self.__playerController._client.sendChat, message) def isReadyForSend(self): self.checkForReadinessOverride() return self.readyToSend def setReadyToSend(self, newReadyState): oldState = self.readyToSend self.readyToSend = newReadyState self.lastNotReadyTime = time.time( ) if newReadyState == False else None if self.readyToSend == True: self.__playerController._client.ui.showDebugMessage( "<mpv> Ready to send: True") else: self.__playerController._client.ui.showDebugMessage( "<mpv> Ready to send: False") if self.readyToSend == True and oldState == False: self.processSendQueue() def checkForReadinessOverride(self): if self.lastNotReadyTime and time.time( ) - self.lastNotReadyTime > constants.MPV_MAX_NEWFILE_COOLDOWN_TIME: self.setReadyToSend(True) def sendLine(self, line, notReadyAfterThis=None): self.checkForReadinessOverride() try: if self.sendQueue: if constants.MPV_SUPERSEDE_IF_DUPLICATE_COMMANDS: for command in constants.MPV_SUPERSEDE_IF_DUPLICATE_COMMANDS: line_command = " ".join(line) answer = line_command.startswith(command) #self.__playerController._client.ui.showDebugMessage("Does line_command {} start with {}? {}".format(line_command, command, answer)) if line_command.startswith(command): for itemID, deletionCandidate in enumerate( self.sendQueue): if " ".join(deletionCandidate).startswith( command): self.__playerController._client.ui.showDebugMessage( "<mpv> Remove duplicate (supersede): {}" .format(self.sendQueue[itemID])) try: self.sendQueue.remove( self.sendQueue[itemID]) except UnicodeWarning: self.__playerController._client.ui.showDebugMessage( "<mpv> Unicode mismatch occured when trying to remove duplicate" ) # TODO: Prevent this from being triggered pass break break if constants.MPV_REMOVE_BOTH_IF_DUPLICATE_COMMANDS: for command in constants.MPV_REMOVE_BOTH_IF_DUPLICATE_COMMANDS: if line == command: for itemID, deletionCandidate in enumerate( self.sendQueue): if deletionCandidate == command: self.__playerController._client.ui.showDebugMessage( "<mpv> Remove duplicate (delete both): {}" .format(self.sendQueue[itemID])) self.__playerController._client.ui.showDebugMessage( self.sendQueue[itemID]) return except Exception as e: self.__playerController._client.ui.showDebugMessage( "<mpv> Problem removing duplicates, etc: {}".format(e)) self.sendQueue.append(line) self.processSendQueue() if notReadyAfterThis: self.setReadyToSend(False) def processSendQueue(self): while self.sendQueue and self.readyToSend: if self.lastSendTime and time.time( ) - self.lastSendTime < constants.MPV_SENDMESSAGE_COOLDOWN_TIME: self.__playerController._client.ui.showDebugMessage( "<mpv> Throttling message send, so sleeping for {}". format(constants.MPV_SENDMESSAGE_COOLDOWN_TIME)) time.sleep(constants.MPV_SENDMESSAGE_COOLDOWN_TIME) try: lineToSend = self.sendQueue.pop() if lineToSend: self.lastSendTime = time.time() self.actuallySendLine(lineToSend) except IndexError: pass def stop_client(self): self.readyToSend = False try: self.mpvpipe.terminate() except: #When mpv is already closed pass self.__playerController._takeLocksDown() self.__playerController.reactor.callFromThread( self.__playerController._client.stop, False) def actuallySendLine(self, line): try: self.__playerController._client.ui.showDebugMessage( "player >> {}".format(line)) try: self.mpvpipe.command(*line) except Exception as e: self.__playerController._client.ui.showDebugMessage( "CANNOT SEND {} DUE TO {}".format(line, e)) self.stop_client() except IOError: pass