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} Add a channel to the database.
''',
{init} )
p_add_channel.examples = [
{refresh_channels} 'UCFhXFikryT4aFcLkLw2LBLA',
'UCFhXFikryT4aFcLkLw2LBLA --automark downloaded',
{video_list} 'UCLx053rWZxCiYWsBETgdKrQ --name LGR',
]
You can add --yes to avoid the "Commit?" prompt on commands that modify the db. p_add_channel.add_argument(
'channel_id',
TO SEE DETAILS ON EACH COMMAND, RUN )
> ycdl_cli.py <command> --help p_add_channel.add_argument(
''' '--automark',
default='pending',
SUB_DOCSTRINGS = dict( help='''
add_channel='''
add_channel:
Add a channel to the database.
> ycdl_cli.py add_channel channel_id <flags>
flags:
--automark X:
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: )
p_add_channel.add_argument(
'--name',
default=None,
help='''
Override the channel's own name with a name of your choosing. 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.
--no_videos: You should run refresh_channels later.
By default, the channel's videos will be fetched right away. ''',
Add this argument if you don't want to do that yet. )
p_add_channel.add_argument(
--queuefile_extension X: '--queuefile_extension',
'--queuefile-extension',
type=str,
default=None,
help='''
Set the queuefile extension for all videos downloaded from this channel. 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)
Examples: ################################################################################################
> ycdl_cli.py add_channel UCFhXFikryT4aFcLkLw2LBLA
''',
channel_list=''' p_channel_list = subparsers.add_parser(
channel_list: 'channel_list',
Print all channels in the database. aliases=['channel-list'],
description='''
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.
''',
)
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)
> ycdl_cli.py channel_list ################################################################################################
Examples: p_delete_channel = subparsers.add_parser(
> ycdl_cli.py channel_list 'delete_channel',
> ycdl_cli.py channel_list --format "{id} automark={automark}" aliases=['delete-channel'],
''', description='''
Delete a channel and all its videos from the database.
''',
)
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.
delete_channel=''' Uses pipeable to support !c clipboard, !i stdin lines of IDs.
delete_channel: ''',
Delete a channel and all its videos from the database. )
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)
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...] p_download_video = subparsers.add_parser(
> ycdl_cli.py delete_channel --channel_list listargs 'download_video',
aliases=['download-video'],
description='''
Create the queuefiles for one or more videos.
Examples: The video will have its state set to "downloaded".
# Delete one channel ''',
> ycdl_cli.py delete_channel UCOYBuFGi8T3NM5fNAptCLCw )
p_download_video.examples = [
# Delete many channels {'args': 'thOifuHs6eY', 'comment': 'Download one video'},
> ycdl_cli.py delete_channel UCOYBuFGi8T3NM5fNAptCLCw UCmu9PVIZBk-ZCi-Sk2F2utA {'args': 'yJ-oASr_djo vHuFizITMdA --force', 'comment': 'Force download many videos'},
{'args': '--video_list --channel UCvBv3PCvD9v-IKKTkd94XPg', 'comment': 'Download all videos from this channel'},
# Delete all channels in the database {'args': '--force --video_list --state downloaded', 'comment': 'Force re-download all videos that have already been downloaded'},
> ycdl_cli.py delete_channel --channel-list ]
p_download_video.add_argument(
See ycdl_cli.py channel_list --help for help with listargs. 'video_ids',
''', nargs='*',
help='''
download_video=''' Uses pipeable to support !c clipboard, !i stdin lines of IDs.
download_video: ''',
Create the queuefiles for one or more videos. )
p_download_video.add_argument(
They will be placed in the channel's download_directory if it has one, or '--download_directory',
else the download_directory in the ycdl.json config file. The video will '--download-directory',
have its state set to "downloaded". default=None,
help='''
Uses pipeable to support !c clipboard, !i stdin.
> ycdl_cli.py download_video video_id [video_id video_id...] <flags>
> ycdl_cli.py download_video <flags> --video_list listargs
flags:
--download_directory X:
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.
''',
)
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)
Examples: ################################################################################################
# Download one video
> ycdl_cli.py download_video thOifuHs6eY
# Force download many videos p_init = subparsers.add_parser(
> ycdl_cli.py download_video yJ-oASr_djo vHuFizITMdA --force 'init',
description='''
Create a new YCDL database in the current directory.
''',
)
p_init.set_defaults(func=init_argparse)
# 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 p_refresh_channels = subparsers.add_parser(
> ycdl_cli.py download_video --force --video_list --state downloaded 'refresh_channels',
aliases=['refresh-channels'],
description='''
Refresh some or all channels in the database.
See ycdl_cli.py video_list --help for help with listargs. New videos will have their state marked with the channel's automark value,
''', and queuefiles will be created for channels with automark=downloaded.
''',
init=''' )
init: p_refresh_channels.examples = [
Create a new YCDL database in the current directory. '--force',
'--channels UC1_uAIS3r8Vu6JjXWvastJg',
> ycdl_cli.py init ]
''', p_refresh_channels.add_argument(
'--channels',
refresh_channels=''' nargs='*',
refresh_channels: help='''
Refresh some or all channels in the database.
New videos will have their state marked with the channel's automark value,
and queuefiles will be created for channels with automark=downloaded.
> ycdl_cli.py refresh_channels <flags>
flags:
--channels X Y Z:
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.
''',
)
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)
Examples: ################################################################################################
> ycdl_cli.py refresh_channels --force
> ycdl_cli.py refresh_channels --channels UC1_uAIS3r8Vu6JjXWvastJg
''',
video_list=''' p_video_list = subparsers.add_parser(
video_list: 'video_list',
Print videos in the database. aliases=['video-list'],
description='''
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. p_video_list.add_argument(
'--limit',
--orderby X: type=int,
default=None,
help='''
Only show up to this many results.
''',
)
p_video_list.add_argument(
'--orderby',
default=None,
help='''
Order the results by published, views, duration, or random. Order the results by published, views, duration, or random.
''',
--state X: )
Only show videos with this state. p_video_list.add_argument(
'--state',
Examples: default=None,
> ycdl_cli.py video_list --state pending --limit 100 help='''
> ycdl_cli.py video_list --channel UCzIiTeduaanyEboRfwJJznA --orderby views Only show videos with this state, pending, downloaded, or ignored.
''', ''',
) )
DOCSTRING = betterhelp.add_previews(DOCSTRING, SUB_DOCSTRINGS)
@operatornotify.main_decorator(subject='ycdl_cli')
@vlogging.main_decorator
def main(argv):
parser = argparse.ArgumentParser(description=__doc__)
subparsers = parser.add_subparsers()
primary_args = []
video_list_args = []
channel_list_args = []
mode = primary_args
for arg in argv:
if 0:
pass
elif arg in {'--channel_list', '--channel-list'}:
mode = channel_list_args
elif arg in {'--video_list', '--video-list'}:
mode = video_list_args
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:]))