def _create_arguments(self): if self.namedpipe: filename = self.namedpipe.path if is_win32: if self.player_name == "vlc": filename = f"stream://\\{filename}" elif self.player_name == "mpv": filename = f"file://{filename}" elif self.filename: filename = self.filename elif self.http: filename = self.http.url else: filename = "-" extra_args = [] if self.title is not None: # vlc if self.player_name == "vlc": # see https://wiki.videolan.org/Documentation:Format_String/, allow escaping with \$ self.title = self.title.replace("$", "$$").replace(r'\$$', "$") extra_args.extend(["--input-title-format", self.title]) # mpv if self.player_name == "mpv": # see https://mpv.io/manual/stable/#property-expansion, allow escaping with \$, respect mpv's $> self.title = self._mpv_title_escape(self.title) extra_args.append(f"--force-media-title={self.title}") # potplayer if self.player_name == "potplayer": if filename != "-": # PotPlayer - About - Command Line # You can specify titles for URLs by separating them with a backslash (\) at the end of URLs. # eg. "http://...\title of this url" self.title = self.title.replace('"', '') filename = filename[:-1] + '\\' + self.title + filename[-1] # format args via the formatter, so that invalid/unknown variables don't raise a KeyError argsformatter = Formatter({ PLAYER_ARGS_INPUT_DEFAULT: lambda: filename, PLAYER_ARGS_INPUT_FALLBACK: lambda: filename }) args = argsformatter.title(self.args) cmd = self.cmd # player command if is_win32: eargs = subprocess.list2cmdline(extra_args) # do not insert and extra " " when there are no extra_args return " ".join([cmd] + ([eargs] if eargs else []) + [args]) return shlex.split(cmd) + extra_args + shlex.split(args)
def output_stream_passthrough(stream, formatter: Formatter): """Prepares a filename to be passed to the player.""" global output try: url = stream.to_url() except TypeError: console.exit("The stream specified cannot be translated to a URL") return False output = PlayerOutput( args.player, args=args.player_args, filename=f'"{url}"', call=True, quiet=not args.verbose_player, title=formatter.title(args.title, defaults=DEFAULT_STREAM_METADATA) if args.title else args.url ) try: log.info(f"Starting player: {args.player}") output.open() except OSError as err: console.exit(f"Failed to start player: {args.player} ({err})") return False return True
def get_formatter(plugin: Plugin): return Formatter( { "url": lambda: args.url, "author": lambda: plugin.get_author(), "category": lambda: plugin.get_category(), "game": lambda: plugin.get_category(), "title": lambda: plugin.get_title(), "time": lambda: datetime.now() }, { "time": lambda dt, fmt: dt.strftime(fmt) })
def create_output(formatter: Formatter): """Decides where to write the stream. Depending on arguments it can be one of these: - The stdout pipe - A subprocess' stdin pipe - A named pipe that the subprocess reads from - A regular file """ if (args.output or args.stdout) and (args.record or args.record_and_pipe): console.exit( "Cannot use record options with other file output options.") if args.output: if args.output == "-": out = FileOutput(fd=stdout) else: out = check_file_output( formatter.path(args.output, args.fs_safe_rules), args.force) elif args.stdout: out = FileOutput(fd=stdout) elif args.record_and_pipe: record = check_file_output( formatter.path(args.record_and_pipe, args.fs_safe_rules), args.force) out = FileOutput(fd=stdout, record=record) elif not args.player: console.exit("The default player (VLC) does not seem to be " "installed. You must specify the path to a player " "executable with --player, a file path to save the " "stream with --output, or pipe the stream to " "another program with --stdout.") else: http = namedpipe = record = None if args.player_fifo: try: namedpipe = NamedPipe() except OSError as err: console.exit(f"Failed to create pipe: {err}") elif args.player_http: http = create_http_server() if args.record: record = check_file_output( formatter.path(args.record, args.fs_safe_rules), args.force) log.info(f"Starting player: {args.player}") out = PlayerOutput( args.player, args=args.player_args, quiet=not args.verbose_player, kill=not args.player_no_close, namedpipe=namedpipe, http=http, record=record, title=formatter.title(args.title, defaults=DEFAULT_STREAM_METADATA) if args.title else args.url) return out
def output_stream_http(plugin: Plugin, initial_streams: Streams, formatter: Formatter, external=False, port=0): """Continuously output the stream over HTTP.""" global output if not external: if not args.player: console.exit("The default player (VLC) does not seem to be " "installed. You must specify the path to a player " "executable with --player.") server = create_http_server() player = output = PlayerOutput( args.player, args=args.player_args, filename=server.url, quiet=not args.verbose_player, title=formatter.title(args.title, defaults=DEFAULT_STREAM_METADATA) if args.title else args.url) try: log.info(f"Starting player: {args.player}") if player: player.open() except OSError as err: console.exit(f"Failed to start player: {args.player} ({err})") else: server = create_http_server(host=None, port=port) player = None log.info("Starting server, access with one of:") for url in server.urls: log.info(" " + url) for req in iter_http_requests(server, player): user_agent = req.headers.get("User-Agent") or "unknown player" log.info(f"Got HTTP request from {user_agent}") stream_fd = prebuffer = None while not stream_fd and (not player or player.running): try: streams = initial_streams or fetch_streams(plugin) initial_streams = None for stream_name in (resolve_stream_name(streams, s) for s in args.stream): if stream_name in streams: stream = streams[stream_name] break else: log.info( "Stream not available, will re-fetch streams in 10 sec" ) sleep(10) continue except PluginError as err: log.error(f"Unable to fetch new streams: {err}") continue try: log.info( f"Opening stream: {stream_name} ({type(stream).shortname()})" ) stream_fd, prebuffer = open_stream(stream) except StreamError as err: log.error(err) if stream_fd and prebuffer: log.debug("Writing stream to player") read_stream(stream_fd, server, prebuffer, formatter) server.close(True) player.close() server.close()