streamlink/src/streamlink_cli/main.py

1108 lines
37 KiB
Python
Raw Normal View History

import errno
2012-08-23 22:46:06 +02:00
import os
import platform
import requests
2012-08-23 22:46:06 +02:00
import sys
2013-06-09 07:50:22 +02:00
import signal
import webbrowser
2012-08-15 19:49:54 +02:00
from contextlib import closing
from distutils.version import StrictVersion
from functools import partial
from itertools import chain
from socks import __version__ as socks_version
from time import sleep
from websocket import __version__ as websocket_version
from streamlink import __version__ as streamlink_version
from streamlink import (Streamlink, StreamError, PluginError,
NoPluginError)
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
from .argparser import parser
from .compat import stdout, is_win32
from .console import ConsoleOutput
from .constants import CONFIG_FILES, PLUGINS_DIR, STREAM_SYNONYMS
from .output import FileOutput, PlayerOutput
from .utils import NamedPipe, HTTPServer, ignored, progress, stream_to_url
ACCEPTABLE_ERRNO = (errno.EPIPE, errno.EINVAL, errno.ECONNRESET)
try:
ACCEPTABLE_ERRNO += (errno.WSAECONNABORTED,)
except AttributeError:
pass # Not windows
QUIET_OPTIONS = ("json", "stream_url", "subprocess_cmdline", "quiet")
2013-03-15 21:41:40 +01: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
def check_file_output(filename, force):
"""Checks if file already exists and ask the user if it should
be overwritten if it does."""
console.logger.debug("Checking file output")
2012-08-15 22:26:05 +02: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)
if answer.lower() != "y":
sys.exit()
return FileOutput(filename)
def create_output():
"""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:
if args.output == "-":
out = FileOutput(fd=stdout)
else:
out = check_file_output(args.output, args.force)
elif args.stdout:
out = FileOutput(fd=stdout)
else:
http = namedpipe = None
2013-07-02 12:59:00 +02: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.")
if args.player_fifo:
pipename = "streamlinkpipe-{0}".format(os.getpid())
console.logger.info("Creating pipe {0}", pipename)
try:
namedpipe = NamedPipe(pipename)
except IOError as err:
console.exit("Failed to create pipe: {0}", err)
elif args.player_http:
http = create_http_server()
console.logger.info("Starting player: {0}", 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)
return out
def create_http_server(host=None, port=0):
"""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.
"""
try:
http = HTTPServer()
http.bind(host=host, port=port)
except OSError as err:
console.exit("Failed to create HTTP server: {0}", err)
return http
def iter_http_requests(server, player):
"""Repeatedly accept HTTP connections on a server.
Forever if the serving externally, or while a player is running if it is not
empty.
"""
while not player or player.running:
try:
yield server.open(timeout=2.5)
except OSError:
continue
def output_stream_http(plugin, initial_streams, external=False, port=0):
"""Continuously output the stream over HTTP."""
2015-08-02 13:01:46 +02:00
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()
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)
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
console.logger.info("Starting server, access with one of:")
for url in server.urls:
console.logger.info(" " + url)
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))
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:
console.logger.info("Stream not available, will re-fetch "
"streams in 10 sec")
sleep(10)
continue
except PluginError as err:
console.logger.error(u"Unable to fetch new streams: {0}", err)
continue
try:
console.logger.info("Opening stream: {0} ({1})", stream_name,
type(stream).shortname())
stream_fd, prebuffer = open_stream(stream)
except StreamError as err:
console.logger.error("{0}", err)
if stream_fd and prebuffer:
console.logger.debug("Writing stream to player")
read_stream(stream_fd, server, prebuffer)
server.close(True)
player.close()
server.close()
def output_stream_passthrough(stream):
"""Prepares a filename to be passed to the player."""
2015-08-02 13:01:46 +02:00
global output
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)
try:
console.logger.info("Starting player: {0}", args.player)
2015-08-02 13:01:46 +02:00
output.open()
except OSError as err:
console.exit("Failed to start player: {0} ({1})", args.player, err)
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.
"""
global stream_fd
2012-08-15 19:49:54 +02:00
# Attempts to open the stream
try:
stream_fd = stream.open()
2012-08-23 22:46:06 +02:00
except StreamError as err:
raise StreamError("Could not open stream: {0}".format(err))
# Read 8192 bytes before proceeding to check for errors.
# This is to avoid opening the output unnecessarily.
try:
console.logger.debug("Pre-buffering 8192 bytes")
prebuffer = stream_fd.read(8192)
except IOError as err:
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
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
success_open = False
for i in range(args.retry_open):
try:
stream_fd, prebuffer = open_stream(stream)
success_open = True
break
except StreamError as err:
console.logger.error("Try {0}/{1}: Could not open stream {2} ({3})", i + 1, args.retry_open, stream, err)
if not success_open:
console.exit("Could not open stream {0}, tried {1} times, exiting", stream, args.retry_open)
output = create_output()
2012-06-14 01:03:44 +02:00
try:
output.open()
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)
with closing(output):
console.logger.debug("Writing stream to output")
read_stream(stream_fd, output, prebuffer)
return True
def read_stream(stream, output, prebuffer, chunk_size=8192):
"""Reads data from stream and then writes it to the output."""
is_player = isinstance(output, PlayerOutput)
is_http = isinstance(output, HTTPServer)
is_fifo = is_player and output.namedpipe
show_progress = isinstance(output, FileOutput) and output.fd is not stdout
2012-08-15 21:05:35 +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))
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()
if output.player.returncode is not None:
console.logger.info("Player closed")
break
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:
console.exit("Error when writing to output: {0}, exiting", err)
break
except IOError as err:
console.exit("Error when reading from stream: {0}, exiting", err)
finally:
stream.close()
console.logger.info("Stream ended")
def handle_stream(plugin, streams, stream_name):
"""Decides what to do with the selected stream.
Depending on arguments it can be one of these:
- Output internal command-line
- Output JSON represenation
- Continuously output the stream over HTTP
- Output stream data to selected output
"""
stream_name = resolve_stream_name(streams, stream_name)
stream = streams[stream_name]
# Print internal command-line if this stream
# uses a subprocess.
if args.subprocess_cmdline:
if isinstance(stream, StreamProcess):
try:
cmdline = stream.cmdline()
except StreamError as err:
console.exit("{0}", err)
console.msg("{0}", cmdline)
else:
console.exit("The stream specified cannot be translated to a command")
# Print JSON representation of the stream
elif console.json:
console.msg_json(stream)
elif args.stream_url:
try:
console.msg("{0}", stream.to_url())
except TypeError:
console.exit("The stream specified cannot be translated to a URL")
# Output the stream
else:
# Find any streams with a '_alt' suffix and attempt
# to use these in case the main stream is not usable.
alt_streams = list(filter(lambda k: stream_name + "_alt" in k,
sorted(streams.keys())))
file_output = args.output or args.stdout
for stream_name in [stream_name] + alt_streams:
stream = streams[stream_name]
stream_type = type(stream).shortname()
if stream_type in args.player_passthrough and not file_output:
console.logger.info("Opening stream: {0} ({1})", stream_name,
stream_type)
success = output_stream_passthrough(stream)
elif args.player_external_http:
return output_stream_http(plugin, streams, external=True,
port=args.player_external_http_port)
elif args.player_continuous_http and not file_output:
return output_stream_http(plugin, streams)
else:
console.logger.info("Opening stream: {0} ({1})", stream_name,
stream_type)
success = output_stream(stream)
if success:
break
2011-08-15 04:37:22 +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)
def fetch_streams_with_retry(plugin, interval, count):
"""Attempts to fetch streams repeatedly
until some are returned or limit hit."""
try:
streams = fetch_streams(plugin)
except PluginError as err:
console.logger.error(u"{0}", err)
streams = None
if not streams:
console.logger.info("Waiting for streams, retrying every {0} "
"second(s)", interval)
attempts = 0
while not streams:
sleep(interval)
try:
streams = fetch_streams(plugin)
except PluginError as err:
console.logger.error(u"{0}", err)
if count > 0:
attempts += 1
if attempts >= count:
break
return streams
def resolve_stream_name(streams, stream_name):
"""Returns the real stream name of a synonym."""
if stream_name in STREAM_SYNONYMS and stream_name in streams:
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):
"""Formats a dict of streams.
2012-08-15 19:49:54 +02: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).
"""
2011-08-15 04:37:22 +02:00
delimiter = ", "
validstreams = []
2015-08-03 02:41:54 +02:00
for name, stream in sorted(streams.items(),
key=lambda stream: plugin.stream_weight(stream[0])):
if name in STREAM_SYNONYMS:
continue
def synonymfilter(n):
return stream is streams[n] and n is not name
synonyms = list(filter(synonymfilter, streams.keys()))
if len(synonyms) > 0:
joined = delimiter.join(synonyms)
name = "{0} ({1})".format(name, joined)
2015-08-03 02:41:54 +02:00
validstreams.append(name)
2015-08-03 02:41:54 +02:00
return delimiter.join(validstreams)
def handle_url():
"""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:
plugin = streamlink.resolve_url(args.url)
console.logger.info("Found matching plugin {0} for URL {1}",
plugin.module, args.url)
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)
else:
streams = fetch_streams(plugin)
except NoPluginError:
console.exit("No plugin can handle URL: {0}", args.url)
except PluginError as err:
console.exit(u"{0}", err)
if not streams:
console.exit("No playable streams found on this URL: {0}", args.url)
2011-08-15 04:37:22 +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
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)
for stream_name in args.stream:
if stream_name in streams:
console.logger.info("Available streams: {0}", validstreams)
handle_stream(plugin, streams, stream_name)
return
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:
if console.json:
console.msg_json(dict(streams=streams, plugin=plugin.module))
else:
2015-07-30 11:40:40 +02:00
validstreams = format_valid_streams(plugin, streams)
console.msg("Available streams: {0}", validstreams)
2011-08-15 04:37:22 +02:00
def print_plugins():
"""Outputs a list of all plugins Streamlink has loaded."""
pluginlist = list(streamlink.get_plugins().keys())
pluginlist_formatted = ", ".join(sorted(pluginlist))
if console.json:
console.msg_json(pluginlist)
else:
console.msg("Loaded plugins: {0}", pluginlist_formatted)
2011-08-15 04:37:22 +02:00
def authenticate_twitch_oauth():
"""Opens a web browser to allow the user to grant Streamlink
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"
url = ("https://api.twitch.tv/kraken/oauth2/authorize/"
"?response_type=token&client_id={0}&redirect_uri="
"{1}&scope=user_read+user_subscriptions").format(client_id, redirect_uri)
console.msg("Attempting to open a browser to let you authenticate "
"Streamlink with Twitch")
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))
def load_plugins(dirs):
"""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]
for directory in dirs:
if os.path.isdir(directory):
streamlink.load_plugins(directory)
else:
2013-08-08 15:01:32 +02:00
console.logger.warning("Plugin path {0} does not exist or is not "
"a directory!", directory)
2011-08-15 04:37:22 +02:00
def setup_args(config_files=[]):
"""Parses arguments."""
global args
arglist = sys.argv[1:]
# Load arguments from config files
for config_file in filter(os.path.isfile, config_files):
arglist.insert(0, "@" + config_file)
args = parser.parse_args(arglist)
# Force lowercase to allow case-insensitive lookup
if args.stream:
args.stream = [stream.lower() for stream in args.stream]
if not args.url and args.url_param:
args.url = args.url_param
def setup_config_args():
config_files = []
if args.url:
2014-06-06 18:34:32 +02:00
with ignored(NoPluginError):
plugin = streamlink.resolve_url(args.url)
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))
else:
# Only load first available default config
for config_file in filter(os.path.isfile, CONFIG_FILES):
config_files.append(config_file)
break
if config_files:
setup_args(config_files)
def setup_console():
"""Console setup."""
global console
# All console related operations is handled via the ConsoleOutput class
console = ConsoleOutput(sys.stdout, streamlink)
# 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.
if not any(getattr(args, attr) for attr in QUIET_OPTIONS):
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.")
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.")
console.json = args.json
# Handle SIGTERM just like SIGINT
signal.signal(signal.SIGTERM, signal.default_int_handler)
def setup_http_session():
"""Sets the global HTTP settings, such as proxy and headers."""
if args.http_proxy:
streamlink.set_option("http-proxy", args.http_proxy)
if args.https_proxy:
streamlink.set_option("https-proxy", args.https_proxy)
if args.http_cookie:
streamlink.set_option("http-cookies", dict(args.http_cookie))
if args.http_header:
streamlink.set_option("http-headers", dict(args.http_header))
if args.http_query_param:
streamlink.set_option("http-query-params", dict(args.http_query_param))
2014-03-20 01:33:06 +01:00
if args.http_ignore_env:
streamlink.set_option("http-trust-env", False)
2014-03-20 01:33:06 +01:00
if args.http_no_ssl_verify:
streamlink.set_option("http-ssl-verify", False)
if args.http_disable_dh:
streamlink.set_option("http-disable-dh", True)
if args.http_ssl_cert:
streamlink.set_option("http-ssl-cert", args.http_ssl_cert)
if args.http_ssl_cert_crt_key:
streamlink.set_option("http-ssl-cert", tuple(args.http_ssl_cert_crt_key))
if args.http_timeout:
streamlink.set_option("http-timeout", args.http_timeout)
if args.http_cookies:
streamlink.set_option("http-cookies", args.http_cookies)
if args.http_headers:
streamlink.set_option("http-headers", args.http_headers)
if args.http_query_params:
streamlink.set_option("http-query-params", args.http_query_params)
def setup_plugins():
"""Loads any additional plugins."""
if os.path.isdir(PLUGINS_DIR):
2013-08-08 15:01:32 +02:00
load_plugins([PLUGINS_DIR])
if args.plugin_dirs:
load_plugins(args.plugin_dirs)
def setup_streamlink():
"""Creates the Streamlink session."""
global streamlink
streamlink = Streamlink()
def setup_options():
"""Sets Streamlink options."""
if args.hls_live_edge:
streamlink.set_option("hls-live-edge", args.hls_live_edge)
if args.hls_segment_attempts:
streamlink.set_option("hls-segment-attempts", args.hls_segment_attempts)
if args.hls_playlist_reload_attempts:
streamlink.set_option("hls-playlist-reload-attempts", args.hls_playlist_reload_attempts)
if args.hls_segment_threads:
streamlink.set_option("hls-segment-threads", args.hls_segment_threads)
if args.hls_segment_timeout:
streamlink.set_option("hls-segment-timeout", args.hls_segment_timeout)
if args.hls_segment_ignore_names:
streamlink.set_option("hls-segment-ignore-names", args.hls_segment_ignore_names)
if args.hls_timeout:
streamlink.set_option("hls-timeout", args.hls_timeout)
if args.hls_audio_select:
streamlink.set_option("hls-audio-select", args.hls_audio_select)
if args.hls_start_offset:
streamlink.set_option("hls-start-offset", args.hls_start_offset)
if args.hls_duration:
streamlink.set_option("hls-duration", args.hls_duration)
if args.hls_live_restart:
streamlink.set_option("hls-live-restart", args.hls_live_restart)
if args.hds_live_edge:
streamlink.set_option("hds-live-edge", args.hds_live_edge)
2013-01-11 19:41:19 +01:00
if args.hds_segment_attempts:
streamlink.set_option("hds-segment-attempts", args.hds_segment_attempts)
if args.hds_segment_threads:
streamlink.set_option("hds-segment-threads", args.hds_segment_threads)
if args.hds_segment_timeout:
streamlink.set_option("hds-segment-timeout", args.hds_segment_timeout)
if args.hds_timeout:
streamlink.set_option("hds-timeout", args.hds_timeout)
if args.http_stream_timeout:
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:
streamlink.set_option("ringbuffer-size", args.ringbuffer_size)
2012-12-19 21:08:57 +01:00
if args.rtmp_proxy:
streamlink.set_option("rtmp-proxy", args.rtmp_proxy)
if args.rtmp_rtmpdump:
streamlink.set_option("rtmp-rtmpdump", args.rtmp_rtmpdump)
if args.rtmp_timeout:
streamlink.set_option("rtmp-timeout", args.rtmp_timeout)
if args.stream_segment_attempts:
streamlink.set_option("stream-segment-attempts", args.stream_segment_attempts)
if args.stream_segment_threads:
streamlink.set_option("stream-segment-threads", args.stream_segment_threads)
if args.stream_segment_timeout:
streamlink.set_option("stream-segment-timeout", args.stream_segment_timeout)
if args.stream_timeout:
streamlink.set_option("stream-timeout", args.stream_timeout)
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)
streamlink.set_option("subprocess-errorlog", args.subprocess_errorlog)
streamlink.set_option("subprocess-errorlog-path", args.subprocess_errorlog_path)
streamlink.set_option("locale", args.locale)
# 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")
def setup_plugin_options():
"""Sets Streamlink plugin options."""
if args.twitch_cookie:
streamlink.set_plugin_option("twitch", "cookie",
args.twitch_cookie)
if args.twitch_oauth_token:
streamlink.set_plugin_option("twitch", "oauth_token",
args.twitch_oauth_token)
if args.twitch_disable_hosting:
streamlink.set_plugin_option("twitch", "disable_hosting",
args.twitch_disable_hosting)
if args.ustream_password:
streamlink.set_plugin_option("ustreamtv", "password",
args.ustream_password)
2014-01-08 00:27:30 +01:00
if args.crunchyroll_username:
streamlink.set_plugin_option("crunchyroll", "username",
args.crunchyroll_username)
2014-01-08 00:27:30 +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:
streamlink.set_plugin_option("crunchyroll", "password",
crunchyroll_password)
2014-01-08 00:27:30 +01:00
if args.crunchyroll_purge_credentials:
streamlink.set_plugin_option("crunchyroll", "purge_credentials",
args.crunchyroll_purge_credentials)
if args.crunchyroll_session_id:
streamlink.set_plugin_option("crunchyroll", "session_id",
args.crunchyroll_session_id)
2014-01-08 00:27:30 +01:00
if args.crunchyroll_locale:
streamlink.set_plugin_option("crunchyroll", "locale",
args.crunchyroll_locale)
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)
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)
if args.rtve_mux_subtitles:
streamlink.set_plugin_option("rtve", "mux_subtitles", args.rtve_mux_subtitles)
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)
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)
if args.pluzz_mux_subtitles:
streamlink.set_plugin_option("pluzz", "mux_subtitles", args.pluzz_mux_subtitles)
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)
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)
if args.npo_subtitles:
streamlink.set_plugin_option("npo", "subtitles", args.npo_subtitles)
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)
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)
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)
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)
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)
def check_root():
if hasattr(os, "getuid"):
if os.geteuid() == 0:
console.logger.info("streamlink is running as root! Be careful!")
def log_current_versions():
"""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))
def check_version(force=False):
cache = Cache(filename="cli.json")
latest_version = cache.get("latest_version")
if force or not latest_version:
res = requests.get("https://pypi.python.org/pypi/streamlink/json")
data = res.json()
latest_version = data.get("info").get("version")
cache.set("latest_version", latest_version, (60 * 60 * 24))
version_info_printed = cache.get("version_info_printed")
if not force and version_info_printed:
return
installed_version = StrictVersion(streamlink.version)
latest_version = StrictVersion(latest_version)
if latest_version > installed_version:
console.logger.info("A new version of Streamlink ({0}) is "
"available!".format(latest_version))
cache.set("version_info_printed", True, (60 * 60 * 6))
elif force:
console.logger.info("Your Streamlink version ({0}) is up to date!",
installed_version)
if force:
sys.exit()
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
setup_args()
setup_streamlink()
setup_plugins()
setup_config_args()
setup_console()
setup_http_session()
check_root()
log_current_versions()
if args.version_check or (not args.no_version_check and args.auto_version_check):
with ignored(Exception):
check_version(force=args.version_check)
if args.plugins:
print_plugins()
elif args.can_handle_url:
try:
streamlink.resolve_url(args.can_handle_url)
except NoPluginError:
2017-09-29 15:06:24 +02:00
error_code = 1
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
elif args.url:
try:
setup_options()
setup_plugin_options()
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()
console.msg("Interrupted! Exiting...")
2017-09-29 14:43:39 +02:00
error_code = 130
finally:
if stream_fd:
try:
console.logger.info("Closing currently open stream...")
stream_fd.close()
except KeyboardInterrupt:
2017-09-29 14:43:39 +02:00
error_code = 130
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()
else:
usage = parser.format_usage()
msg = (
"{usage}\nUse -h/--help to see the available options or "
"read the manual at https://streamlink.github.io"
).format(usage=usage)
console.msg(msg)
2017-09-29 15:06:24 +02:00
sys.exit(error_code)