Use new betterhelp.

This commit is contained in:
voussoir 2022-02-12 19:52:30 -08:00
parent 5bf6fe0cd7
commit 3512768c5d
No known key found for this signature in database
GPG key ID: 5F7554F8C26DACCB
2 changed files with 348 additions and 265 deletions

View file

@ -73,6 +73,10 @@ def add_channel_argparse(args):
def _channel_list_argparse(args): def _channel_list_argparse(args):
ycdldb = closest_db() ycdldb = closest_db()
channels = sorted(ycdldb.get_channels(), key=lambda c: c.name.lower()) channels = sorted(ycdldb.get_channels(), key=lambda c: c.name.lower())
if args.automark:
channels = [channel for channel in channels if channel.automark == args.automark]
yield from channels yield from channels
def channel_list_argparse(args): def channel_list_argparse(args):
@ -182,299 +186,366 @@ def video_list_argparse(args):
return 0 return 0
DOCSTRING = ''' @operatornotify.main_decorator(subject='ycdl_cli')
YCDL CLI @vlogging.main_decorator
======== def main(argv):
parser = argparse.ArgumentParser(
description='''
This is the command-line interface for YCDL, so that you can automate your
database and integrate it into other scripts.
''',
)
subparsers = parser.add_subparsers()
{add_channel} ################################################################################################
{channel_list} p_add_channel = subparsers.add_parser(
'add_channel',
{delete_channel} aliases=['add-channel'],
description='''
{download_video}
{init}
{refresh_channels}
{video_list}
You can add --yes to avoid the "Commit?" prompt on commands that modify the db.
TO SEE DETAILS ON EACH COMMAND, RUN
> ycdl_cli.py <command> --help
'''
SUB_DOCSTRINGS = dict(
add_channel='''
add_channel:
Add a channel to the database. Add a channel to the database.
''',
> ycdl_cli.py add_channel channel_id <flags> )
p_add_channel.examples = [
flags: 'UCFhXFikryT4aFcLkLw2LBLA',
--automark X: 'UCFhXFikryT4aFcLkLw2LBLA --automark downloaded',
'UCLx053rWZxCiYWsBETgdKrQ --name LGR',
]
p_add_channel.add_argument(
'channel_id',
)
p_add_channel.add_argument(
'--automark',
default='pending',
help='''
Set the channel's automark to this value, which should be 'pending', Set the channel's automark to this value, which should be 'pending',
'downloaded', or 'ignored'. 'downloaded', or 'ignored'.
''',
--download_directory X: )
p_add_channel.add_argument(
'--download_directory',
'--download-directory',
default=None,
help='''
Set the channel's download directory to this path, which must Set the channel's download directory to this path, which must
be a directory. be a directory.
--name X:
Override the channel's own name with a name of your choosing.
--no_videos:
By default, the channel's videos will be fetched right away.
Add this argument if you don't want to do that yet.
--queuefile_extension X:
Set the queuefile extension for all videos downloaded from this channel.
Examples:
> ycdl_cli.py add_channel UCFhXFikryT4aFcLkLw2LBLA
''', ''',
)
p_add_channel.add_argument(
'--name',
default=None,
help='''
Override the channel's own name with a name of your choosing.
''',
)
p_add_channel.add_argument(
'--no_videos',
'--no-videos',
dest='get_videos',
action='store_false',
help='''
By default, the channel's videos will be fetched right away. Add this
argument if you don't want to do that yet.
channel_list=''' You should run refresh_channels later.
channel_list: ''',
)
p_add_channel.add_argument(
'--queuefile_extension',
'--queuefile-extension',
type=str,
default=None,
help='''
Set the queuefile extension for all videos downloaded from this channel.
''',
)
p_add_channel.add_argument(
'--yes',
dest='autoyes',
action='store_true',
help='''
Commit the database without prompting.
''',
)
p_add_channel.set_defaults(func=add_channel_argparse)
################################################################################################
p_channel_list = subparsers.add_parser(
'channel_list',
aliases=['channel-list'],
description='''
Print all channels in the database. Print all channels in the database.
Note: If you want to use this in a command pipeline, please specify Note: If you want to use this in a command pipeline, please specify
--format instead of relying on the default. --format instead of relying on the default.
''',
> ycdl_cli.py channel_list <flags> )
p_channel_list.examples = [
flags: '',
--format X: ['--format', '{id} automark={automark}'],
'--automark downloaded',
]
p_channel_list.add_argument(
'--format',
default='{id}:{name}',
help='''
A string like "{id}: {name}" to format the attributes of the channel. A string like "{id}: {name}" to format the attributes of the channel.
The available attributes are id, name, automark, autorefresh, The available attributes are id, name, automark, autorefresh,
uploads_playlist, queuefile_extension. uploads_playlist, queuefile_extension.
If you are using --channel_list as listargs for another command, then If you are using --channel_list as listargs for another command, then
this argument is not relevant. this argument is not relevant.
> ycdl_cli.py channel_list
Examples:
> ycdl_cli.py channel_list
> ycdl_cli.py channel_list --format "{id} automark={automark}"
''', ''',
)
p_channel_list.add_argument(
'--automark',
help='''
Only show channels with this automark, pending, downloaded, or ignored.
''',
)
p_channel_list.set_defaults(func=channel_list_argparse)
delete_channel=''' ################################################################################################
delete_channel:
p_delete_channel = subparsers.add_parser(
'delete_channel',
aliases=['delete-channel'],
description='''
Delete a channel and all its videos from the database. Delete a channel and all its videos from the database.
You can pass multiple channel IDs.
Uses pipeable to support !c clipboard, !i stdin.
> ycdl_cli.py delete_channel channel_id [channel_id channel_id...]
> ycdl_cli.py delete_channel --channel_list listargs
Examples:
# Delete one channel
> ycdl_cli.py delete_channel UCOYBuFGi8T3NM5fNAptCLCw
# Delete many channels
> ycdl_cli.py delete_channel UCOYBuFGi8T3NM5fNAptCLCw UCmu9PVIZBk-ZCi-Sk2F2utA
# Delete all channels in the database
> ycdl_cli.py delete_channel --channel-list
See ycdl_cli.py channel_list --help for help with listargs.
''', ''',
)
p_delete_channel.examples = [
{'args': 'UCOYBuFGi8T3NM5fNAptCLCw', 'comment': 'Delete one channel'},
{'args': 'UCOYBuFGi8T3NM5fNAptCLCw UCmu9PVIZBk-ZCi-Sk2F2utA', 'comment': 'Delete many channels'},
{'args': '--channel-list --automark ignored', 'comment': 'Delete all channels that use the ignored automark'},
]
p_delete_channel.add_argument(
'channel_ids',
nargs='*',
help='''
One or more channel IDs to delete.
download_video=''' Uses pipeable to support !c clipboard, !i stdin lines of IDs.
download_video: ''',
)
p_delete_channel.add_argument(
'--yes',
dest='autoyes',
action='store_true',
help='''
Commit the database without prompting.
''',
)
p_delete_channel.add_argument(
'--channel_list',
'--channel-list',
dest='channel_list_args',
nargs='...',
help='''
All remaining arguments will go to the channel_list command to generate
the list of channels to delete. Do not worry about --format.
See channel_list --help for help.
''',
)
p_delete_channel.set_defaults(func=delete_channel_argparse)
################################################################################################
p_download_video = subparsers.add_parser(
'download_video',
aliases=['download-video'],
description='''
Create the queuefiles for one or more videos. Create the queuefiles for one or more videos.
They will be placed in the channel's download_directory if it has one, or The video will have its state set to "downloaded".
else the download_directory in the ycdl.json config file. The video will ''',
have its state set to "downloaded". )
p_download_video.examples = [
Uses pipeable to support !c clipboard, !i stdin. {'args': 'thOifuHs6eY', 'comment': 'Download one video'},
{'args': 'yJ-oASr_djo vHuFizITMdA --force', 'comment': 'Force download many videos'},
> ycdl_cli.py download_video video_id [video_id video_id...] <flags> {'args': '--video_list --channel UCvBv3PCvD9v-IKKTkd94XPg', 'comment': 'Download all videos from this channel'},
> ycdl_cli.py download_video <flags> --video_list listargs {'args': '--force --video_list --state downloaded', 'comment': 'Force re-download all videos that have already been downloaded'},
]
flags: p_download_video.add_argument(
--download_directory X: 'video_ids',
nargs='*',
help='''
Uses pipeable to support !c clipboard, !i stdin lines of IDs.
''',
)
p_download_video.add_argument(
'--download_directory',
'--download-directory',
default=None,
help='''
By default, the queuefile will be placed in the channel's By default, the queuefile will be placed in the channel's
download_directory if it has one, or the download_directory in the download_directory if it has one, or the download_directory in the
ycdl.json config file. You can pass this argument to override both ycdl.json config file. You can pass this argument to override both
of those. of those and use a specific directory.
''',
--force: )
p_download_video.add_argument(
'--force',
action='store_true',
help='''
By default, a video that is already marked as downloaded will not be By default, a video that is already marked as downloaded will not be
downloaded again. You can add this to make the queuefiles for those downloaded again. You can add this to make the queuefiles for those
videos anyway. videos anyway.
''',
--queuefile_extension X: )
p_download_video.add_argument(
'--queuefile_extension',
'--queuefile-extension',
default=None,
help='''
By default, the queuefile extension is taken from the channel or the By default, the queuefile extension is taken from the channel or the
config file. You can pass this argument to override both of those. config file. You can pass this argument to override both of those.
Examples:
# Download one video
> ycdl_cli.py download_video thOifuHs6eY
# Force download many videos
> ycdl_cli.py download_video yJ-oASr_djo vHuFizITMdA --force
# Download all videos from this channel
> ycdl_cli.py download_video --video_list --channel UCvBv3PCvD9v-IKKTkd94XPg
# Force re-download all videos that have already been downloaded
> ycdl_cli.py download_video --force --video_list --state downloaded
See ycdl_cli.py video_list --help for help with listargs.
''', ''',
)
p_download_video.add_argument(
'--yes',
dest='autoyes',
action='store_true',
help='''
Commit the database without prompting.
''',
)
p_download_video.add_argument(
'--video_list',
'--video-list',
dest='video_list_args',
nargs='...',
help='''
All remaining arguments will go to the video_list command to generate the
list of channels to delete. Do not worry about --format.
See video_list --help for help.
''',
)
p_download_video.set_defaults(func=download_video_argparse)
init=''' ################################################################################################
init:
p_init = subparsers.add_parser(
'init',
description='''
Create a new YCDL database in the current directory. Create a new YCDL database in the current directory.
> ycdl_cli.py init
''', ''',
)
p_init.set_defaults(func=init_argparse)
refresh_channels=''' ################################################################################################
refresh_channels:
p_refresh_channels = subparsers.add_parser(
'refresh_channels',
aliases=['refresh-channels'],
description='''
Refresh some or all channels in the database. Refresh some or all channels in the database.
New videos will have their state marked with the channel's automark value, New videos will have their state marked with the channel's automark value,
and queuefiles will be created for channels with automark=downloaded. and queuefiles will be created for channels with automark=downloaded.
''',
> ycdl_cli.py refresh_channels <flags> )
p_refresh_channels.examples = [
flags: '--force',
--channels X Y Z: '--channels UC1_uAIS3r8Vu6JjXWvastJg',
]
p_refresh_channels.add_argument(
'--channels',
nargs='*',
help='''
Any number of channel IDs. Any number of channel IDs.
If omitted, all channels will be refreshed. If omitted, all channels will be refreshed.
''',
--force: )
p_refresh_channels.add_argument(
'--force',
action='store_true',
help='''
If omitted, only new videos are found. If omitted, only new videos are found.
If included, channels are refreshed completely. This may be slow and If included, channels are refreshed completely. This may be slow and
cost a lot of API calls. cost a lot of API calls.
Examples:
> ycdl_cli.py refresh_channels --force
> ycdl_cli.py refresh_channels --channels UC1_uAIS3r8Vu6JjXWvastJg
''', ''',
)
p_refresh_channels.add_argument(
'--yes',
dest='autoyes',
action='store_true',
help='''
Commit the database without prompting.
''',
)
p_refresh_channels.set_defaults(func=refresh_channels_argparse)
video_list=''' ################################################################################################
video_list:
p_video_list = subparsers.add_parser(
'video_list',
aliases=['video-list'],
description='''
Print videos in the database. Print videos in the database.
Note: If you want to use this in a command pipeline, please specify Note: If you want to use this in a command pipeline, please specify
--format instead of relying on the default. --format instead of relying on the default.
''',
> ycdl_cli.py video_list <flags> )
p_video_list.examples = [
flags: '--state pending --limit 100',
--channel X: '--channel UCzIiTeduaanyEboRfwJJznA --orderby views',
]
p_video_list.add_argument(
'--channel',
dest='channel_id',
default=None,
help='''
A channel ID to list videos from. A channel ID to list videos from.
''',
--format X: )
p_video_list.add_argument(
'--format',
default='{published_string}:{id}:{title}',
help='''
A string like "{published_string}:{id} {title}" to format the A string like "{published_string}:{id} {title}" to format the
attributes of the video. The available attributes are author_id, attributes of the video. The available attributes are author_id,
duration, id, live_broadcast, published, published_string, state, duration, id, live_broadcast, published, published_string, state,
title, views. title, views.
--limit X:
Only show up to X results.
--orderby X:
Order the results by published, views, duration, or random.
--state X:
Only show videos with this state.
Examples:
> ycdl_cli.py video_list --state pending --limit 100
> ycdl_cli.py video_list --channel UCzIiTeduaanyEboRfwJJznA --orderby views
''', ''',
) )
p_video_list.add_argument(
DOCSTRING = betterhelp.add_previews(DOCSTRING, SUB_DOCSTRINGS) '--limit',
type=int,
@operatornotify.main_decorator(subject='ycdl_cli') default=None,
@vlogging.main_decorator help='''
def main(argv): Only show up to this many results.
parser = argparse.ArgumentParser(description=__doc__) ''',
subparsers = parser.add_subparsers() )
p_video_list.add_argument(
primary_args = [] '--orderby',
video_list_args = [] default=None,
channel_list_args = [] help='''
mode = primary_args Order the results by published, views, duration, or random.
for arg in argv: ''',
if 0: )
pass p_video_list.add_argument(
elif arg in {'--channel_list', '--channel-list'}: '--state',
mode = channel_list_args default=None,
elif arg in {'--video_list', '--video-list'}: help='''
mode = video_list_args Only show videos with this state, pending, downloaded, or ignored.
else: ''',
mode.append(arg) )
p_add_channel = subparsers.add_parser('add_channel', aliases=['add-channel'])
p_add_channel.add_argument('channel_id')
p_add_channel.add_argument('--automark', default='pending')
p_add_channel.add_argument('--download_directory', '--download-directory', default=None)
p_add_channel.add_argument('--name', default=None)
p_add_channel.add_argument('--no_videos', '--no-videos', dest='get_videos', action='store_false')
p_add_channel.add_argument('--queuefile_extension', '--queuefile-extension', default=None)
p_add_channel.add_argument('--yes', dest='autoyes', action='store_true')
p_add_channel.set_defaults(func=add_channel_argparse)
p_channel_list = subparsers.add_parser('channel_list', aliases=['channel-list'])
p_channel_list.add_argument('--format', default='{id}:{name}')
p_channel_list.set_defaults(func=channel_list_argparse)
p_delete_channel = subparsers.add_parser('delete_channel', aliases=['delete-channel'])
p_delete_channel.add_argument('channel_ids', nargs='*')
p_delete_channel.add_argument('--yes', dest='autoyes', action='store_true')
p_delete_channel.set_defaults(func=delete_channel_argparse)
p_download_video = subparsers.add_parser('download_video', aliases=['download-video'])
p_download_video.add_argument('video_ids', nargs='*')
p_download_video.add_argument('--download_directory', '--download-directory', default=None)
p_download_video.add_argument('--force', action='store_true')
p_download_video.add_argument('--queuefile_extension', '--queuefile-extension', default=None)
p_download_video.add_argument('--yes', dest='autoyes', action='store_true')
p_download_video.set_defaults(func=download_video_argparse)
p_init = subparsers.add_parser('init')
p_init.set_defaults(func=init_argparse)
p_refresh_channels = subparsers.add_parser('refresh_channels', aliases=['refresh-channels'])
p_refresh_channels.add_argument('--channels', nargs='*')
p_refresh_channels.add_argument('--force', action='store_true')
p_refresh_channels.add_argument('--yes', dest='autoyes', action='store_true')
p_refresh_channels.set_defaults(func=refresh_channels_argparse)
p_video_list = subparsers.add_parser('video_list', aliases=['video-list'])
p_video_list.add_argument('--channel', dest='channel_id', default=None)
p_video_list.add_argument('--format', default='{published_string}:{id}:{title}')
p_video_list.add_argument('--limit', type=int, default=None)
p_video_list.add_argument('--orderby', default=None)
p_video_list.add_argument('--state', default=None)
p_video_list.set_defaults(func=video_list_argparse) p_video_list.set_defaults(func=video_list_argparse)
## ##
def postprocessor(args): def postprocessor(args):
args.video_list_args = p_video_list.parse_args(video_list_args) if hasattr(args, 'video_list_args'):
args.channel_list_args = p_channel_list.parse_args(channel_list_args) args.video_list_args = p_video_list.parse_args(args.video_list_args)
if hasattr(args, 'channel_list_args'):
args.channel_list_args = p_channel_list.parse_args(args.channel_list_args)
return args return args
try: try:
return betterhelp.subparser_main( return betterhelp.go(parser, argv, args_postprocessor=postprocessor)
primary_args,
parser,
main_docstring=DOCSTRING,
sub_docstrings=SUB_DOCSTRINGS,
args_postprocessor=postprocessor,
)
except ycdl.exceptions.NoClosestYCDLDB as exc: except ycdl.exceptions.NoClosestYCDLDB as exc:
pipeable.stderr(exc.error_message) pipeable.stderr(exc.error_message)
pipeable.stderr('Try `ycdl_cli.py init` to create the database.') pipeable.stderr('Try `ycdl_cli.py init` to create the database.')

View file

@ -1,29 +1,5 @@
''' '''
ycdl_flask_dev
==============
This file is the gevent launcher for local / development use. This file is the gevent launcher for local / development use.
> ycdl_flask_dev port <flags>
port:
Port number on which to run the server. Default 5000.
--dont-create:
If this flag is passed, YCDL will raise an error if there is not an
existing ycdl.db in the current directory.
--https:
If this flag is not passed, HTTPS will automatically be enabled if the port
is 443. You can pass this flag to enable HTTPS on other ports.
We expect to find ycdl.key and ycdl.crt in frontends/ycdl_flask/https.
--localhost-only:
If this flag is passed, only localhost will be able to access the server.
Other users on the LAN will be blocked.
--refresh-rate X:
Starts a background thread that refreshes all channels once every X seconds.
''' '''
import gevent.monkey; gevent.monkey.patch_all() import gevent.monkey; gevent.monkey.patch_all()
@ -111,15 +87,51 @@ def ycdl_flask_launch_argparse(args):
@vlogging.main_decorator @vlogging.main_decorator
@operatornotify.main_decorator(subject='YCDL', notify_every_line=True) @operatornotify.main_decorator(subject='YCDL', notify_every_line=True)
def main(argv): def main(argv):
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(
description='''
parser.add_argument('port', nargs='?', type=int, default=5000) This file is the gevent launcher for local / development use.
parser.add_argument('--https', dest='use_https', action='store_true', default=None) ''',
parser.add_argument('--localhost_only', '--localhost-only', dest='localhost_only', action='store_true') )
parser.add_argument('--refresh_rate', '--refresh-rate', dest='refresh_rate', type=int, default=None) parser.add_argument(
'port',
nargs='?',
type=int,
default=5000,
help='''
Port number on which to run the server.
''',
)
parser.add_argument(
'--https',
dest='use_https',
action='store_true',
help='''
If this flag is not passed, HTTPS will automatically be enabled if the port
is 443. You can pass this flag to enable HTTPS on other ports.
We expect to find ycdl.key and ycdl.crt in frontends/ycdl_flask/https.
''',
)
parser.add_argument(
'--localhost_only',
'--localhost-only',
action='store_true',
help='''
If this flag is passed, only localhost will be able to access the server.
Other users on the LAN will be blocked.
''',
)
parser.add_argument(
'--refresh_rate',
'--refresh-rate',
type=int,
default=None,
help='''
Starts a background thread that refreshes all channels once every X seconds.
''',
)
parser.set_defaults(func=ycdl_flask_launch_argparse) parser.set_defaults(func=ycdl_flask_launch_argparse)
return betterhelp.single_main(argv, parser, __doc__) return betterhelp.go(parser, argv)
if __name__ == '__main__': if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:])) raise SystemExit(main(sys.argv[1:]))