import logging, time, asyncio import requests from urllib.parse import urljoin class VLCError(Exception): pass class VLCConnectionError(VLCError): pass class VLCExitError(VLCError): pass class VLCLauncher: def __init__(self, config, debug=False): self.config = config self.debug = debug self.base_url = 'http://' + config['host'] + ':' + str(config['port']) self.process = None def check_connection(self, retries=0): for i in range(retries, -1, -1): try: resp = requests.get(self.base_url, timeout=5) except requests.exceptions.RequestException as e: if i > 0: logging.warning( 'Connection attempt failed because of: %s. Retry in 3 seconds.' % str(e) ) time.sleep(3) continue else: if 'VideoLAN' in resp.text: return True raise VLCConnectionError('Failed to connect to the VLC web server.') async def launch(self): try: self.check_connection() except VLCConnectionError: pass else: logging.warning('Found existing VLC instance.') return logging.info('Launching VLC with HTTP server at %s.' % self.config['path']) command = [ self.config['path'], '--extraintf', self.config['extraintf'], '--http-host', self.config['host'], '--http-port', str(self.config['port']), '--http-password', str(self.config['password']), '--repeat', '--image-duration', '-1' ] + self.config['options'] kwargs = {} if self.debug: command.extend(('--log-verbose', '3')) else: kwargs['stderr'] = asyncio.subprocess.DEVNULL kwargs['stdout'] = asyncio.subprocess.DEVNULL self.process = await asyncio.create_subprocess_exec(*command, **kwargs) time.sleep(1) self.check_connection(3) return self.process async def watch_exit(self): if not self.process: return await self.process.wait() raise VLCExitError('VLC was closed.') class VLCHTTPClient: def __init__(self, config): self.session = requests.session() self.base_url = 'http://' + config['host'] + ':' + str(config['port']) self.session.auth = ('', config['password']) def _request(self, path, **kwargs): resp = self.session.get(urljoin(self.base_url, path), **kwargs) if resp.status_code != requests.codes.ok: resp.raise_for_status() self.session.close() # VLC doesn't support keep-alive return resp def _command(self, command, params={}): # VLC doesn't support urlencoded parameters # https://forum.videolan.org/viewtopic.php?f=16&t=145695 params = ('command=' + command + '&' + '&'.join('%s=%s' % (k, v) for k, v in params.items())) return self._request('requests/status.xml', params=params) def _format_uri(self, uri): # VLC only understands urlencoded = return uri.replace('=', '%3D') def status(self): return self._request('requests/status.json').json() def add(self, uri): return self._command('in_play', {'input': self._format_uri(uri)}) def enqueue(self, uri): return self._command('in_enqueue', {'input': self._format_uri(uri)}) def play(self, uid=None): if uid: return self._command('pl_play', {'id': uid}) else: return self._command('pl_play') def pause(self): return self._command('pl_pause') def stop(self): return self._command('pl_stop') def next(self): return self._command('pl_next') def previous(self): return self._command('pl_previous') def empty(self): return self._command('pl_empty') def toggle_repeat(self): return self._command('pl_repeat') def repeat(self, value=None): if value is None: return self._command('pl_repeat') if self.status()['repeat'] != value: return self._command('pl_repeat')