2013-01-31 00:06:27 +01:00
|
|
|
import errno
|
2012-08-23 22:46:06 +02:00
|
|
|
import os
|
2017-12-13 19:12:03 +01:00
|
|
|
import platform
|
2013-08-31 14:24:12 +02:00
|
|
|
import requests
|
2012-08-23 22:46:06 +02:00
|
|
|
import sys
|
2013-06-09 07:50:22 +02:00
|
|
|
import signal
|
2013-12-16 01:10:42 +01:00
|
|
|
import webbrowser
|
2012-08-15 19:49:54 +02:00
|
|
|
|
2013-11-15 06:27:08 +01:00
|
|
|
from contextlib import closing
|
2013-08-31 14:24:12 +02:00
|
|
|
from distutils.version import StrictVersion
|
2014-08-12 21:10:20 +02:00
|
|
|
from functools import partial
|
|
|
|
from itertools import chain
|
2017-12-13 19:12:03 +01:00
|
|
|
from socks import __version__ as socks_version
|
2014-08-12 21:10:20 +02:00
|
|
|
from time import sleep
|
2017-12-13 19:12:03 +01:00
|
|
|
from websocket import __version__ as websocket_version
|
2013-09-19 22:52:34 +02:00
|
|
|
|
2017-12-13 19:12:03 +01:00
|
|
|
from streamlink import __version__ as streamlink_version
|
2016-09-19 21:46:06 +02:00
|
|
|
from streamlink import (Streamlink, StreamError, PluginError,
|
2017-01-16 18:57:12 +01:00
|
|
|
NoPluginError)
|
2016-09-19 21:46:06 +02:00
|
|
|
from streamlink.cache import Cache
|
|
|
|
from streamlink.stream import StreamProcess
|
|
|
|
from streamlink.plugins.twitch import TWITCH_CLIENT_ID
|
2013-08-08 15:01:32 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
from .argparser import parser
|
2013-03-22 18:12:24 +01:00
|
|
|
from .compat import stdout, is_win32
|
2013-03-19 04:44:41 +01:00
|
|
|
from .console import ConsoleOutput
|
2014-06-04 23:20:33 +02:00
|
|
|
from .constants import CONFIG_FILES, PLUGINS_DIR, STREAM_SYNONYMS
|
2013-03-19 04:44:41 +01:00
|
|
|
from .output import FileOutput, PlayerOutput
|
2014-08-12 21:10:20 +02:00
|
|
|
from .utils import NamedPipe, HTTPServer, ignored, progress, stream_to_url
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
ACCEPTABLE_ERRNO = (errno.EPIPE, errno.EINVAL, errno.ECONNRESET)
|
2017-11-07 16:47:08 +01:00
|
|
|
try:
|
|
|
|
ACCEPTABLE_ERRNO += (errno.WSAECONNABORTED,)
|
|
|
|
except AttributeError:
|
|
|
|
pass # Not windows
|
2014-06-03 17:56:05 +02:00
|
|
|
QUIET_OPTIONS = ("json", "stream_url", "subprocess_cmdline", "quiet")
|
2013-03-15 21:41:40 +01:00
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
args = console = streamlink = plugin = stream_fd = output = None
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2012-08-15 19:49:54 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
def check_file_output(filename, force):
|
|
|
|
"""Checks if file already exists and ask the user if it should
|
|
|
|
be overwritten if it does."""
|
2013-01-01 04:34:41 +01:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
console.logger.debug("Checking file output")
|
2012-08-15 22:26:05 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
if os.path.isfile(filename) and not force:
|
2013-08-08 15:01:32 +02:00
|
|
|
answer = console.ask("File {0} already exists! Overwrite it? [y/N] ",
|
|
|
|
filename)
|
2012-05-25 17:26:11 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
if answer.lower() != "y":
|
|
|
|
sys.exit()
|
2013-01-31 03:25:26 +01:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
return FileOutput(filename)
|
2012-05-25 17:26:11 +02:00
|
|
|
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def create_output():
|
2013-03-19 04:44:41 +01:00
|
|
|
"""Decides where to write the stream.
|
2012-05-25 17:26:11 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
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
|
2012-05-25 17:26:11 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
"""
|
2012-05-26 21:44:15 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
if args.output:
|
|
|
|
if args.output == "-":
|
|
|
|
out = FileOutput(fd=stdout)
|
|
|
|
else:
|
|
|
|
out = check_file_output(args.output, args.force)
|
|
|
|
elif args.stdout:
|
|
|
|
out = FileOutput(fd=stdout)
|
|
|
|
else:
|
2013-09-19 22:52:34 +02:00
|
|
|
http = namedpipe = None
|
2013-07-02 12:59:00 +02:00
|
|
|
|
2013-12-13 16:04:12 +01:00
|
|
|
if not args.player:
|
2013-08-08 15:01:32 +02:00
|
|
|
console.exit("The default player (VLC) does not seem to be "
|
|
|
|
"installed. You must specify the path to a player "
|
|
|
|
"executable with --player.")
|
2013-03-19 17:29:08 +01:00
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
if args.player_fifo:
|
2016-09-19 21:46:06 +02:00
|
|
|
pipename = "streamlinkpipe-{0}".format(os.getpid())
|
2013-03-19 04:44:41 +01:00
|
|
|
console.logger.info("Creating pipe {0}", pipename)
|
2012-05-27 00:51:00 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
try:
|
|
|
|
namedpipe = NamedPipe(pipename)
|
|
|
|
except IOError as err:
|
|
|
|
console.exit("Failed to create pipe: {0}", err)
|
2013-09-19 22:52:34 +02:00
|
|
|
elif args.player_http:
|
|
|
|
http = create_http_server()
|
2012-05-27 00:51:00 +02:00
|
|
|
|
2013-12-13 16:04:12 +01:00
|
|
|
console.logger.info("Starting player: {0}", args.player)
|
|
|
|
out = PlayerOutput(args.player, args=args.player_args,
|
2013-09-19 22:52:34 +02:00
|
|
|
quiet=not args.verbose_player,
|
2013-10-04 12:15:14 +02:00
|
|
|
kill=not args.player_no_close,
|
2013-09-19 22:52:34 +02:00
|
|
|
namedpipe=namedpipe, http=http)
|
2012-05-26 21:44:15 +02:00
|
|
|
|
|
|
|
return out
|
2012-05-25 17:26:11 +02:00
|
|
|
|
2012-05-28 01:11:52 +02:00
|
|
|
|
2015-01-25 18:45:19 +01:00
|
|
|
def create_http_server(host=None, port=0):
|
2015-01-25 19:04:30 +01:00
|
|
|
"""Creates a HTTP server listening on a given host and port.
|
|
|
|
|
|
|
|
If host is empty, listen on all available interfaces, and if port is 0,
|
|
|
|
listen on a random high port.
|
|
|
|
"""
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
http = HTTPServer()
|
2015-01-24 22:13:19 +01:00
|
|
|
http.bind(host=host, port=port)
|
2013-09-19 22:52:34 +02:00
|
|
|
except OSError as err:
|
|
|
|
console.exit("Failed to create HTTP server: {0}", err)
|
|
|
|
|
|
|
|
return http
|
|
|
|
|
|
|
|
|
|
|
|
def iter_http_requests(server, player):
|
2015-01-25 19:04:30 +01:00
|
|
|
"""Repeatedly accept HTTP connections on a server.
|
|
|
|
|
|
|
|
Forever if the serving externally, or while a player is running if it is not
|
|
|
|
empty.
|
|
|
|
"""
|
2013-09-19 22:52:34 +02:00
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
while not player or player.running:
|
2013-09-19 22:52:34 +02:00
|
|
|
try:
|
|
|
|
yield server.open(timeout=2.5)
|
|
|
|
except OSError:
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
def output_stream_http(plugin, initial_streams, external=False, port=0):
|
2013-09-19 22:52:34 +02:00
|
|
|
"""Continuously output the stream over HTTP."""
|
2015-08-02 13:01:46 +02:00
|
|
|
global output
|
2013-09-19 22:52:34 +02:00
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
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.")
|
2013-12-09 16:17:20 +01:00
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
server = create_http_server()
|
2015-08-02 13:01:46 +02:00
|
|
|
player = output = PlayerOutput(args.player, args=args.player_args,
|
|
|
|
filename=server.url,
|
|
|
|
quiet=not args.verbose_player)
|
2013-12-09 16:17:20 +01:00
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
try:
|
|
|
|
console.logger.info("Starting player: {0}", args.player)
|
|
|
|
if player:
|
|
|
|
player.open()
|
|
|
|
except OSError as err:
|
|
|
|
console.exit("Failed to start player: {0} ({1})",
|
|
|
|
args.player, err)
|
|
|
|
else:
|
|
|
|
server = create_http_server(host=None, port=port)
|
|
|
|
player = None
|
2013-09-19 22:52:34 +02:00
|
|
|
|
2015-01-24 22:13:19 +01:00
|
|
|
console.logger.info("Starting server, access with one of:")
|
|
|
|
for url in server.urls:
|
|
|
|
console.logger.info(" " + url)
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
for req in iter_http_requests(server, player):
|
|
|
|
user_agent = req.headers.get("User-Agent") or "unknown player"
|
|
|
|
console.logger.info("Got HTTP request from {0}".format(user_agent))
|
|
|
|
|
2015-02-06 20:27:12 +01:00
|
|
|
stream_fd = prebuffer = None
|
2015-01-24 22:13:19 +01:00
|
|
|
while not stream_fd and (not player or player.running):
|
2013-09-19 22:52:34 +02:00
|
|
|
try:
|
2014-11-23 12:10:55 +01:00
|
|
|
streams = initial_streams or fetch_streams(plugin)
|
|
|
|
initial_streams = None
|
2013-11-03 23:04:47 +01:00
|
|
|
|
2014-11-23 12:10:55 +01:00
|
|
|
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:
|
|
|
|
console.logger.info("Stream not available, will re-fetch "
|
|
|
|
"streams in 10 sec")
|
|
|
|
sleep(10)
|
|
|
|
continue
|
2013-09-19 22:52:34 +02:00
|
|
|
except PluginError as err:
|
2014-06-27 15:52:31 +02:00
|
|
|
console.logger.error(u"Unable to fetch new streams: {0}", err)
|
2013-09-19 22:52:34 +02:00
|
|
|
continue
|
|
|
|
|
|
|
|
try:
|
2014-04-18 12:38:18 +02:00
|
|
|
console.logger.info("Opening stream: {0} ({1})", stream_name,
|
|
|
|
type(stream).shortname())
|
2013-09-19 22:52:34 +02:00
|
|
|
stream_fd, prebuffer = open_stream(stream)
|
|
|
|
except StreamError as err:
|
|
|
|
console.logger.error("{0}", err)
|
|
|
|
|
2015-02-06 20:27:12 +01:00
|
|
|
if stream_fd and prebuffer:
|
|
|
|
console.logger.debug("Writing stream to player")
|
|
|
|
read_stream(stream_fd, server, prebuffer)
|
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
server.close(True)
|
|
|
|
|
|
|
|
player.close()
|
2013-12-08 04:39:22 +01:00
|
|
|
server.close()
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
def output_stream_passthrough(stream):
|
|
|
|
"""Prepares a filename to be passed to the player."""
|
2015-08-02 13:01:46 +02:00
|
|
|
global output
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
filename = '"{0}"'.format(stream_to_url(stream))
|
2015-08-02 13:01:46 +02:00
|
|
|
output = PlayerOutput(args.player, args=args.player_args,
|
|
|
|
filename=filename, call=True,
|
|
|
|
quiet=not args.verbose_player)
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
try:
|
2013-12-13 16:04:12 +01:00
|
|
|
console.logger.info("Starting player: {0}", args.player)
|
2015-08-02 13:01:46 +02:00
|
|
|
output.open()
|
2013-09-19 22:52:34 +02:00
|
|
|
except OSError as err:
|
2013-12-13 16:04:12 +01:00
|
|
|
console.exit("Failed to start player: {0} ({1})", args.player, err)
|
2013-09-19 22:52:34 +02:00
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def open_stream(stream):
|
|
|
|
"""Opens a stream and reads 8192 bytes from it.
|
|
|
|
|
|
|
|
This is useful to check if a stream actually has data
|
|
|
|
before opening the output.
|
|
|
|
|
|
|
|
"""
|
2014-07-25 18:11:48 +02:00
|
|
|
global stream_fd
|
2012-08-15 19:49:54 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
# Attempts to open the stream
|
2012-05-28 01:11:52 +02:00
|
|
|
try:
|
2013-09-19 22:52:34 +02:00
|
|
|
stream_fd = stream.open()
|
2012-08-23 22:46:06 +02:00
|
|
|
except StreamError as err:
|
2013-09-19 22:52:34 +02:00
|
|
|
raise StreamError("Could not open stream: {0}".format(err))
|
2012-05-28 01:11:52 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
# Read 8192 bytes before proceeding to check for errors.
|
|
|
|
# This is to avoid opening the output unnecessarily.
|
2012-08-15 14:57:58 +02:00
|
|
|
try:
|
2013-03-19 04:44:41 +01:00
|
|
|
console.logger.debug("Pre-buffering 8192 bytes")
|
2013-09-19 22:52:34 +02:00
|
|
|
prebuffer = stream_fd.read(8192)
|
2012-11-22 11:43:32 +01:00
|
|
|
except IOError as err:
|
2013-09-19 22:52:34 +02:00
|
|
|
raise StreamError("Failed to read data from stream: {0}".format(err))
|
|
|
|
|
|
|
|
if not prebuffer:
|
|
|
|
raise StreamError("No data returned from stream")
|
|
|
|
|
|
|
|
return stream_fd, prebuffer
|
2012-08-16 18:08:03 +02:00
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
|
|
|
|
def output_stream(stream):
|
|
|
|
"""Open stream, create output and finally write the stream to output."""
|
2015-08-02 13:01:46 +02:00
|
|
|
global output
|
2013-09-19 22:52:34 +02:00
|
|
|
|
2017-04-13 17:21:07 +02:00
|
|
|
success_open = False
|
2014-03-20 22:06:51 +01:00
|
|
|
for i in range(args.retry_open):
|
|
|
|
try:
|
|
|
|
stream_fd, prebuffer = open_stream(stream)
|
2017-04-13 17:21:07 +02:00
|
|
|
success_open = True
|
2014-03-20 22:06:51 +01:00
|
|
|
break
|
|
|
|
except StreamError as err:
|
2018-01-16 03:49:52 +01:00
|
|
|
console.logger.error("Try {0}/{1}: Could not open stream {2} ({3})", i + 1, args.retry_open, stream, err)
|
2017-04-13 17:21:07 +02:00
|
|
|
|
|
|
|
if not success_open:
|
|
|
|
console.exit("Could not open stream {0}, tried {1} times, exiting", stream, args.retry_open)
|
2012-08-15 14:57:58 +02:00
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
output = create_output()
|
2012-06-14 01:03:44 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
try:
|
|
|
|
output.open()
|
2013-09-19 22:52:34 +02:00
|
|
|
except (IOError, OSError) as err:
|
|
|
|
if isinstance(output, PlayerOutput):
|
|
|
|
console.exit("Failed to start player: {0} ({1})",
|
|
|
|
args.player, err)
|
|
|
|
else:
|
|
|
|
console.exit("Failed to open output: {0} ({1})",
|
|
|
|
args.output, err)
|
2012-06-22 00:07:06 +02:00
|
|
|
|
2013-11-15 06:27:08 +01:00
|
|
|
with closing(output):
|
|
|
|
console.logger.debug("Writing stream to output")
|
|
|
|
read_stream(stream_fd, output, prebuffer)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
def read_stream(stream, output, prebuffer, chunk_size=8192):
|
2013-03-19 04:44:41 +01:00
|
|
|
"""Reads data from stream and then writes it to the output."""
|
|
|
|
is_player = isinstance(output, PlayerOutput)
|
2013-09-19 22:52:34 +02:00
|
|
|
is_http = isinstance(output, HTTPServer)
|
2013-03-22 18:12:24 +01:00
|
|
|
is_fifo = is_player and output.namedpipe
|
2013-03-19 04:44:41 +01:00
|
|
|
show_progress = isinstance(output, FileOutput) and output.fd is not stdout
|
2012-08-15 21:05:35 +02:00
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
stream_iterator = chain(
|
|
|
|
[prebuffer],
|
|
|
|
iter(partial(stream.read, chunk_size), b"")
|
|
|
|
)
|
|
|
|
if show_progress:
|
|
|
|
stream_iterator = progress(stream_iterator,
|
|
|
|
prefix=os.path.basename(args.output))
|
2013-03-22 18:12:24 +01:00
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
try:
|
|
|
|
for data in stream_iterator:
|
|
|
|
# We need to check if the player process still exists when
|
|
|
|
# using named pipes on Windows since the named pipe is not
|
|
|
|
# automatically closed by the player.
|
|
|
|
if is_win32 and is_fifo:
|
|
|
|
output.player.poll()
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
if output.player.returncode is not None:
|
|
|
|
console.logger.info("Player closed")
|
|
|
|
break
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
try:
|
|
|
|
output.write(data)
|
|
|
|
except IOError as err:
|
|
|
|
if is_player and err.errno in ACCEPTABLE_ERRNO:
|
|
|
|
console.logger.info("Player closed")
|
|
|
|
elif is_http and err.errno in ACCEPTABLE_ERRNO:
|
|
|
|
console.logger.info("HTTP connection closed")
|
|
|
|
else:
|
2017-04-13 17:21:07 +02:00
|
|
|
console.exit("Error when writing to output: {0}, exiting", err)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2014-08-12 21:10:20 +02:00
|
|
|
break
|
|
|
|
except IOError as err:
|
2017-04-13 17:21:07 +02:00
|
|
|
console.exit("Error when reading from stream: {0}, exiting", err)
|
|
|
|
finally:
|
|
|
|
stream.close()
|
|
|
|
console.logger.info("Stream ended")
|
2012-09-18 22:38:07 +02:00
|
|
|
|
2012-11-22 11:43:32 +01:00
|
|
|
|
2013-11-03 23:04:47 +01:00
|
|
|
def handle_stream(plugin, streams, stream_name):
|
2013-03-19 04:44:41 +01:00
|
|
|
"""Decides what to do with the selected stream.
|
|
|
|
|
|
|
|
Depending on arguments it can be one of these:
|
|
|
|
- Output internal command-line
|
|
|
|
- Output JSON represenation
|
2013-09-19 22:52:34 +02:00
|
|
|
- Continuously output the stream over HTTP
|
2013-03-19 04:44:41 +01:00
|
|
|
- Output stream data to selected output
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2013-11-03 23:04:47 +01:00
|
|
|
stream_name = resolve_stream_name(streams, stream_name)
|
2013-09-19 22:52:34 +02:00
|
|
|
stream = streams[stream_name]
|
2012-11-22 11:43:32 +01:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
# Print internal command-line if this stream
|
|
|
|
# uses a subprocess.
|
2014-04-18 18:21:43 +02:00
|
|
|
if args.subprocess_cmdline:
|
2012-11-22 11:43:32 +01:00
|
|
|
if isinstance(stream, StreamProcess):
|
|
|
|
try:
|
|
|
|
cmdline = stream.cmdline()
|
|
|
|
except StreamError as err:
|
2013-03-19 04:44:41 +01:00
|
|
|
console.exit("{0}", err)
|
2012-11-22 11:43:32 +01:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
console.msg("{0}", cmdline)
|
2012-11-22 11:43:32 +01:00
|
|
|
else:
|
2014-06-03 17:56:05 +02:00
|
|
|
console.exit("The stream specified cannot be translated to a command")
|
2013-03-19 04:44:41 +01:00
|
|
|
|
|
|
|
# Print JSON representation of the stream
|
|
|
|
elif console.json:
|
|
|
|
console.msg_json(stream)
|
|
|
|
|
2014-06-03 17:56:05 +02:00
|
|
|
elif args.stream_url:
|
2017-02-10 13:09:09 +01:00
|
|
|
try:
|
|
|
|
console.msg("{0}", stream.to_url())
|
|
|
|
except TypeError:
|
2014-06-03 17:56:05 +02:00
|
|
|
console.exit("The stream specified cannot be translated to a URL")
|
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
# Output the stream
|
2012-11-22 11:43:32 +01:00
|
|
|
else:
|
2013-03-19 04:44:41 +01:00
|
|
|
# Find any streams with a '_alt' suffix and attempt
|
|
|
|
# to use these in case the main stream is not usable.
|
2013-09-19 22:52:34 +02:00
|
|
|
alt_streams = list(filter(lambda k: stream_name + "_alt" in k,
|
|
|
|
sorted(streams.keys())))
|
2014-04-17 22:02:33 +02:00
|
|
|
file_output = args.output or args.stdout
|
2012-11-22 11:43:32 +01:00
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
for stream_name in [stream_name] + alt_streams:
|
|
|
|
stream = streams[stream_name]
|
|
|
|
stream_type = type(stream).shortname()
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2014-04-17 22:02:33 +02:00
|
|
|
if stream_type in args.player_passthrough and not file_output:
|
2014-04-18 12:38:18 +02:00
|
|
|
console.logger.info("Opening stream: {0} ({1})", stream_name,
|
|
|
|
stream_type)
|
2013-09-19 22:52:34 +02:00
|
|
|
success = output_stream_passthrough(stream)
|
2015-01-24 22:13:19 +01:00
|
|
|
elif args.player_external_http:
|
|
|
|
return output_stream_http(plugin, streams, external=True,
|
2015-01-25 18:45:19 +01:00
|
|
|
port=args.player_external_http_port)
|
2014-04-17 22:02:33 +02:00
|
|
|
elif args.player_continuous_http and not file_output:
|
|
|
|
return output_stream_http(plugin, streams)
|
2013-09-19 22:52:34 +02:00
|
|
|
else:
|
2014-04-18 12:38:18 +02:00
|
|
|
console.logger.info("Opening stream: {0} ({1})", stream_name,
|
|
|
|
stream_type)
|
2013-09-19 22:52:34 +02:00
|
|
|
success = output_stream(stream)
|
2012-11-22 11:43:32 +01:00
|
|
|
|
2012-12-30 01:11:48 +01:00
|
|
|
if success:
|
2012-11-22 11:43:32 +01:00
|
|
|
break
|
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
def fetch_streams(plugin):
|
|
|
|
"""Fetches streams using correct parameters."""
|
|
|
|
|
|
|
|
return plugin.get_streams(stream_types=args.stream_types,
|
|
|
|
sorting_excludes=args.stream_sorting_excludes)
|
|
|
|
|
|
|
|
|
2017-12-14 05:14:49 +01:00
|
|
|
def fetch_streams_with_retry(plugin, interval, count):
|
|
|
|
"""Attempts to fetch streams repeatedly
|
|
|
|
until some are returned or limit hit."""
|
2014-03-20 22:06:51 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
streams = fetch_streams(plugin)
|
|
|
|
except PluginError as err:
|
2014-06-27 15:52:31 +02:00
|
|
|
console.logger.error(u"{0}", err)
|
2014-03-20 22:06:51 +01:00
|
|
|
streams = None
|
|
|
|
|
|
|
|
if not streams:
|
|
|
|
console.logger.info("Waiting for streams, retrying every {0} "
|
2016-11-04 18:19:03 +01:00
|
|
|
"second(s)", interval)
|
2017-12-14 05:14:49 +01:00
|
|
|
attempts = 0
|
|
|
|
|
2014-03-20 22:06:51 +01:00
|
|
|
while not streams:
|
2016-11-04 18:19:03 +01:00
|
|
|
sleep(interval)
|
2014-03-20 22:06:51 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
streams = fetch_streams(plugin)
|
|
|
|
except PluginError as err:
|
2014-06-27 15:52:31 +02:00
|
|
|
console.logger.error(u"{0}", err)
|
2014-03-20 22:06:51 +01:00
|
|
|
|
2017-12-14 05:14:49 +01:00
|
|
|
if count > 0:
|
|
|
|
attempts += 1
|
|
|
|
if attempts >= count:
|
|
|
|
break
|
|
|
|
|
2014-03-20 22:06:51 +01:00
|
|
|
return streams
|
|
|
|
|
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
def resolve_stream_name(streams, stream_name):
|
|
|
|
"""Returns the real stream name of a synonym."""
|
|
|
|
|
2014-08-13 15:31:24 +02:00
|
|
|
if stream_name in STREAM_SYNONYMS and stream_name in streams:
|
2013-09-19 22:52:34 +02:00
|
|
|
for name, stream in streams.items():
|
|
|
|
if stream is streams[stream_name] and name not in STREAM_SYNONYMS:
|
|
|
|
return name
|
|
|
|
|
|
|
|
return stream_name
|
|
|
|
|
|
|
|
|
2015-07-30 11:40:40 +02:00
|
|
|
def format_valid_streams(plugin, streams):
|
2013-03-19 04:44:41 +01:00
|
|
|
"""Formats a dict of streams.
|
2012-08-15 19:49:54 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
Filters out synonyms and displays them next to
|
|
|
|
the stream they point to.
|
|
|
|
|
2015-07-30 11:40:40 +02:00
|
|
|
Streams are sorted according to their quality
|
|
|
|
(based on plugin.stream_weight).
|
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
"""
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
delimiter = ", "
|
2012-12-31 22:58:34 +01:00
|
|
|
validstreams = []
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2015-08-03 02:41:54 +02:00
|
|
|
for name, stream in sorted(streams.items(),
|
|
|
|
key=lambda stream: plugin.stream_weight(stream[0])):
|
2013-03-19 04:44:41 +01:00
|
|
|
if name in STREAM_SYNONYMS:
|
2012-12-31 22:58:34 +01:00
|
|
|
continue
|
|
|
|
|
2017-01-16 18:57:12 +01:00
|
|
|
def synonymfilter(n):
|
|
|
|
return stream is streams[n] and n is not name
|
2012-12-31 22:58:34 +01:00
|
|
|
synonyms = list(filter(synonymfilter, streams.keys()))
|
|
|
|
|
|
|
|
if len(synonyms) > 0:
|
2013-03-19 04:44:41 +01:00
|
|
|
joined = delimiter.join(synonyms)
|
2012-12-31 22:58:34 +01:00
|
|
|
name = "{0} ({1})".format(name, joined)
|
|
|
|
|
2015-08-03 02:41:54 +02:00
|
|
|
validstreams.append(name)
|
2012-12-31 22:58:34 +01:00
|
|
|
|
2015-08-03 02:41:54 +02:00
|
|
|
return delimiter.join(validstreams)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def handle_url():
|
2013-03-19 04:44:41 +01:00
|
|
|
"""The URL handler.
|
|
|
|
|
|
|
|
Attempts to resolve the URL to a plugin and then attempts
|
|
|
|
to fetch a list of available streams.
|
|
|
|
|
|
|
|
Proceeds to handle stream if user specified a valid one,
|
|
|
|
otherwise output list of valid streams.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
try:
|
2016-09-19 21:46:06 +02:00
|
|
|
plugin = streamlink.resolve_url(args.url)
|
2013-09-19 22:52:34 +02:00
|
|
|
console.logger.info("Found matching plugin {0} for URL {1}",
|
|
|
|
plugin.module, args.url)
|
2014-03-20 22:06:51 +01:00
|
|
|
|
2017-12-14 05:14:49 +01:00
|
|
|
if args.retry_max or args.retry_streams:
|
|
|
|
retry_streams = 1
|
|
|
|
retry_max = 0
|
|
|
|
if args.retry_streams:
|
|
|
|
retry_streams = args.retry_streams
|
|
|
|
if args.retry_max:
|
|
|
|
retry_max = args.retry_max
|
|
|
|
streams = fetch_streams_with_retry(plugin, retry_streams,
|
|
|
|
retry_max)
|
2014-03-20 22:06:51 +01:00
|
|
|
else:
|
|
|
|
streams = fetch_streams(plugin)
|
2013-03-19 04:44:41 +01:00
|
|
|
except NoPluginError:
|
|
|
|
console.exit("No plugin can handle URL: {0}", args.url)
|
2013-09-19 22:52:34 +02:00
|
|
|
except PluginError as err:
|
2014-06-27 15:52:31 +02:00
|
|
|
console.exit(u"{0}", err)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2013-09-19 22:52:34 +02:00
|
|
|
if not streams:
|
2017-02-14 15:12:43 +01:00
|
|
|
console.exit("No playable streams found on this URL: {0}", args.url)
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2014-06-06 14:53:33 +02:00
|
|
|
if args.best_stream_default:
|
|
|
|
args.default_stream = ["best"]
|
|
|
|
|
|
|
|
if args.default_stream and not args.stream and not args.json:
|
|
|
|
args.stream = args.default_stream
|
2014-04-17 21:41:55 +02:00
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
if args.stream:
|
2015-07-30 11:40:40 +02:00
|
|
|
validstreams = format_valid_streams(plugin, streams)
|
2013-11-03 23:04:47 +01:00
|
|
|
for stream_name in args.stream:
|
|
|
|
if stream_name in streams:
|
2014-04-17 21:41:55 +02:00
|
|
|
console.logger.info("Available streams: {0}", validstreams)
|
2013-11-03 23:04:47 +01:00
|
|
|
handle_stream(plugin, streams, stream_name)
|
|
|
|
return
|
2013-01-01 04:34:41 +01:00
|
|
|
|
2013-11-03 23:04:47 +01:00
|
|
|
err = ("The specified stream(s) '{0}' could not be "
|
|
|
|
"found".format(", ".join(args.stream)))
|
|
|
|
|
|
|
|
if console.json:
|
|
|
|
console.msg_json(dict(streams=streams, plugin=plugin.module,
|
|
|
|
error=err))
|
|
|
|
else:
|
|
|
|
console.exit("{0}.\n Available streams: {1}",
|
|
|
|
err, validstreams)
|
2011-08-15 04:37:22 +02:00
|
|
|
else:
|
2013-03-19 04:44:41 +01:00
|
|
|
if console.json:
|
|
|
|
console.msg_json(dict(streams=streams, plugin=plugin.module))
|
2013-01-01 04:34:41 +01:00
|
|
|
else:
|
2015-07-30 11:40:40 +02:00
|
|
|
validstreams = format_valid_streams(plugin, streams)
|
2013-09-19 22:52:34 +02:00
|
|
|
console.msg("Available streams: {0}", validstreams)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def print_plugins():
|
2016-09-19 21:46:06 +02:00
|
|
|
"""Outputs a list of all plugins Streamlink has loaded."""
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
pluginlist = list(streamlink.get_plugins().keys())
|
2013-07-17 01:44:42 +02:00
|
|
|
pluginlist_formatted = ", ".join(sorted(pluginlist))
|
2013-03-19 04:44:41 +01:00
|
|
|
|
|
|
|
if console.json:
|
|
|
|
console.msg_json(pluginlist)
|
2013-01-01 04:34:41 +01:00
|
|
|
else:
|
2013-07-17 01:44:42 +02:00
|
|
|
console.msg("Loaded plugins: {0}", pluginlist_formatted)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2013-12-16 01:10:42 +01:00
|
|
|
def authenticate_twitch_oauth():
|
2016-09-19 21:46:06 +02:00
|
|
|
"""Opens a web browser to allow the user to grant Streamlink
|
2013-12-16 01:10:42 +01:00
|
|
|
access to their Twitch account."""
|
|
|
|
|
2016-09-16 20:23:31 +02:00
|
|
|
client_id = TWITCH_CLIENT_ID
|
2016-11-22 02:52:24 +01:00
|
|
|
redirect_uri = "https://streamlink.github.io/twitch_oauth.html"
|
2013-12-16 01:10:42 +01:00
|
|
|
url = ("https://api.twitch.tv/kraken/oauth2/authorize/"
|
|
|
|
"?response_type=token&client_id={0}&redirect_uri="
|
2015-03-06 15:07:40 +01:00
|
|
|
"{1}&scope=user_read+user_subscriptions").format(client_id, redirect_uri)
|
2013-12-16 01:10:42 +01:00
|
|
|
|
|
|
|
console.msg("Attempting to open a browser to let you authenticate "
|
2016-09-19 21:46:06 +02:00
|
|
|
"Streamlink with Twitch")
|
2013-12-16 01:10:42 +01:00
|
|
|
|
|
|
|
try:
|
|
|
|
if not webbrowser.open_new_tab(url):
|
|
|
|
raise webbrowser.Error
|
|
|
|
except webbrowser.Error:
|
|
|
|
console.exit("Unable to open a web browser, try accessing this URL "
|
|
|
|
"manually instead:\n{0}".format(url))
|
|
|
|
|
|
|
|
|
2012-09-29 22:04:44 +02:00
|
|
|
def load_plugins(dirs):
|
2013-03-19 04:44:41 +01:00
|
|
|
"""Attempts to load plugins from a list of directories."""
|
|
|
|
|
2013-08-08 15:01:32 +02:00
|
|
|
dirs = [os.path.expanduser(d) for d in dirs]
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2012-09-29 22:04:44 +02:00
|
|
|
for directory in dirs:
|
|
|
|
if os.path.isdir(directory):
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.load_plugins(directory)
|
2012-09-29 22:04:44 +02:00
|
|
|
else:
|
2013-08-08 15:01:32 +02:00
|
|
|
console.logger.warning("Plugin path {0} does not exist or is not "
|
|
|
|
"a directory!", directory)
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
|
2014-06-04 23:20:33 +02:00
|
|
|
def setup_args(config_files=[]):
|
2013-07-17 01:44:42 +02:00
|
|
|
"""Parses arguments."""
|
|
|
|
global args
|
|
|
|
arglist = sys.argv[1:]
|
|
|
|
|
2014-06-04 23:20:33 +02:00
|
|
|
# Load arguments from config files
|
|
|
|
for config_file in filter(os.path.isfile, config_files):
|
|
|
|
arglist.insert(0, "@" + config_file)
|
2013-07-17 01:44:42 +02:00
|
|
|
|
|
|
|
args = parser.parse_args(arglist)
|
|
|
|
|
2013-09-20 13:14:34 +02:00
|
|
|
# Force lowercase to allow case-insensitive lookup
|
|
|
|
if args.stream:
|
2013-11-03 23:04:47 +01:00
|
|
|
args.stream = [stream.lower() for stream in args.stream]
|
2013-09-20 13:14:34 +02:00
|
|
|
|
2017-04-18 15:06:03 +02:00
|
|
|
if not args.url and args.url_param:
|
|
|
|
args.url = args.url_param
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
|
2014-07-07 17:29:17 +02:00
|
|
|
def setup_config_args():
|
2014-06-05 00:28:05 +02:00
|
|
|
config_files = []
|
|
|
|
|
2014-06-04 23:20:33 +02:00
|
|
|
if args.url:
|
2014-06-06 18:34:32 +02:00
|
|
|
with ignored(NoPluginError):
|
2016-09-19 21:46:06 +02:00
|
|
|
plugin = streamlink.resolve_url(args.url)
|
2014-06-05 00:28:05 +02:00
|
|
|
config_files += ["{0}.{1}".format(fn, plugin.module) for fn in CONFIG_FILES]
|
|
|
|
|
|
|
|
if args.config:
|
|
|
|
# We want the config specified last to get highest priority
|
|
|
|
config_files += list(reversed(args.config))
|
2014-07-07 17:29:17 +02:00
|
|
|
else:
|
|
|
|
# Only load first available default config
|
|
|
|
for config_file in filter(os.path.isfile, CONFIG_FILES):
|
|
|
|
config_files.append(config_file)
|
|
|
|
break
|
2014-06-05 00:28:05 +02:00
|
|
|
|
|
|
|
if config_files:
|
|
|
|
setup_args(config_files)
|
2014-06-04 23:20:33 +02:00
|
|
|
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def setup_console():
|
|
|
|
"""Console setup."""
|
|
|
|
global console
|
|
|
|
|
|
|
|
# All console related operations is handled via the ConsoleOutput class
|
2016-09-19 21:46:06 +02:00
|
|
|
console = ConsoleOutput(sys.stdout, streamlink)
|
2013-07-17 01:44:42 +02:00
|
|
|
|
|
|
|
# Console output should be on stderr if we are outputting
|
|
|
|
# a stream to stdout.
|
|
|
|
if args.stdout or args.output == "-":
|
|
|
|
console.set_output(sys.stderr)
|
|
|
|
|
|
|
|
# We don't want log output when we are printing JSON or a command-line.
|
2014-06-03 17:56:05 +02:00
|
|
|
if not any(getattr(args, attr) for attr in QUIET_OPTIONS):
|
2013-07-17 01:44:42 +02:00
|
|
|
console.set_level(args.loglevel)
|
|
|
|
|
|
|
|
if args.quiet_player:
|
|
|
|
console.logger.warning("The option --quiet-player is deprecated since "
|
|
|
|
"version 1.4.3 as hiding player output is now "
|
|
|
|
"the default.")
|
|
|
|
|
2014-06-06 14:53:33 +02:00
|
|
|
if args.best_stream_default:
|
|
|
|
console.logger.warning("The option --best-stream-default is deprecated "
|
|
|
|
"since version 1.9.0, use '--default-stream best' "
|
|
|
|
"instead.")
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
console.json = args.json
|
|
|
|
|
|
|
|
# Handle SIGTERM just like SIGINT
|
|
|
|
signal.signal(signal.SIGTERM, signal.default_int_handler)
|
|
|
|
|
|
|
|
|
2014-03-14 21:02:34 +01:00
|
|
|
def setup_http_session():
|
|
|
|
"""Sets the global HTTP settings, such as proxy and headers."""
|
2013-10-26 02:36:16 +02:00
|
|
|
if args.http_proxy:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-proxy", args.http_proxy)
|
2013-10-26 02:36:16 +02:00
|
|
|
|
|
|
|
if args.https_proxy:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("https-proxy", args.https_proxy)
|
2013-10-26 02:36:16 +02:00
|
|
|
|
2014-12-06 17:07:14 +01:00
|
|
|
if args.http_cookie:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-cookies", dict(args.http_cookie))
|
2014-03-14 21:02:34 +01:00
|
|
|
|
2014-12-06 17:07:14 +01:00
|
|
|
if args.http_header:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-headers", dict(args.http_header))
|
2014-03-14 21:02:34 +01:00
|
|
|
|
2014-12-06 17:07:14 +01:00
|
|
|
if args.http_query_param:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-query-params", dict(args.http_query_param))
|
2014-03-14 21:02:34 +01:00
|
|
|
|
2014-03-20 01:33:06 +01:00
|
|
|
if args.http_ignore_env:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-trust-env", False)
|
2014-03-20 01:33:06 +01:00
|
|
|
|
2014-03-14 21:02:34 +01:00
|
|
|
if args.http_no_ssl_verify:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-ssl-verify", False)
|
2014-03-14 21:02:34 +01:00
|
|
|
|
2017-01-19 10:33:34 +01:00
|
|
|
if args.http_disable_dh:
|
|
|
|
streamlink.set_option("http-disable-dh", True)
|
|
|
|
|
2014-03-14 21:02:34 +01:00
|
|
|
if args.http_ssl_cert:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-ssl-cert", args.http_ssl_cert)
|
2014-03-14 21:02:34 +01:00
|
|
|
|
|
|
|
if args.http_ssl_cert_crt_key:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-ssl-cert", tuple(args.http_ssl_cert_crt_key))
|
2014-03-14 21:02:34 +01:00
|
|
|
|
2014-04-20 17:07:16 +02:00
|
|
|
if args.http_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-timeout", args.http_timeout)
|
2013-10-26 02:36:16 +02:00
|
|
|
|
2014-12-06 17:07:14 +01:00
|
|
|
if args.http_cookies:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-cookies", args.http_cookies)
|
2014-12-06 17:07:14 +01:00
|
|
|
|
|
|
|
if args.http_headers:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-headers", args.http_headers)
|
2014-12-06 17:07:14 +01:00
|
|
|
|
|
|
|
if args.http_query_params:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-query-params", args.http_query_params)
|
2014-12-06 17:07:14 +01:00
|
|
|
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def setup_plugins():
|
2013-09-19 22:52:34 +02:00
|
|
|
"""Loads any additional plugins."""
|
2013-07-17 01:44:42 +02:00
|
|
|
if os.path.isdir(PLUGINS_DIR):
|
2013-08-08 15:01:32 +02:00
|
|
|
load_plugins([PLUGINS_DIR])
|
2013-07-17 01:44:42 +02:00
|
|
|
|
|
|
|
if args.plugin_dirs:
|
|
|
|
load_plugins(args.plugin_dirs)
|
|
|
|
|
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
def setup_streamlink():
|
|
|
|
"""Creates the Streamlink session."""
|
|
|
|
global streamlink
|
2013-07-17 01:44:42 +02:00
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink = Streamlink()
|
2013-07-17 01:44:42 +02:00
|
|
|
|
2013-03-19 04:44:41 +01:00
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
def setup_options():
|
2016-09-19 21:46:06 +02:00
|
|
|
"""Sets Streamlink options."""
|
2014-04-18 14:20:45 +02:00
|
|
|
if args.hls_live_edge:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hls-live-edge", args.hls_live_edge)
|
2014-04-18 14:20:45 +02:00
|
|
|
|
|
|
|
if args.hls_segment_attempts:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hls-segment-attempts", args.hls_segment_attempts)
|
2014-04-18 14:20:45 +02:00
|
|
|
|
2017-02-03 18:26:56 +01:00
|
|
|
if args.hls_playlist_reload_attempts:
|
|
|
|
streamlink.set_option("hls-playlist-reload-attempts", args.hls_playlist_reload_attempts)
|
|
|
|
|
2014-07-25 00:31:47 +02:00
|
|
|
if args.hls_segment_threads:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hls-segment-threads", args.hls_segment_threads)
|
2014-07-25 00:31:47 +02:00
|
|
|
|
2014-04-18 14:20:45 +02:00
|
|
|
if args.hls_segment_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hls-segment-timeout", args.hls_segment_timeout)
|
2014-04-18 14:20:45 +02:00
|
|
|
|
2018-01-13 23:44:18 +01:00
|
|
|
if args.hls_segment_ignore_names:
|
|
|
|
streamlink.set_option("hls-segment-ignore-names", args.hls_segment_ignore_names)
|
|
|
|
|
2014-04-18 14:20:45 +02:00
|
|
|
if args.hls_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hls-timeout", args.hls_timeout)
|
2014-04-18 14:20:45 +02:00
|
|
|
|
2017-03-09 14:12:20 +01:00
|
|
|
if args.hls_audio_select:
|
|
|
|
streamlink.set_option("hls-audio-select", args.hls_audio_select)
|
|
|
|
|
2017-07-26 14:08:12 +02:00
|
|
|
if args.hls_start_offset:
|
|
|
|
streamlink.set_option("hls-start-offset", args.hls_start_offset)
|
2017-02-24 12:05:43 +01:00
|
|
|
|
2017-07-26 14:08:12 +02:00
|
|
|
if args.hls_duration:
|
|
|
|
streamlink.set_option("hls-duration", args.hls_duration)
|
2017-02-24 12:05:43 +01:00
|
|
|
|
2017-05-19 20:15:01 +02:00
|
|
|
if args.hls_live_restart:
|
|
|
|
streamlink.set_option("hls-live-restart", args.hls_live_restart)
|
|
|
|
|
2014-04-19 01:12:24 +02:00
|
|
|
if args.hds_live_edge:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hds-live-edge", args.hds_live_edge)
|
2013-01-11 19:41:19 +01:00
|
|
|
|
2014-07-24 14:49:21 +02:00
|
|
|
if args.hds_segment_attempts:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hds-segment-attempts", args.hds_segment_attempts)
|
2014-07-24 14:49:21 +02:00
|
|
|
|
2014-07-25 00:31:47 +02:00
|
|
|
if args.hds_segment_threads:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hds-segment-threads", args.hds_segment_threads)
|
2014-07-25 00:31:47 +02:00
|
|
|
|
2014-07-24 14:49:21 +02:00
|
|
|
if args.hds_segment_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hds-segment-timeout", args.hds_segment_timeout)
|
2014-07-24 14:49:21 +02:00
|
|
|
|
|
|
|
if args.hds_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("hds-timeout", args.hds_timeout)
|
2014-07-24 14:49:21 +02:00
|
|
|
|
2014-04-20 17:07:16 +02:00
|
|
|
if args.http_stream_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("http-stream-timeout", args.http_stream_timeout)
|
2014-04-18 18:46:14 +02:00
|
|
|
|
2012-12-19 21:08:57 +01:00
|
|
|
if args.ringbuffer_size:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("ringbuffer-size", args.ringbuffer_size)
|
2012-12-19 21:08:57 +01:00
|
|
|
|
2014-04-18 18:21:43 +02:00
|
|
|
if args.rtmp_proxy:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("rtmp-proxy", args.rtmp_proxy)
|
2014-04-18 18:21:43 +02:00
|
|
|
|
|
|
|
if args.rtmp_rtmpdump:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("rtmp-rtmpdump", args.rtmp_rtmpdump)
|
2014-04-18 18:21:43 +02:00
|
|
|
|
|
|
|
if args.rtmp_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("rtmp-timeout", args.rtmp_timeout)
|
2014-04-18 18:21:43 +02:00
|
|
|
|
2014-07-27 15:55:10 +02:00
|
|
|
if args.stream_segment_attempts:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("stream-segment-attempts", args.stream_segment_attempts)
|
2014-07-27 15:55:10 +02:00
|
|
|
|
|
|
|
if args.stream_segment_threads:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("stream-segment-threads", args.stream_segment_threads)
|
2014-07-27 15:55:10 +02:00
|
|
|
|
|
|
|
if args.stream_segment_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("stream-segment-timeout", args.stream_segment_timeout)
|
2014-07-27 15:55:10 +02:00
|
|
|
|
|
|
|
if args.stream_timeout:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("stream-timeout", args.stream_timeout)
|
2014-07-27 15:55:10 +02:00
|
|
|
|
2017-01-10 18:44:32 +01:00
|
|
|
if args.ffmpeg_ffmpeg:
|
|
|
|
streamlink.set_option("ffmpeg-ffmpeg", args.ffmpeg_ffmpeg)
|
|
|
|
if args.ffmpeg_verbose:
|
|
|
|
streamlink.set_option("ffmpeg-verbose", args.ffmpeg_verbose)
|
|
|
|
if args.ffmpeg_verbose_path:
|
|
|
|
streamlink.set_option("ffmpeg-verbose-path", args.ffmpeg_verbose_path)
|
|
|
|
if args.ffmpeg_video_transcode:
|
|
|
|
streamlink.set_option("ffmpeg-video-transcode", args.ffmpeg_video_transcode)
|
|
|
|
if args.ffmpeg_audio_transcode:
|
|
|
|
streamlink.set_option("ffmpeg-audio-transcode", args.ffmpeg_audio_transcode)
|
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_option("subprocess-errorlog", args.subprocess_errorlog)
|
2017-01-03 16:41:56 +01:00
|
|
|
streamlink.set_option("subprocess-errorlog-path", args.subprocess_errorlog_path)
|
2017-01-20 14:02:14 +01:00
|
|
|
streamlink.set_option("locale", args.locale)
|
|
|
|
|
2014-04-19 01:12:24 +02:00
|
|
|
# Deprecated options
|
|
|
|
if args.hds_fragment_buffer:
|
|
|
|
console.logger.warning("The option --hds-fragment-buffer is deprecated "
|
|
|
|
"and will be removed in the future. Use "
|
|
|
|
"--ringbuffer-size instead")
|
2014-04-18 18:21:43 +02:00
|
|
|
|
2015-02-03 20:18:40 +01:00
|
|
|
|
2014-04-18 18:21:43 +02:00
|
|
|
def setup_plugin_options():
|
2016-09-19 21:46:06 +02:00
|
|
|
"""Sets Streamlink plugin options."""
|
2014-08-06 18:32:18 +02:00
|
|
|
if args.twitch_cookie:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("twitch", "cookie",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.twitch_cookie)
|
2013-09-28 19:19:20 +02:00
|
|
|
|
2013-12-16 01:10:42 +01:00
|
|
|
if args.twitch_oauth_token:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("twitch", "oauth_token",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.twitch_oauth_token)
|
2013-12-16 01:10:42 +01:00
|
|
|
|
2016-09-29 19:19:17 +02:00
|
|
|
if args.twitch_disable_hosting:
|
|
|
|
streamlink.set_plugin_option("twitch", "disable_hosting",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.twitch_disable_hosting)
|
2016-09-29 19:19:17 +02:00
|
|
|
|
2014-02-20 14:07:33 +01:00
|
|
|
if args.ustream_password:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("ustreamtv", "password",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.ustream_password)
|
2014-02-20 14:07:33 +01:00
|
|
|
|
2014-01-08 00:27:30 +01:00
|
|
|
if args.crunchyroll_username:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("crunchyroll", "username",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.crunchyroll_username)
|
2014-01-08 00:27:30 +01:00
|
|
|
|
2014-02-27 20:37:04 +01:00
|
|
|
if args.crunchyroll_username and not args.crunchyroll_password:
|
|
|
|
crunchyroll_password = console.askpass("Enter Crunchyroll password: ")
|
|
|
|
else:
|
|
|
|
crunchyroll_password = args.crunchyroll_password
|
|
|
|
|
2014-01-08 00:27:30 +01:00
|
|
|
if crunchyroll_password:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("crunchyroll", "password",
|
2017-01-16 18:57:12 +01:00
|
|
|
crunchyroll_password)
|
2014-01-08 00:27:30 +01:00
|
|
|
if args.crunchyroll_purge_credentials:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("crunchyroll", "purge_credentials",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.crunchyroll_purge_credentials)
|
2017-02-05 12:50:34 +01:00
|
|
|
if args.crunchyroll_session_id:
|
|
|
|
streamlink.set_plugin_option("crunchyroll", "session_id",
|
|
|
|
args.crunchyroll_session_id)
|
2014-01-08 00:27:30 +01:00
|
|
|
|
2015-07-29 07:04:45 +02:00
|
|
|
if args.crunchyroll_locale:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.set_plugin_option("crunchyroll", "locale",
|
2017-01-16 18:57:12 +01:00
|
|
|
args.crunchyroll_locale)
|
2015-07-29 07:04:45 +02:00
|
|
|
|
2017-01-09 23:11:10 +01:00
|
|
|
if args.btv_username:
|
|
|
|
streamlink.set_plugin_option("btv", "username", args.btv_username)
|
|
|
|
|
|
|
|
if args.btv_username and not args.btv_password:
|
|
|
|
btv_password = console.askpass("Enter BTV password: ")
|
|
|
|
else:
|
|
|
|
btv_password = args.btv_password
|
|
|
|
|
|
|
|
if btv_password:
|
|
|
|
streamlink.set_plugin_option("btv", "password", btv_password)
|
|
|
|
|
2017-01-09 20:28:24 +01:00
|
|
|
if args.schoolism_email:
|
|
|
|
streamlink.set_plugin_option("schoolism", "email", args.schoolism_email)
|
|
|
|
if args.schoolism_email and not args.schoolism_password:
|
|
|
|
schoolism_password = console.askpass("Enter Schoolism password: ")
|
|
|
|
else:
|
|
|
|
schoolism_password = args.schoolism_password
|
|
|
|
if schoolism_password:
|
|
|
|
streamlink.set_plugin_option("schoolism", "password", schoolism_password)
|
|
|
|
|
|
|
|
if args.schoolism_part:
|
|
|
|
streamlink.set_plugin_option("schoolism", "part", args.schoolism_part)
|
|
|
|
|
2017-02-27 18:20:17 +01:00
|
|
|
if args.rtve_mux_subtitles:
|
|
|
|
streamlink.set_plugin_option("rtve", "mux_subtitles", args.rtve_mux_subtitles)
|
|
|
|
|
2017-02-28 18:26:37 +01:00
|
|
|
if args.funimation_mux_subtitles:
|
|
|
|
streamlink.set_plugin_option("funimationnow", "mux_subtitles", True)
|
|
|
|
|
|
|
|
if args.funimation_language:
|
|
|
|
# map en->english, ja->japanese
|
|
|
|
lang = {"en": "english", "ja": "japanese"}.get(args.funimation_language.lower(),
|
|
|
|
args.funimation_language.lower())
|
|
|
|
streamlink.set_plugin_option("funimationnow", "language", lang)
|
|
|
|
|
2017-01-27 11:33:37 +01:00
|
|
|
if args.tvplayer_email:
|
|
|
|
streamlink.set_plugin_option("tvplayer", "email", args.tvplayer_email)
|
|
|
|
|
|
|
|
if args.tvplayer_email and not args.tvplayer_password:
|
|
|
|
tvplayer_password = console.askpass("Enter TVPlayer password: ")
|
|
|
|
else:
|
|
|
|
tvplayer_password = args.tvplayer_password
|
|
|
|
|
|
|
|
if tvplayer_password:
|
|
|
|
streamlink.set_plugin_option("tvplayer", "password", tvplayer_password)
|
|
|
|
|
2017-03-02 17:35:57 +01:00
|
|
|
if args.pluzz_mux_subtitles:
|
|
|
|
streamlink.set_plugin_option("pluzz", "mux_subtitles", args.pluzz_mux_subtitles)
|
|
|
|
|
2017-03-02 16:13:05 +01:00
|
|
|
if args.wwenetwork_email:
|
|
|
|
streamlink.set_plugin_option("wwenetwork", "email", args.wwenetwork_email)
|
|
|
|
if args.wwenetwork_email and not args.wwenetwork_password:
|
|
|
|
wwenetwork_password = console.askpass("Enter WWE Network password: ")
|
|
|
|
else:
|
|
|
|
wwenetwork_password = args.wwenetwork_password
|
|
|
|
if wwenetwork_password:
|
|
|
|
streamlink.set_plugin_option("wwenetwork", "password", wwenetwork_password)
|
|
|
|
|
2017-03-02 12:13:41 +01:00
|
|
|
if args.animelab_email:
|
|
|
|
streamlink.set_plugin_option("animelab", "email", args.animelab_email)
|
|
|
|
|
|
|
|
if args.animelab_email and not args.animelab_password:
|
|
|
|
animelab_password = console.askpass("Enter AnimeLab password: ")
|
|
|
|
else:
|
|
|
|
animelab_password = args.animelab_password
|
|
|
|
|
|
|
|
if animelab_password:
|
|
|
|
streamlink.set_plugin_option("animelab", "password", animelab_password)
|
|
|
|
|
2017-03-03 20:15:08 +01:00
|
|
|
if args.npo_subtitles:
|
|
|
|
streamlink.set_plugin_option("npo", "subtitles", args.npo_subtitles)
|
|
|
|
|
2017-04-03 11:14:14 +02:00
|
|
|
if args.liveedu_email:
|
|
|
|
streamlink.set_plugin_option("liveedu", "email", args.liveedu_email)
|
|
|
|
|
|
|
|
if args.liveedu_email and not args.liveedu_password:
|
|
|
|
liveedu_password = console.askpass("Enter LiveEdu.tv password: ")
|
|
|
|
else:
|
|
|
|
liveedu_password = args.liveedu_password
|
|
|
|
|
|
|
|
if liveedu_password:
|
|
|
|
streamlink.set_plugin_option("liveedu", "password", liveedu_password)
|
|
|
|
|
2017-05-16 15:09:33 +02:00
|
|
|
if args.bbciplayer_username:
|
|
|
|
streamlink.set_plugin_option("bbciplayer", "username", args.bbciplayer_username)
|
|
|
|
|
|
|
|
if args.bbciplayer_username and not args.bbciplayer_password:
|
|
|
|
bbciplayer_password = console.askpass("Enter bbc.co.uk account password: ")
|
|
|
|
else:
|
|
|
|
bbciplayer_password = args.bbciplayer_password
|
|
|
|
|
|
|
|
if bbciplayer_password:
|
|
|
|
streamlink.set_plugin_option("bbciplayer", "password", bbciplayer_password)
|
|
|
|
|
2017-07-01 03:56:44 +02:00
|
|
|
if args.zattoo_email:
|
|
|
|
streamlink.set_plugin_option("zattoo", "email", args.zattoo_email)
|
|
|
|
if args.zattoo_email and not args.zattoo_password:
|
|
|
|
zattoo_password = console.askpass("Enter zattoo password: ")
|
|
|
|
else:
|
|
|
|
zattoo_password = args.zattoo_password
|
|
|
|
if zattoo_password:
|
|
|
|
streamlink.set_plugin_option("zattoo", "password", zattoo_password)
|
|
|
|
|
|
|
|
if args.zattoo_purge_credentials:
|
|
|
|
streamlink.set_plugin_option("zattoo", "purge_credentials",
|
|
|
|
args.zattoo_purge_credentials)
|
|
|
|
|
2018-02-07 11:10:46 +01:00
|
|
|
if args.afreeca_username:
|
|
|
|
streamlink.set_plugin_option("afreeca", "username", args.afreeca_username)
|
|
|
|
|
|
|
|
if args.afreeca_username and not args.afreeca_password:
|
|
|
|
afreeca_password = console.askpass("Enter afreecatv account password: ")
|
|
|
|
else:
|
|
|
|
afreeca_password = args.afreeca_password
|
|
|
|
|
|
|
|
if afreeca_password:
|
|
|
|
streamlink.set_plugin_option("afreeca", "password", afreeca_password)
|
|
|
|
|
2018-03-16 23:52:08 +01:00
|
|
|
if args.pixiv_username:
|
|
|
|
streamlink.set_plugin_option("pixiv", "username", args.pixiv_username)
|
|
|
|
|
|
|
|
if args.pixiv_username and not args.pixiv_password:
|
|
|
|
pixiv_password = console.askpass("Enter pixiv account password: ")
|
|
|
|
else:
|
|
|
|
pixiv_password = args.pixiv_password
|
|
|
|
|
|
|
|
if pixiv_password:
|
|
|
|
streamlink.set_plugin_option("pixiv", "password", pixiv_password)
|
|
|
|
|
2013-04-11 13:28:12 +02:00
|
|
|
|
2013-07-17 02:14:47 +02:00
|
|
|
def check_root():
|
|
|
|
if hasattr(os, "getuid"):
|
2016-10-29 03:58:28 +02:00
|
|
|
if os.geteuid() == 0:
|
2016-11-23 14:25:13 +01:00
|
|
|
console.logger.info("streamlink is running as root! Be careful!")
|
2013-07-17 02:14:47 +02:00
|
|
|
|
2013-11-03 23:04:47 +01:00
|
|
|
|
2018-01-16 19:11:58 +01:00
|
|
|
def log_current_versions():
|
2017-12-13 19:12:03 +01:00
|
|
|
"""Show current installed versions"""
|
|
|
|
if args.loglevel == "debug":
|
|
|
|
# MAC OS X
|
|
|
|
if sys.platform == "darwin":
|
|
|
|
os_version = "macOS {0}".format(platform.mac_ver()[0])
|
|
|
|
# Windows
|
|
|
|
elif sys.platform.startswith("win"):
|
|
|
|
os_version = "{0} {1}".format(platform.system(), platform.release())
|
|
|
|
# linux / other
|
|
|
|
else:
|
|
|
|
os_version = platform.platform()
|
|
|
|
|
|
|
|
console.logger.debug("OS: {0}".format(os_version))
|
|
|
|
console.logger.debug("Python: {0}".format(platform.python_version()))
|
|
|
|
console.logger.debug("Streamlink: {0}".format(streamlink_version))
|
|
|
|
console.logger.debug("Requests({0}), Socks({1}), Websocket({2})".format(
|
|
|
|
requests.__version__, socks_version, websocket_version))
|
|
|
|
|
|
|
|
|
2015-02-03 20:18:40 +01:00
|
|
|
def check_version(force=False):
|
2013-08-31 14:24:12 +02:00
|
|
|
cache = Cache(filename="cli.json")
|
|
|
|
latest_version = cache.get("latest_version")
|
|
|
|
|
2015-02-03 20:18:40 +01:00
|
|
|
if force or not latest_version:
|
2016-09-19 21:46:06 +02:00
|
|
|
res = requests.get("https://pypi.python.org/pypi/streamlink/json")
|
2013-08-31 14:24:12 +02:00
|
|
|
data = res.json()
|
|
|
|
latest_version = data.get("info").get("version")
|
|
|
|
cache.set("latest_version", latest_version, (60 * 60 * 24))
|
|
|
|
|
2014-06-13 12:07:48 +02:00
|
|
|
version_info_printed = cache.get("version_info_printed")
|
2015-02-03 20:18:40 +01:00
|
|
|
if not force and version_info_printed:
|
2014-06-13 12:07:48 +02:00
|
|
|
return
|
|
|
|
|
2016-09-19 21:46:06 +02:00
|
|
|
installed_version = StrictVersion(streamlink.version)
|
2013-08-31 14:24:12 +02:00
|
|
|
latest_version = StrictVersion(latest_version)
|
|
|
|
|
|
|
|
if latest_version > installed_version:
|
2016-09-19 21:46:06 +02:00
|
|
|
console.logger.info("A new version of Streamlink ({0}) is "
|
2013-08-31 14:24:12 +02:00
|
|
|
"available!".format(latest_version))
|
2014-06-13 12:07:48 +02:00
|
|
|
cache.set("version_info_printed", True, (60 * 60 * 6))
|
2015-02-03 20:18:40 +01:00
|
|
|
elif force:
|
2016-09-19 21:46:06 +02:00
|
|
|
console.logger.info("Your Streamlink version ({0}) is up to date!",
|
2015-02-03 20:18:40 +01:00
|
|
|
installed_version)
|
|
|
|
|
|
|
|
if force:
|
|
|
|
sys.exit()
|
2013-08-31 14:24:12 +02:00
|
|
|
|
2013-08-08 15:01:32 +02:00
|
|
|
|
2011-08-15 04:37:22 +02:00
|
|
|
def main():
|
2017-09-29 14:43:39 +02:00
|
|
|
error_code = 0
|
|
|
|
|
2013-07-17 01:44:42 +02:00
|
|
|
setup_args()
|
2016-09-19 21:46:06 +02:00
|
|
|
setup_streamlink()
|
2014-06-04 23:20:33 +02:00
|
|
|
setup_plugins()
|
2014-07-07 17:29:17 +02:00
|
|
|
setup_config_args()
|
2013-07-17 01:44:42 +02:00
|
|
|
setup_console()
|
2014-03-14 21:02:34 +01:00
|
|
|
setup_http_session()
|
2016-11-23 14:25:13 +01:00
|
|
|
check_root()
|
2018-01-16 19:11:58 +01:00
|
|
|
log_current_versions()
|
2012-09-29 22:04:44 +02:00
|
|
|
|
2017-02-28 09:36:52 +01:00
|
|
|
if args.version_check or (not args.no_version_check and args.auto_version_check):
|
2014-03-09 15:57:03 +01:00
|
|
|
with ignored(Exception):
|
2015-02-03 20:18:40 +01:00
|
|
|
check_version(force=args.version_check)
|
2013-08-31 14:24:12 +02:00
|
|
|
|
2013-10-28 17:38:14 +01:00
|
|
|
if args.plugins:
|
|
|
|
print_plugins()
|
2014-12-20 18:56:07 +01:00
|
|
|
elif args.can_handle_url:
|
|
|
|
try:
|
2016-09-19 21:46:06 +02:00
|
|
|
streamlink.resolve_url(args.can_handle_url)
|
2014-12-20 18:56:07 +01:00
|
|
|
except NoPluginError:
|
2017-09-29 15:06:24 +02:00
|
|
|
error_code = 1
|
2017-01-10 20:16:05 +01:00
|
|
|
elif args.can_handle_url_no_redirect:
|
|
|
|
try:
|
|
|
|
streamlink.resolve_url_no_redirect(args.can_handle_url_no_redirect)
|
|
|
|
except NoPluginError:
|
2017-09-29 15:06:24 +02:00
|
|
|
error_code = 1
|
2013-10-28 17:38:14 +01:00
|
|
|
elif args.url:
|
2014-07-25 18:11:48 +02:00
|
|
|
try:
|
2013-10-30 01:12:36 +01:00
|
|
|
setup_options()
|
2014-04-18 18:21:43 +02:00
|
|
|
setup_plugin_options()
|
2013-10-30 01:12:36 +01:00
|
|
|
handle_url()
|
2017-09-29 14:43:39 +02:00
|
|
|
except KeyboardInterrupt:
|
2015-08-02 13:01:46 +02:00
|
|
|
# Close output
|
|
|
|
if output:
|
|
|
|
output.close()
|
2017-01-16 18:53:23 +01:00
|
|
|
console.msg("Interrupted! Exiting...")
|
2017-09-29 14:43:39 +02:00
|
|
|
error_code = 130
|
2017-01-16 18:53:23 +01:00
|
|
|
finally:
|
2014-07-25 18:11:48 +02:00
|
|
|
if stream_fd:
|
2014-08-16 17:24:30 +02:00
|
|
|
try:
|
2017-01-16 18:53:23 +01:00
|
|
|
console.logger.info("Closing currently open stream...")
|
2014-08-16 17:24:30 +02:00
|
|
|
stream_fd.close()
|
|
|
|
except KeyboardInterrupt:
|
2017-09-29 14:43:39 +02:00
|
|
|
error_code = 130
|
2013-12-16 01:10:42 +01:00
|
|
|
elif args.twitch_oauth_authenticate:
|
|
|
|
authenticate_twitch_oauth()
|
2015-08-02 19:49:17 +02:00
|
|
|
elif args.help:
|
2011-08-15 04:37:22 +02:00
|
|
|
parser.print_help()
|
2015-08-02 13:42:09 +02:00
|
|
|
else:
|
|
|
|
usage = parser.format_usage()
|
|
|
|
msg = (
|
|
|
|
"{usage}\nUse -h/--help to see the available options or "
|
2016-12-06 13:10:42 +01:00
|
|
|
"read the manual at https://streamlink.github.io"
|
2015-08-02 13:42:09 +02:00
|
|
|
).format(usage=usage)
|
|
|
|
console.msg(msg)
|
2017-09-29 15:06:24 +02:00
|
|
|
|
|
|
|
sys.exit(error_code)
|