def getConfiguration(self): iniPath = self._getConfigurationFilePath() self._parseConfigFile(iniPath) # # Watch out for the method self._overrideConfigWithArgs when you're adding custom multi-word command line arguments # if self._config['language']: setLanguage(self._config['language']) self._argparser = argparse.ArgumentParser(description=getMessage("argument-description"), epilog=getMessage("argument-epilog")) self._argparser.add_argument('--no-gui', action='store_true', help=getMessage("nogui-argument")) self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help=getMessage("host-argument")) self._argparser.add_argument('-n', '--name', metavar='username', type=str, help=getMessage("name-argument")) self._argparser.add_argument('-d', '--debug', action='store_true', help=getMessage("debug-argument")) self._argparser.add_argument('-g', '--force-gui-prompt', action='store_true', help=getMessage("force-gui-prompt-argument")) self._argparser.add_argument('--no-store', action='store_true', help=getMessage("no-store-argument")) self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help=getMessage("room-argument")) self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("password-argument")) self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("player-path-argument")) self._argparser.add_argument('--language', metavar='language', type=str, help=getMessage("language-argument")) self._argparser.add_argument('file', metavar='file', type=lambda s: unicode(s, 'utf8'), nargs='?', help=getMessage("file-argument")) self._argparser.add_argument('--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) args = self._argparser.parse_args() if args.version: print getMessage("version-message").format(version, milestone) sys.exit() self._overrideConfigWithArgs(args) if not self._config['noGui']: try: from syncplay.vendor.Qt import QtWidgets, IsPySide, IsPySide2 from syncplay.vendor.Qt.QtCore import QCoreApplication from syncplay.vendor import qt5reactor if not (IsPySide2 or IsPySide): raise ImportError if QCoreApplication.instance() is None: self.app = QtWidgets.QApplication(sys.argv) qt5reactor.install() if isMacOS(): import appnope appnope.nope() except ImportError: print getMessage("unable-import-gui-error") self._config['noGui'] = True if self._config['file'] and self._config['file'][:2] == "--": self._config['playerArgs'].insert(0, self._config['file']) self._config['file'] = None # Arguments not validated yet - booleans are still text values if self._config['language']: setLanguage(self._config['language']) if (self._config['forceGuiPrompt'] == "True" or not self._config['file']) and not self._config['noGui']: self._forceGuiPrompt() self._checkConfig() self._saveConfig(iniPath) if self._config['file']: self._config['loadedRelativePaths'] = self._loadRelativeConfiguration() if self._config['language']: setLanguage(self._config['language']) return self._config
def __init__(self, playerController, playerPath, filePath, args): 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) call = [playerPath] if filePath: if isWindows() and not utils.isASCII(filePath): self.__playerController.delayedFilePath = filePath filePath = None else: call.extend([filePath]) call.extend(playerController.getStartupArgs(playerPath, args)) # 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 if filePath: self.__process = subprocess.Popen( call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd(filePath, env), env=env, bufsize=0) else: self.__process = subprocess.Popen( call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, bufsize=0) threading.Thread.__init__(self, name="MPlayer Listener")
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")
def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): self.__playerController = playerController self.requestedVLCVersion = False self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) def _usevlcintf(vlcIntfPath, vlcIntfUserPath): vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua" if not os.path.isfile(vlcSyncplayInterfacePath): vlcSyncplayInterfacePath = vlcIntfUserPath + "syncplay.lua" if os.path.isfile(vlcSyncplayInterfacePath): with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: for line in interfacefile: if "local connectorversion" in line: interface_version = line[26:31] if utils.meetsMinVersion( interface_version, constants.VLC_INTERFACE_MIN_VERSION): return True else: self.oldIntfVersion = line[26:31] return False playerController._client.ui.showErrorMessage( getMessage("vlc-interface-not-installed")) return False if isLinux(): playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") else: playerController.vlcIntfPath = os.path.dirname( playerPath).replace("\\", "/") + "/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath): playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{port=\"{}\"}}'.format( str(playerController.vlcport))) else: if isLinux(): playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: playerController.vlcDataPath = utils.findWorkingDir( ) + "\\resources" playerController.SLAVE_ARGS.append('--data-path={}'.format( playerController.vlcDataPath)) playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'. format(playerController.vlcModulePath, str(playerController.vlcport))) call.extend(playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcready = vlcReady self._vlcclosed = vlcClosed self._vlcVersion = None if self.oldIntfVersion: self.__playerController.drop( getMessage("vlc-interface-version-mismatch").format( self.oldIntfVersion, constants.VLC_INTERFACE_MIN_VERSION)) else: if isWindows() and getattr(sys, 'frozen', '') and getattr( sys, '_MEIPASS', '' ) is not None: # Needed for pyinstaller --onefile bundle self.__process = subprocess.Popen(call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): line = line.decode('utf-8') self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format( line), True) break elif "lua interface error" in line: playerController._client.ui.showErrorMessage( getMessage("media-player-error").format( line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread( target=self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() threading.Thread.__init__(self, name="VLC Listener") asynchat.async_chat.__init__(self) self.set_terminator(b'\n') self._ibuffer = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._sendingData = threading.Lock()
class VlcPlayer(BasePlayer): speedSupported = True customOpenDialog = False chatOSDSupported = False alertOSDSupported = True osdMessageSeparator = "; " RE_ANSWER = re.compile(constants.VLC_ANSWER_REGEX) SLAVE_ARGS = constants.VLC_SLAVE_ARGS if isMacOS(): SLAVE_ARGS.extend(constants.VLC_SLAVE_MACOS_ARGS) else: SLAVE_ARGS.extend(constants.VLC_SLAVE_NONMACOS_ARGS) vlcport = random.randrange( constants.VLC_MIN_PORT, constants.VLC_MAX_PORT) if ( constants.VLC_MIN_PORT < constants.VLC_MAX_PORT ) else constants.VLC_MIN_PORT def __init__(self, client, playerPath, filePath, args): from twisted.internet import reactor self.reactor = reactor self._client = client self._paused = None self._duration = None self._filename = None self._filepath = None self._filechanged = False self._lastVLCPositionUpdate = None self.shownVLCLatencyError = False self._previousPreviousPosition = -2 self._previousPosition = -1 self._position = 0 try: # Hack to fix locale issue without importing locale library self.radixChar = "{:n}".format(1.5)[1:2] if self.radixChar == "" or self.radixChar == "1" or self.radixChar == "5": raise ValueError except: self._client.ui.showErrorMessage( "Failed to determine locale. As a fallback Syncplay is using the following radix character: \".\"." ) self.radixChar = "." self._durationAsk = threading.Event() self._filenameAsk = threading.Event() self._pathAsk = threading.Event() self._positionAsk = threading.Event() self._pausedAsk = threading.Event() self._vlcready = threading.Event() self._vlcclosed = threading.Event() self._listener = None try: self._listener = self.__Listener(self, playerPath, filePath, args, self._vlcready, self._vlcclosed) except ValueError: self._client.ui.showErrorMessage( getMessage("vlc-failed-connection"), True) self.reactor.callFromThread( self._client.stop, True, ) return try: self._listener.setDaemon(True) self._listener.start() if not self._vlcready.wait(constants.VLC_OPEN_MAX_WAIT_TIME): self._vlcready.set() self._client.ui.showErrorMessage( getMessage("vlc-failed-connection"), True) self.reactor.callFromThread( self._client.stop, True, ) self.reactor.callFromThread( self._client.initPlayer, self, ) except: pass def _fileUpdateClearEvents(self): self._durationAsk.clear() self._filenameAsk.clear() self._pathAsk.clear() def _fileUpdateWaitEvents(self): self._durationAsk.wait() self._filenameAsk.wait() self._pathAsk.wait() def _onFileUpdate(self): self._fileUpdateClearEvents() self._getFileInfo() self._fileUpdateWaitEvents() args = (self._filename, self._duration, self._filepath) self.reactor.callFromThread(self._client.updateFile, *args) self.setPaused(self._client.getGlobalPaused()) self.setPosition(self._client.getGlobalPosition()) def askForStatus(self): self._filechanged = False self._positionAsk.clear() self._pausedAsk.clear() self._listener.sendLine(".") if self._filename and not self._filechanged: self._positionAsk.wait(constants.PLAYER_ASK_DELAY) self._client.updatePlayerStatus(self._paused, self.getCalculatedPosition()) else: self._client.updatePlayerStatus(self._client.getGlobalPaused(), self._client.getGlobalPosition()) def getCalculatedPosition(self): if self._lastVLCPositionUpdate is None: return self._client.getGlobalPosition() diff = time.time() - self._lastVLCPositionUpdate if diff > constants.PLAYER_ASK_DELAY and not self._paused: self._client.ui.showDebugMessage( "VLC did not response in time, so assuming position is {} ({}+{})" .format(self._position + diff, self._position, diff)) if diff > constants.VLC_LATENCY_ERROR_THRESHOLD: if not self.shownVLCLatencyError or constants.DEBUG_MODE: self._client.ui.showErrorMessage( getMessage("media-player-latency-warning").format( int(diff))) self.shownVLCLatencyError = True return self._position + diff else: return self._position def displayMessage(self, message, duration=constants.OSD_DURATION * 1000, OSDType=constants.OSD_DURATION, mood=constants.MESSAGE_NEUTRAL): duration /= 1000 if OSDType != constants.OSD_ALERT: self._listener.sendLine('display-osd: {}, {}, {}'.format( 'top-right', duration, message)) else: self._listener.sendLine('display-secondary-osd: {}, {}, {}'.format( 'center', duration, message)) def setSpeed(self, value): self._listener.sendLine("set-rate: {:.2n}".format(value)) def setFeatures(self, featureList): pass def setPosition(self, value): self._lastVLCPositionUpdate = time.time() self._listener.sendLine("set-position: {}".format(value).replace( ".", self.radixChar)) def setPaused(self, value): self._paused = value if not value: self._lastVLCPositionUpdate = time.time() self._listener.sendLine( 'set-playstate: {}'.format("paused" if value else "playing")) def getMRL(self, fileURL): if utils.isURL(fileURL): fileURL = urllib.parse.quote(fileURL, safe="%/:=&?~#+!$,;'@()*") return fileURL fileURL = fileURL.replace('\\', '/') fileURL = fileURL.encode('utf8') fileURL = urllib.parse.quote_plus(fileURL) if isWindows(): fileURL = "file:///" + fileURL else: fileURL = "file://" + fileURL fileURL = fileURL.replace("+", "%20") return fileURL def openFile(self, filePath, resetPosition=False): if not utils.isURL(filePath): normedPath = os.path.normpath(filePath) if os.path.isfile(normedPath): filePath = normedPath if utils.isASCII(filePath) and not utils.isURL(filePath): self._listener.sendLine('load-file: {}'.format(filePath)) else: fileURL = self.getMRL(filePath) self._listener.sendLine('load-file: {}'.format(fileURL)) def _getFileInfo(self): self._listener.sendLine("get-duration") self._listener.sendLine("get-filepath") self._listener.sendLine("get-filename") def lineReceived(self, line): # try: line = line.decode('utf-8') self._client.ui.showDebugMessage("player << {}".format(line)) # except: # pass match, name, value = self.RE_ANSWER.match(line), "", "" if match: name, value = match.group('command'), match.group('argument') if line == "filepath-change-notification": self._filechanged = True t = threading.Thread(target=self._onFileUpdate) t.setDaemon(True) t.start() elif name == "filepath": self._filechanged = True if value == "no-input": self._filepath = None else: if "file://" in value: value = value.replace("file://", "") if not os.path.isfile(value): value = value.lstrip("/") elif utils.isURL(value): value = urllib.parse.unquote(value) # value = value.decode('utf-8') self._filepath = value self._pathAsk.set() elif name == "duration": if value == "no-input": self._duration = 0 elif value == "invalid-32-bit-value": self._duration = 0 self.drop(getMessage("vlc-failed-versioncheck")) else: self._duration = float(value.replace(",", ".")) self._durationAsk.set() elif name == "playstate": self._paused = bool(value != 'playing') if ( value != "no-input" and self._filechanged == False) else self._client.getGlobalPaused() diff = time.time( ) - self._lastVLCPositionUpdate if self._lastVLCPositionUpdate else 0 if (self._paused == False and self._position == self._previousPreviousPosition and self._previousPosition == self._position and self._duration > constants.PLAYLIST_LOAD_NEXT_FILE_MINIMUM_LENGTH and (self._duration - self._position) < constants.VLC_EOF_DURATION_THRESHOLD and diff > constants.VLC_LATENCY_ERROR_THRESHOLD): self._client.ui.showDebugMessage( "Treating 'playing' response as 'paused' due to VLC EOF bug" ) self.setPaused(True) self._pausedAsk.set() elif name == "position": newPosition = float(value.replace( ",", ".")) if (value != "no-input" and not self._filechanged ) else self._client.getGlobalPosition() if newPosition == self._previousPosition and newPosition != self._duration and self._paused is False: self._client.ui.showDebugMessage( "Not considering position {} duplicate as new time because of VLC time precision bug" .format(newPosition)) self._previousPreviousPosition = self._previousPosition self._previousPosition = self._position self._positionAsk.set() return self._previousPreviousPosition = self._previousPosition self._previousPosition = self._position self._position = newPosition if self._position < 0 and self._duration > 2147 and self._vlcVersion == "3.0.0": self.drop(getMessage("vlc-failed-versioncheck")) self._lastVLCPositionUpdate = time.time() self._positionAsk.set() elif name == "filename": self._filechanged = True self._filename = value self._filenameAsk.set() elif line.startswith("vlc-version: "): self._vlcVersion = line.split(': ')[1].replace(' ', '-').split('-')[0] if not utils.meetsMinVersion(self._vlcVersion, constants.VLC_MIN_VERSION): self._client.ui.showErrorMessage( getMessage("vlc-version-mismatch").format( constants.VLC_MIN_VERSION)) self._vlcready.set() @staticmethod def run(client, playerPath, filePath, args): vlc = VlcPlayer(client, VlcPlayer.getExpandedPath(playerPath), filePath, args) return vlc @staticmethod def getDefaultPlayerPathsList(): l = [] for path in constants.VLC_PATHS: p = VlcPlayer.getExpandedPath(path) if p: l.append(p) return l @staticmethod def isValidPlayerPath(path): if "vlc" in path.lower() and VlcPlayer.getExpandedPath(path): return True return False @staticmethod def getPlayerPathErrors(playerPath, filePath): return None @staticmethod def getIconPath(path): return constants.VLC_ICONPATH @staticmethod def getExpandedPath(playerPath): if not os.path.isfile(playerPath): if os.path.isfile(playerPath + "vlc.exe"): playerPath += "vlc.exe" return playerPath elif os.path.isfile(playerPath + "\\vlc.exe"): playerPath += "\\vlc.exe" return playerPath elif os.path.isfile(playerPath + "VLCPortable.exe"): playerPath += "VLCPortable.exe" return playerPath elif os.path.isfile(playerPath + "\\VLCPortable.exe"): playerPath += "\\VLCPortable.exe" return playerPath 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 def drop(self, dropErrorMessage=None): if self._listener: self._vlcclosed.clear() self._listener.sendLine('close-vlc') self._vlcclosed.wait() self._durationAsk.set() self._filenameAsk.set() self._pathAsk.set() self._positionAsk.set() self._vlcready.set() self._pausedAsk.set() if dropErrorMessage: self.reactor.callFromThread(self._client.ui.showErrorMessage, dropErrorMessage, True) self.reactor.callFromThread( self._client.stop, False, ) class __Listener(threading.Thread, asynchat.async_chat): def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): self.__playerController = playerController self.requestedVLCVersion = False self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) def _usevlcintf(vlcIntfPath, vlcIntfUserPath): vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua" if not os.path.isfile(vlcSyncplayInterfacePath): vlcSyncplayInterfacePath = vlcIntfUserPath + "syncplay.lua" if os.path.isfile(vlcSyncplayInterfacePath): with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: for line in interfacefile: if "local connectorversion" in line: interface_version = line[26:31] if utils.meetsMinVersion( interface_version, constants.VLC_INTERFACE_MIN_VERSION): return True else: self.oldIntfVersion = line[26:31] return False playerController._client.ui.showErrorMessage( getMessage("vlc-interface-not-installed")) return False if isLinux(): playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") else: playerController.vlcIntfPath = os.path.dirname( playerPath).replace("\\", "/") + "/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath): playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{port=\"{}\"}}'.format( str(playerController.vlcport))) else: if isLinux(): playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: playerController.vlcDataPath = utils.findWorkingDir( ) + "\\resources" playerController.SLAVE_ARGS.append('--data-path={}'.format( playerController.vlcDataPath)) playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'. format(playerController.vlcModulePath, str(playerController.vlcport))) call.extend(playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcready = vlcReady self._vlcclosed = vlcClosed self._vlcVersion = None if self.oldIntfVersion: self.__playerController.drop( getMessage("vlc-interface-version-mismatch").format( self.oldIntfVersion, constants.VLC_INTERFACE_MIN_VERSION)) else: if isWindows() and getattr(sys, 'frozen', '') and getattr( sys, '_MEIPASS', '' ) is not None: # Needed for pyinstaller --onefile bundle self.__process = subprocess.Popen(call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): line = line.decode('utf-8') self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format( line), True) break elif "lua interface error" in line: playerController._client.ui.showErrorMessage( getMessage("media-player-error").format( line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread( target=self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() threading.Thread.__init__(self, name="VLC Listener") asynchat.async_chat.__init__(self) self.set_terminator(b'\n') self._ibuffer = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._sendingData = threading.Lock() def _shouldListenForSTDOUT(self): return not isWindows() def initiate_send(self): with self._sendingData: asynchat.async_chat.initiate_send(self) def run(self): self._vlcready.clear() self.connect(('localhost', self.__playerController.vlcport)) asyncore.loop() def handle_connect(self): asynchat.async_chat.handle_connect(self) self._vlcready.set() self.timeVLCLaunched = None def collect_incoming_data(self, data): self._ibuffer.append(data) def handle_close(self): if self.timeVLCLaunched and time.time( ) - self.timeVLCLaunched < constants.VLC_OPEN_MAX_WAIT_TIME: try: self.__playerController._client.ui.showDebugMessage( "Failed to connect to VLC, but reconnecting as within max wait time" ) except: pass self.run() elif self.vlcHasResponded: asynchat.async_chat.handle_close(self) self.__playerController.drop() else: self.vlcHasResponded = True asynchat.async_chat.handle_close(self) self.__playerController.drop( getMessage("vlc-failed-connection").format( constants.VLC_MIN_VERSION)) def handle_vlcoutput(self): out = self.__process.stderr for line in iter(out.readline, ''): line = line.decode('utf-8') if '[syncplay] core interface debug: removing module' in line: self.__playerController.drop() break out.close() def found_terminator(self): self.vlcHasResponded = True self.__playerController.lineReceived(b"".join(self._ibuffer)) self._ibuffer = [] def sendLine(self, line): if self.connected: if not self.requestedVLCVersion: self.requestedVLCVersion = True self.sendLine("get-vlc-version") # try: lineToSend = line + "\n" self.push(lineToSend.encode('utf-8')) if self.__playerController._client and self.__playerController._client.ui: self.__playerController._client.ui.showDebugMessage( "player >> {}".format(line)) # except: # pass if line == "close-vlc": self._vlcclosed.set() if not self.connected and not self.timeVLCLaunched: # For circumstances where Syncplay is not connected to VLC and is not reconnecting try: self.__process.terminate() except: # When VLC is already closed pass
def getConfiguration(self): iniPath = self._getConfigurationFilePath() self._parseConfigFile(iniPath) # # Watch out for the method self._overrideConfigWithArgs when you're adding custom multi-word command line arguments # if self._config['language']: setLanguage(self._config['language']) self._argparser = argparse.ArgumentParser( description=getMessage("argument-description"), epilog=getMessage("argument-epilog")) self._argparser.add_argument('--no-gui', action='store_true', help=getMessage("nogui-argument")) self._argparser.add_argument('-a', '--host', metavar='hostname', type=str, help=getMessage("host-argument")) self._argparser.add_argument('-n', '--name', metavar='username', type=str, help=getMessage("name-argument")) self._argparser.add_argument('-d', '--debug', action='store_true', help=getMessage("debug-argument")) self._argparser.add_argument( '-g', '--force-gui-prompt', action='store_true', help=getMessage("force-gui-prompt-argument")) self._argparser.add_argument('--no-store', action='store_true', help=getMessage("no-store-argument")) self._argparser.add_argument('-r', '--room', metavar='room', type=str, nargs='?', help=getMessage("room-argument")) self._argparser.add_argument('-p', '--password', metavar='password', type=str, nargs='?', help=getMessage("password-argument")) self._argparser.add_argument('--player-path', metavar='path', type=str, help=getMessage("player-path-argument")) self._argparser.add_argument('--language', metavar='language', type=str, help=getMessage("language-argument")) self._argparser.add_argument('file', metavar='file', type=str, nargs='?', help=getMessage("file-argument")) self._argparser.add_argument( '--clear-gui-data', action='store_true', help=getMessage("clear-gui-data-argument")) self._argparser.add_argument('-v', '--version', action='store_true', help=getMessage("version-argument")) self._argparser.add_argument('_args', metavar='options', type=str, nargs='*', help=getMessage("args-argument")) args = self._argparser.parse_args() if args.version: print(getMessage("version-message").format(version, milestone)) sys.exit() self._overrideConfigWithArgs(args) if not self._config['noGui']: try: from syncplay.vendor.Qt import QtWidgets, IsPySide, IsPySide2 from syncplay.vendor.Qt.QtCore import QCoreApplication from syncplay.vendor import qt5reactor if not (IsPySide2 or IsPySide): raise ImportError if QCoreApplication.instance() is None: self.app = QtWidgets.QApplication(sys.argv) qt5reactor.install() if isMacOS(): import appnope appnope.nope() except ImportError: try: from twisted.trial import unittest except: print(getMessage("unable-import-twisted-error")) sys.exit() print(getMessage("unable-import-gui-error")) self._config['noGui'] = True if self._config['file'] and self._config['file'][:2] == "--": self._config['playerArgs'].insert(0, self._config['file']) self._config['file'] = None # Arguments not validated yet - booleans are still text values if self._config['language']: setLanguage(self._config['language']) if (self._config['forceGuiPrompt'] == "True" or not self._config['file']) and not self._config['noGui']: self._forceGuiPrompt() self._checkConfig() self._saveConfig(iniPath) if self._config['file']: self._config[ 'loadedRelativePaths'] = self._loadRelativeConfiguration() if self._config['language']: setLanguage(self._config['language']) return self._config
def __init__(self, playerController, playerPath, filePath, args): 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) call = [playerPath] if filePath: if isWindows() and not utils.isASCII(filePath): self.__playerController.delayedFilePath = filePath filePath = None else: call.extend([filePath]) call.extend(playerController.getStartupArgs(playerPath, args)) # 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 if filePath: self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.__getCwd( filePath, env), env=env, bufsize=0) else: self.__process = subprocess.Popen(call, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, bufsize=0) threading.Thread.__init__(self, name="MPlayer Listener")
def __init__(self, playerController, playerIPCHandler, playerPath, filePath, args): self.playerIPCHandler = playerIPCHandler 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: env['PATH'] = '/opt/homebrew/bin:/usr/local/bin:/usr/bin' ytdl_path = subprocess.check_output(['which', 'youtube-dl'], text=True, env=env).rstrip('\n') with open(ytdl_path, 'rb') as f: ytdl_shebang = f.readline() ytdl_python = ytdl_shebang.decode('utf-8').lstrip('!#').rstrip('\n') if '/usr/bin/env' in ytdl_python: python_name = ytdl_python.split(' ')[1] python_executable = subprocess.check_output(['which', python_name], text=True, env=env).rstrip('\n') else: python_executable = ytdl_python pythonLibs = subprocess.check_output([python_executable, '-E', '-c', 'import sys; print(sys.path)'], text=True, env=dict()) pythonLibs = ast.literal_eval(pythonLibs) pythonPath = ':'.join(pythonLibs[1:]) except Exception as e: pythonPath = None if pythonPath is not None: env['PATH'] = python_executable + ':' + env['PATH'] env['PYTHONPATH'] = pythonPath try: socket = self.mpv_arguments.get('input-ipc-server') self.mpvpipe: MPV = 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) self.mpvpipe.bind_event("file-loaded", playerController.file_ready) self.mpvpipe.bind_property_observer("eof-reached", playerController.eofDetected) self.mpvpipe.bind_property_observer('time-pos', playerController.property_update) self.mpvpipe.bind_property_observer('filename', playerController.property_update) self.mpvpipe.bind_property_observer('duration', playerController.property_update) self.mpvpipe.bind_property_observer('path', playerController.property_update) self.mpvpipe.bind_property_observer('pause', playerController.property_update) self.mpvpipe.bind_property_observer('demuxer-cache-duration', playerController.property_update) 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 __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): self.__playerController = playerController self.requestedVLCVersion = False self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) def _usevlcintf(vlcIntfPath, vlcIntfUserPath): vlcSyncplayInterfacePath = vlcIntfPath + "syncplay.lua" if not os.path.isfile(vlcSyncplayInterfacePath): vlcSyncplayInterfacePath = vlcIntfUserPath + "syncplay.lua" if os.path.isfile(vlcSyncplayInterfacePath): with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: for line in interfacefile: if "local connectorversion" in line: interface_version = line[26:31] if utils.meetsMinVersion(interface_version, constants.VLC_INTERFACE_MIN_VERSION): return True else: self.oldIntfVersion = line[26:31] return False playerController._client.ui.showErrorMessage(getMessage("vlc-interface-not-installed")) return False if isLinux(): playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") else: playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" if _usevlcintf(playerController.vlcIntfPath, playerController.vlcIntfUserPath): playerController.SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(playerController.vlcport))) else: if isLinux(): playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: playerController.vlcDataPath = utils.findWorkingDir() + "\\resources" playerController.SLAVE_ARGS.append('--data-path={}'.format(playerController.vlcDataPath)) playerController.SLAVE_ARGS.append('--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format(playerController.vlcModulePath, str(playerController.vlcport))) call.extend(playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcready = vlcReady self._vlcclosed = vlcClosed self._vlcVersion = None if self.oldIntfVersion: self.__playerController.drop(getMessage("vlc-interface-version-mismatch").format(self.oldIntfVersion,constants.VLC_INTERFACE_MIN_VERSION)) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format(line), True) break elif "lua interface error" in line: playerController._client.ui.showErrorMessage( getMessage("media-player-error").format(line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread(target = self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() threading.Thread.__init__(self, name="VLC Listener") asynchat.async_chat.__init__(self) self.set_terminator("\n") self._ibuffer = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._sendingData = threading.Lock()
def __init__(self, playerController, playerPath, filePath, args, reactor): self.__playerController = playerController self.reactor = reactor self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) if isLinux(): if 'snap' in playerPath: self.__playerController.vlcIntfPath = '/snap/vlc/current/usr/lib/vlc/lua/intf/' self.__playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), "snap/vlc/current/.local/share/vlc/lua/intf/") else: self.__playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" self.__playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): self.__playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" self.__playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. self.__playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" self.__playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif "vlcportable.exe" in playerPath.lower(): self.__playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/App/vlc/lua/intf/" self.__playerController.vlcIntfUserPath = playerController.vlcIntfPath else: self.__playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/lua/intf/" self.__playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") self.__playerController.vlcModulePath = self.__playerController.vlcIntfPath + "modules/?.luac" def _createIntfFolder(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage("Checking if syncplay.lua intf directory exists") from pathlib import Path if os.path.exists(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage("Found syncplay.lua intf directory:'{}'".format(vlcSyncplayInterfaceDir)) else: self.__playerController._client.ui.showDebugMessage("syncplay.lua intf directory not found, so creating directory '{}'".format(vlcSyncplayInterfaceDir)) Path(vlcSyncplayInterfaceDir).mkdir(mode=0o755, parents=True, exist_ok=True) try: _createIntfFolder(self.__playerController.vlcIntfUserPath) copyForm = utils.findResourcePath("syncplay.lua") copyTo = os.path.join(self.__playerController.vlcIntfUserPath, "syncplay.lua") self.__playerController._client.ui.showDebugMessage("Copying VLC Lua Interface from '{}' to '{}'".format(copyForm, copyTo)) import shutil if os.path.exists(copyTo): os.chmod(copyTo, 0o755) shutil.copyfile(copyForm, copyTo) os.chmod(copyTo, 0o755) except Exception as e: self.__playerController._client.ui.showErrorMessage(e) return if isLinux(): self.__playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: self.__playerController.vlcDataPath = utils.findWorkingDir() + "\\resources" self.__playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format( self.__playerController.vlcModulePath, str(self.__playerController.vlcport))) call.extend(self.__playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcVersion = None if isWindows() and getattr(sys, 'frozen', '') and getattr(sys, '_MEIPASS', '') is not None: # Needed for pyinstaller --onefile bundle self.__process = subprocess.Popen( call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): line = line.decode('utf-8') self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: self.__playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format(line), True) break elif "lua interface error" in line: self.__playerController._client.ui.showErrorMessage( getMessage("media-player-error").format(line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread(target=self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() self.__playerController._vlcready.clear() self._factory = VLCClientFactory(self.__playerController, self.vlcHasResponded, self.timeVLCLaunched, self.__process) self.reactor.connectTCP('localhost', self.__playerController.vlcport, self._factory)
def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): self.__playerController = playerController self.requestedVLCVersion = False self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) if isLinux(): if 'snap' in playerPath: playerController.vlcIntfPath = '/snap/vlc/current/usr/lib/vlc/lua/intf/' playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "snap/vlc/current/.local/share/vlc/lua/intf/") else: playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif "vlcportable.exe" in playerPath.lower(): playerController.vlcIntfPath = os.path.dirname( playerPath).replace("\\", "/") + "/App/vlc/lua/intf/" playerController.vlcIntfUserPath = playerController.vlcIntfPath else: playerController.vlcIntfPath = os.path.dirname( playerPath).replace("\\", "/") + "/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" def _createIntfFolder(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage( "Checking if syncplay.lua intf directory exists") from pathlib import Path if os.path.exists(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage( "Found syncplay.lua intf directory:'{}'".format( vlcSyncplayInterfaceDir)) else: self.__playerController._client.ui.showDebugMessage( "syncplay.lua intf directory not found, so creating directory '{}'" .format(vlcSyncplayInterfaceDir)) Path(vlcSyncplayInterfaceDir).mkdir(mode=0o755, parents=True, exist_ok=True) def _intfNeedsUpdating(vlcSyncplayInterfacePath): self.__playerController._client.ui.showDebugMessage( "Checking if '{}' exists and if it is the expected version" .format(vlcSyncplayInterfacePath)) if not os.path.isfile(vlcSyncplayInterfacePath): self.__playerController._client.ui.showDebugMessage( "syncplay.lua not found, so file needs copying") return True if os.path.isfile(vlcSyncplayInterfacePath): with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: for line in interfacefile: if "local connectorversion" in line: interface_version = line[26:31] if interface_version == constants.VLC_INTERFACE_VERSION: self.__playerController._client.ui.showDebugMessage( "syncplay.lua exists and is expected version, so no file needs copying" ) return False else: self.oldIntfVersion = line[26:31] self.__playerController._client.ui.showDebugMessage( "syncplay.lua is {} but expected version is {} so file needs to be copied" .format( interface_version, constants.VLC_INTERFACE_VERSION)) return True self.__playerController._client.ui.showDebugMessage( "Up-to-dateness checks failed, so copy the file.") return True if _intfNeedsUpdating( os.path.join(playerController.vlcIntfUserPath, "syncplay.lua")): try: _createIntfFolder(playerController.vlcIntfUserPath) copyForm = utils.findResourcePath("syncplay.lua") copyTo = os.path.join(playerController.vlcIntfUserPath, "syncplay.lua") self.__playerController._client.ui.showDebugMessage( "Copying VLC Lua Interface from '{}' to '{}'".format( copyForm, copyTo)) import shutil if os.path.exists(copyTo): os.chmod(copyTo, 0o755) shutil.copyfile(copyForm, copyTo) os.chmod(copyTo, 0o755) except Exception as e: playerController._client.ui.showErrorMessage(e) return if isLinux(): playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: playerController.vlcDataPath = utils.findWorkingDir( ) + "\\resources" playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'. format(playerController.vlcModulePath, str(playerController.vlcport))) call.extend(playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcready = vlcReady self._vlcclosed = vlcClosed self._vlcVersion = None if isWindows() and getattr(sys, 'frozen', '') and getattr( sys, '_MEIPASS', '') is not None: # Needed for pyinstaller --onefile bundle self.__process = subprocess.Popen(call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): line = line.decode('utf-8') self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format(line), True) break elif "lua interface error" in line: playerController._client.ui.showErrorMessage( getMessage("media-player-error").format(line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread( target=self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() threading.Thread.__init__(self, name="VLC Listener") asynchat.async_chat.__init__(self) self.set_terminator(b'\n') self._ibuffer = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._sendingData = threading.Lock()
def __init__(self, playerController, playerPath, filePath, args, vlcReady, vlcClosed): self.__playerController = playerController self.requestedVLCVersion = False self.vlcHasResponded = False self.oldIntfVersion = None self.timeVLCLaunched = None call = [playerPath] if filePath: if utils.isASCII(filePath): call.append(filePath) else: call.append(self.__playerController.getMRL(filePath)) if isLinux(): playerController.vlcIntfPath = "/usr/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") elif isMacOS(): playerController.vlcIntfPath = "/Applications/VLC.app/Contents/MacOS/share/lua/intf/" playerController.vlcIntfUserPath = os.path.join( os.getenv('HOME', '.'), "Library/Application Support/org.videolan.vlc/lua/intf/") elif isBSD(): # *BSD ports/pkgs install to /usr/local by default. # This should also work for all the other BSDs, such as OpenBSD or DragonFly. playerController.vlcIntfPath = "/usr/local/lib/vlc/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('HOME', '.'), ".local/share/vlc/lua/intf/") else: playerController.vlcIntfPath = os.path.dirname(playerPath).replace("\\", "/") + "/lua/intf/" playerController.vlcIntfUserPath = os.path.join(os.getenv('APPDATA', '.'), "VLC\\lua\\intf\\") playerController.vlcModulePath = playerController.vlcIntfPath + "modules/?.luac" def _createIntfFolder(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage("Checking if syncplay.lua intf directory exists") from pathlib import Path if os.path.exists(vlcSyncplayInterfaceDir): self.__playerController._client.ui.showDebugMessage("Found syncplay.lua intf directory:'{}'".format(vlcSyncplayInterfaceDir)) else: self.__playerController._client.ui.showDebugMessage("syncplay.lua intf directory not found, so creating directory '{}'".format(vlcSyncplayInterfaceDir)) Path(vlcSyncplayInterfaceDir).mkdir(mode=0o755, parents=True, exist_ok=True) def _intfNeedsUpdating(vlcSyncplayInterfacePath): self.__playerController._client.ui.showDebugMessage("Checking if '{}' exists and if it is the expected version".format(vlcSyncplayInterfacePath)) if not os.path.isfile(vlcSyncplayInterfacePath): self.__playerController._client.ui.showDebugMessage("syncplay.lua not found, so file needs copying") return True if os.path.isfile(vlcSyncplayInterfacePath): with open(vlcSyncplayInterfacePath, 'rU') as interfacefile: for line in interfacefile: if "local connectorversion" in line: interface_version = line[26:31] if interface_version == constants.VLC_INTERFACE_VERSION: self.__playerController._client.ui.showDebugMessage("syncplay.lua exists and is expected version, so no file needs copying") return False else: self.oldIntfVersion = line[26:31] self.__playerController._client.ui.showDebugMessage("syncplay.lua is {} but expected version is {} so file needs to be copied".format(interface_version, constants.VLC_INTERFACE_VERSION)) return True self.__playerController._client.ui.showDebugMessage("Up-to-dateness checks failed, so copy the file.") return True if _intfNeedsUpdating(os.path.join(playerController.vlcIntfUserPath, "syncplay.lua")): try: _createIntfFolder(playerController.vlcIntfUserPath) copyForm = utils.findResourcePath("syncplay.lua") copyTo = os.path.join(playerController.vlcIntfUserPath, "syncplay.lua") self.__playerController._client.ui.showDebugMessage("Copying VLC Lua Interface from '{}' to '{}'".format(copyForm, copyTo)) import shutil if os.path.exists(copyTo): os.chmod(copyTo, 0o755) shutil.copyfile(copyForm, copyTo) os.chmod(copyTo, 0o755) except Exception as e: playerController._client.ui.showErrorMessage(e) return if isLinux(): playerController.vlcDataPath = "/usr/lib/syncplay/resources" else: playerController.vlcDataPath = utils.findWorkingDir() + "\\resources" playerController.SLAVE_ARGS.append('--data-path={}'.format(playerController.vlcDataPath)) playerController.SLAVE_ARGS.append( '--lua-config=syncplay={{modulepath=\"{}\",port=\"{}\"}}'.format( playerController.vlcModulePath, str(playerController.vlcport))) call.extend(playerController.SLAVE_ARGS) if args: call.extend(args) self._vlcready = vlcReady self._vlcclosed = vlcClosed self._vlcVersion = None if isWindows() and getattr(sys, 'frozen', '') and getattr(sys, '_MEIPASS', '') is not None: # Needed for pyinstaller --onefile bundle self.__process = subprocess.Popen( call, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False, creationflags=0x08000000) else: self.__process = subprocess.Popen(call, stderr=subprocess.PIPE, stdout=subprocess.PIPE) self.timeVLCLaunched = time.time() if self._shouldListenForSTDOUT(): for line in iter(self.__process.stderr.readline, ''): line = line.decode('utf-8') self.vlcHasResponded = True self.timeVLCLaunched = None if "[syncplay]" in line: if "Listening on host" in line: break if "Hosting Syncplay" in line: break elif "Couldn't find lua interface" in line: playerController._client.ui.showErrorMessage( getMessage("vlc-failed-noscript").format(line), True) break elif "lua interface error" in line: playerController._client.ui.showErrorMessage( getMessage("media-player-error").format(line), True) break if not isMacOS(): self.__process.stderr = None else: vlcoutputthread = threading.Thread(target=self.handle_vlcoutput, args=()) vlcoutputthread.setDaemon(True) vlcoutputthread.start() threading.Thread.__init__(self, name="VLC Listener") asynchat.async_chat.__init__(self) self.set_terminator(b'\n') self._ibuffer = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self._sendingData = threading.Lock()