1
0
mirror of https://github.com/spaam/svtplay-dl.git synced 2024-11-24 12:15:40 +01:00
svtplay-dl/lib/svtplay_dl/utils/parser.py
Olof Johansson 0e4fc4899c utils.parser: Allow direct access to argparse object before use
The svtplay_dl.util.parser.parser() function builds an argparse object
and applies it to sys.argv. This change splits this to two functions: a
gen_parser() that generates the argparse object and the parser()
function that still implements the old behavior (by itself calling
gen_parser()).

By being able to intercept the generated argparse object before it has
consumed the command line arguments, we can do things like generating
manpages directly from the argument definitions (kudos to the
argparse-manpage project). Such a tool can either be integrated in the
release process of svtplay-dl (affecting the set of dependencies for
everybody), or integrated as part of the distribution building process
(limiting the dependency to opt-in usage from distributions --- or
opening up for other solutions). This change will allow for either, but
does not introduce any new dependencies.

Whereas svtplay-dl supplies its version number as input to the argparse
generation, the argparse-manpage tool requires the function to be
callable without arguments; hence the "unknown" default value for the
version parameter to gen_parser(). It is overriden when used by
svtplay-dl and does not end up in the manpage when used with
argparse-manpage.

This change is backwards compatible; the interface or behavior of
parser() does not change.
2020-10-12 15:03:20 +02:00

354 lines
15 KiB
Python

import argparse
import logging
import os
import platform
from yaml import safe_load
configdata = None
if platform.system() == "Windows":
APPDATA = os.environ["APPDATA"]
CONFIGFILE = os.path.join(APPDATA, "svtplay-dl", "svtplay-dl.yaml")
else:
CONFIGFILE = os.path.expanduser("~/.svtplay-dl.yaml")
class Options:
"""
Options used when invoking the script from another Python script.
Simple container class used when calling get_media() from another Python
script. The variables corresponds to the command line parameters parsed
in main() when the script is called directly.
When called from a script there are a few more things to consider:
* Logging is done to 'log'. main() calls setup_log() which sets the
logging to either stdout or stderr depending on the silent level.
A user calling get_media() directly can either also use setup_log()
or configure the log manually.
* Progress information is printed to 'progress_stream' which defaults to
sys.stderr but can be changed to any stream.
* Many errors results in calls to system.exit() so catch 'SystemExit'-
Exceptions to prevent the entire application from exiting if that happens.
"""
def __init__(self):
self.default = {}
def set(self, key, value):
self.default[key] = value
def get(self, key):
if key in self.default:
return self.default[key]
def get_variable(self):
return self.default
def set_variable(self, value):
self.default = value
def gen_parser(version="unknown"):
parser = argparse.ArgumentParser(prog="svtplay-dl")
general = parser.add_argument_group()
general.add_argument("--version", action="version", version="%(prog)s {}".format(version))
general.add_argument("-o", "--output", metavar="output", default=None, help="outputs to the given filename or folder")
general.add_argument(
"--subfolder", action="store_true", default=False, help="Create a subfolder titled as the show, non-series gets in folder movies"
)
general.add_argument("--config", dest="configfile", metavar="configfile", default=CONFIGFILE, help="Specify configuration file")
general.add_argument("-f", "--force", action="store_true", dest="force", default=False, help="overwrite if file exists already")
general.add_argument("-r", "--resume", action="store_true", dest="resume", default=False, help="resume a download (RTMP obsolete)")
general.add_argument("-l", "--live", action="store_true", dest="live", default=False, help="enable for live streams (RTMP based ones)")
general.add_argument("-c", "--capture_time", default=-1, type=int, metavar="capture_time", help="define capture time in minutes of a live stream")
general.add_argument("-s", "--silent", action="store_true", dest="silent", default=False, help="be less verbose")
general.add_argument(
"--silent-semi", action="store_true", dest="silent_semi", default=False, help="only show a message when the file is downloaded"
)
general.add_argument("-u", "--username", default=None, help="username")
general.add_argument("-p", "--password", default=None, help="password")
general.add_argument(
"-t", "--thumbnail", action="store_true", dest="thumbnail", default=False, help="download thumbnail from the site if available"
)
general.add_argument(
"-g", "--get-url", action="store_true", dest="get_url", default=False, help="do not download any video, but instead print the URL."
)
general.add_argument(
"--get-only-episode-url",
action="store_true",
dest="get_only_episode_url",
default=False,
help="do not get video URLs, only print the episode URL.",
)
general.add_argument(
"--dont-verify-ssl-cert", action="store_false", dest="ssl_verify", default=True, help="Don't attempt to verify SSL certificates."
)
general.add_argument(
"--http-header", dest="http_headers", default=None, metavar="header1=value;header2=value2", help="A header to add to each HTTP request."
)
general.add_argument(
"--cookies", dest="cookies", default=None, metavar="cookie1=value;cookie2=value2", help="A cookies to add to each HTTP request."
)
general.add_argument("--remux", dest="remux", default=False, action="store_true", help="Remux from one container to mp4 using ffmpeg or avconv")
general.add_argument(
"--exclude", dest="exclude", default=None, metavar="WORD1,WORD2,...", help="exclude videos with the WORD(s) in the filename. comma separated."
)
general.add_argument("--after-date", dest="after_date", default=None, metavar="yyyy-MM-dd", help="only videos published on or after this date")
general.add_argument(
"--proxy",
dest="proxy",
default=None,
metavar="proxy",
help="Use the specified HTTP/HTTPS/SOCKS proxy. To enable experimental "
"SOCKS proxy, specify a proper scheme. For example "
"socks5://127.0.0.1:1080/.",
)
general.add_argument("-v", "--verbose", action="store_true", dest="verbose", default=False, help="explain what is going on")
general.add_argument("--nfo", action="store_true", dest="nfo", default=False, help="create a NFO file")
general.add_argument("--force-nfo", action="store_true", dest="force_nfo", default=False, help="download only NFO if used with --nfo")
quality = parser.add_argument_group("Quality")
quality.add_argument(
"-q",
"--quality",
default=0,
metavar="quality",
help="choose what format to download based on bitrate / video resolution." "it will download the best format by default",
)
quality.add_argument(
"-Q", "--flexible-quality", default=0, metavar="amount", dest="flexibleq", help="allow given quality (as above) to differ by an amount"
)
quality.add_argument("-P", "--preferred", default=None, metavar="preferred", help="preferred download method (dash, hls, hds, or http)")
quality.add_argument("--list-quality", dest="list_quality", action="store_true", default=False, help="list the quality for a video")
quality.add_argument(
"--stream-priority",
dest="stream_prio",
default=None,
metavar="dash,hls,hds,http",
help="If two streams have the same quality, choose the one you prefer",
)
quality.add_argument(
"--format-preferred",
dest="format_preferred",
default=None,
metavar="h264,h264-51",
help="Choose the format you prefer, --list-quality to show which one to choose from",
)
subtitle = parser.add_argument_group("Subtitle")
subtitle.add_argument(
"-S", "--subtitle", action="store_true", dest="subtitle", default=False, help="download subtitle from the site if available"
)
subtitle.add_argument(
"-M",
"--merge-subtitle",
action="store_true",
dest="merge_subtitle",
default=False,
help="merge subtitle with video/audio file with corresponding ISO639-3 language code."
"this invokes --remux automatically. use with -S for external also.",
)
subtitle.add_argument(
"--force-subtitle", dest="force_subtitle", default=False, action="store_true", help="download only subtitle if its used with -S"
)
subtitle.add_argument(
"--require-subtitle", dest="require_subtitle", default=False, action="store_true", help="download only if a subtitle is available"
)
subtitle.add_argument(
"--all-subtitles", dest="get_all_subtitles", default=False, action="store_true", help="Download all available subtitles for the video"
)
subtitle.add_argument(
"--raw-subtitles", dest="get_raw_subtitles", default=False, action="store_true", help="also download the subtitles in their native format"
)
subtitle.add_argument(
"--convert-subtitle-colors",
dest="convert_subtitle_colors",
default=False,
action="store_true",
help='converts the color information in subtitles, to <font color=""> tags',
)
alleps = parser.add_argument_group("All")
alleps.add_argument("-A", "--all-episodes", action="store_true", dest="all_episodes", default=False, help="try to download all episodes")
alleps.add_argument("--all-last", dest="all_last", default=-1, type=int, metavar="NN", help="get last NN episodes instead of all episodes")
alleps.add_argument("--include-clips", dest="include_clips", default=False, action="store_true", help="include clips from websites when using -A")
cmorep = parser.add_argument_group("C More")
cmorep.add_argument("--cmore-operatorlist", dest="cmoreoperatorlist", default=False, action="store_true", help="show operatorlist for cmore")
cmorep.add_argument("--cmore-operator", dest="cmoreoperator", default=None, metavar="operator")
parser.add_argument("urls", nargs="*")
return parser
def parser(version):
parser = gen_parser(version)
options = parser.parse_args()
return parser, options
def setup_defaults():
options = Options()
options.set("output", None)
options.set("subfolder", False)
options.set("configfile", CONFIGFILE)
options.set("resume", False)
options.set("live", False)
options.set("capture_time", -1)
options.set("silent", False)
options.set("force", False)
options.set("quality", 0)
options.set("flexibleq", 0)
options.set("list_quality", False)
options.set("other", None)
options.set("subtitle", False)
options.set("username", None)
options.set("password", None)
options.set("thumbnail", False)
options.set("all_episodes", False)
options.set("all_last", -1)
options.set("merge_subtitle", False)
options.set("force_subtitle", False)
options.set("require_subtitle", False)
options.set("get_all_subtitles", False)
options.set("get_raw_subtitles", False)
options.set("convert_subtitle_colors", False)
options.set("preferred", None)
options.set("verbose", False)
options.set("nfo", False)
options.set("force_nfo", False)
options.set("output_auto", False)
options.set("service", None)
options.set("cookies", None)
options.set("exclude", None)
options.set("after_date", None)
options.set("get_url", False)
options.set("get_only_episode_url", False)
options.set("ssl_verify", True)
options.set("http_headers", None)
options.set("format_preferred", None)
options.set("stream_prio", None)
options.set("remux", False)
options.set("silent_semi", False)
options.set("proxy", None)
options.set("include_clips", False)
options.set("cmoreoperatorlist", False)
options.set("filename", "{title}.s{season}e{episode}.{episodename}-{id}-{service}.{ext}")
return _special_settings(options)
def parsertoconfig(config, parser):
config.set("output", parser.output)
config.set("subfolder", parser.subfolder)
config.set("configfile", parser.configfile)
config.set("resume", parser.resume)
config.set("live", parser.live)
config.set("capture_time", parser.capture_time)
config.set("silent", parser.silent)
config.set("force", parser.force)
config.set("quality", parser.quality)
config.set("flexibleq", parser.flexibleq)
config.set("list_quality", parser.list_quality)
config.set("subtitle", parser.subtitle)
config.set("merge_subtitle", parser.merge_subtitle)
config.set("silent_semi", parser.silent_semi)
config.set("username", parser.username)
config.set("password", parser.password)
config.set("thumbnail", parser.thumbnail)
config.set("all_episodes", parser.all_episodes)
config.set("all_last", parser.all_last)
config.set("force_subtitle", parser.force_subtitle)
config.set("require_subtitle", parser.require_subtitle)
config.set("preferred", parser.preferred)
config.set("verbose", parser.verbose)
config.set("nfo", parser.nfo)
config.set("force_nfo", parser.force_nfo)
config.set("exclude", parser.exclude)
config.set("after_date", parser.after_date)
config.set("get_url", parser.get_url)
config.set("get_only_episode_url", parser.get_only_episode_url)
config.set("ssl_verify", parser.ssl_verify)
config.set("http_headers", parser.http_headers)
config.set("cookies", parser.cookies)
config.set("format_preferred", parser.format_preferred)
config.set("stream_prio", parser.stream_prio)
config.set("remux", parser.remux)
config.set("get_all_subtitles", parser.get_all_subtitles)
config.set("get_raw_subtitles", parser.get_raw_subtitles)
config.set("convert_subtitle_colors", parser.convert_subtitle_colors)
config.set("include_clips", parser.include_clips)
config.set("cmoreoperatorlist", parser.cmoreoperatorlist)
config.set("cmoreoperator", parser.cmoreoperator)
config.set("proxy", parser.proxy)
return _special_settings(config)
def _special_settings(config):
if config.get("require_subtitle"):
if config.get("merge_subtitle"):
config.set("merge_subtitle", True)
else:
config.set("subtitle", True)
if config.get("merge_subtitle"):
config.set("remux", True)
if config.get("silent_semi"):
config.set("silent", True)
if config.get("proxy"):
config.set("proxy", config.get("proxy").replace("socks5", "socks5h", 1))
config.set("proxy", dict(http=config.get("proxy"), https=config.get("proxy")))
if config.get("get_only_episode_url"):
config.set("get_url", True)
return config
def merge(old, new):
if isinstance(new, list):
new = {list(i.keys())[0]: i[list(i.keys())[0]] for i in new}
config = setup_defaults()
if new:
for item in new:
if item in new:
if new[item] != config.get(item): # Check if new value is not a default one.
old[item] = new[item]
else:
old[item] = new[item]
options = Options()
options.set_variable(old)
return options
def readconfig(config, configfile, service=None, preset=None):
global configdata
if configfile and configdata is None:
try:
with open(configfile) as fd:
data = fd.read()
configdata = safe_load(data)
except PermissionError:
logging.error("Permission denied while reading config: {}".format(configfile))
if configdata is None:
return config
if "default" in configdata:
config = merge(config.get_variable(), configdata["default"])
if service and "service" in configdata and service in configdata["service"]:
config = merge(config.get_variable(), configdata["service"][service])
if preset and "presets" in configdata and preset in configdata["presets"]:
config = merge(config.get_variable(), configdata["presets"][preset])
return config