示例#1
0
def parser():
    description = (
        'Plex Manager\n\nYou will be securely prompted for your password for the first login, after which a session'
        ' token will be cached')
    parser = ArgParser(description=description)

    with parser.add_subparser('action', 'sync',
                              help='Sync Plex information') as sync_parser:
        ratings_parser = sync_parser.add_subparser(
            'sync_action',
            'ratings',
            help='Sync song rating information between Plex and files')
        ratings_parser.add_argument('direction',
                                    choices=('to_files', 'from_files'),
                                    help='Direction to sync information')
        ratings_parser.add_argument(
            '--path_filter',
            '-f',
            help=
            'If specified, paths that will be synced must contain the given text (not case sensitive)'
        )

        playlists_parser = sync_parser.add_subparser(
            'sync_action',
            'playlists',
            help='Sync playlists with custom filters')

    obj_types = ('track', 'artist', 'album', 'tracks', 'artists', 'albums')
    ops = (
        'contains, endswith, exact, exists, gt, gte, icontains, iendswith, iexact, in, iregex, istartswith, like, lt, '
        'lte, ne, regex, startswith')

    with parser.add_subparser('action', 'find',
                              help='Find Plex information') as find_parser:
        find_parser.add_argument('obj_type',
                                 choices=obj_types,
                                 help='Object type')
        find_parser.add_argument('title',
                                 nargs='*',
                                 default=None,
                                 help='Object title (optional)')
        find_parser.add_argument(
            '--escape',
            '-e',
            default='()',
            help=
            'Escape the provided regex special characters (default: %(default)r)'
        )
        find_parser.add_argument(
            '--allow_inst',
            '-I',
            action='store_true',
            help=
            'Allow search results that include instrumental versions of songs')
        find_parser.add_argument(
            '--full_info',
            '-F',
            action='store_true',
            help='Print all available info about the discovered objects')
        find_parser.add_argument(
            '--format',
            '-f',
            choices=PRINTER_FORMATS,
            default='yaml',
            help='Output format to use for --full_info (default: %(default)s)')
        find_parser.add_argument(
            'query',
            nargs=argparse.REMAINDER,
            help=
            f'Query in the format --field[__operation] value; valid operations: {ops}'
        )

    with parser.add_subparser('action', 'rate',
                              help='Update ratings in Plex') as rate_parser:
        rate_parser.add_argument('obj_type',
                                 choices=obj_types,
                                 help='Object type')
        rate_parser.add_argument('rating', type=int, help='Rating out of 10')
        rate_parser.add_argument('title',
                                 nargs='*',
                                 default=None,
                                 help='Object title (optional)')
        rate_parser.add_argument(
            '--escape',
            '-e',
            default='()',
            help=
            'Escape the provided regex special characters (default: %(default)r)'
        )
        rate_parser.add_argument(
            '--allow_inst',
            '-I',
            action='store_true',
            help=
            'Allow search results that include instrumental versions of songs')
        rate_parser.add_argument(
            'query',
            nargs=argparse.REMAINDER,
            help=
            f'Query in the format --field[__operation] value; valid operations: {ops}'
        )

    with parser.add_subparser(
            'action',
            'rate_offset',
            help='Update all track ratings in Plex with an offset'
    ) as rate_offset_parser:
        rate_offset_parser.add_argument(
            '--min_rating',
            '-min',
            type=int,
            default=2,
            help='Minimum rating for which a change will be made')
        rate_offset_parser.add_argument(
            '--max_rating',
            '-max',
            type=int,
            default=10,
            help='Maximum rating for which a change will be made')
        rate_offset_parser.add_argument('--offset',
                                        '-o',
                                        type=int,
                                        default=-1,
                                        help='Adjustment to make')

    with parser.add_subparser(
            'action', 'playlist',
            help='Save or compare playlists') as playlist_parser:
        with playlist_parser.add_subparser(
                'sub_action', 'dump', help='Save playlists') as playlist_dump:
            playlist_dump.add_argument(
                'path', help='Location to write the playlist dump')
            playlist_dump.add_argument(
                '--playlist',
                '-p',
                help='Dump the specified playlist (default: all)')
        with playlist_parser.add_subparser(
                'sub_action', 'compare',
                help='Compare playlists') as playlist_cmp:
            playlist_cmp.add_argument(
                'path', help='Location of the playlist dump to compare')
            playlist_cmp.add_argument(
                '--playlist',
                '-p',
                help='Compare the specified playlist (default: all)')
            playlist_cmp.add_argument(
                '--strict',
                '-s',
                action='store_true',
                help=
                'Perform a strict comparison (default: by artist/album/title)')
        with playlist_parser.add_subparser(
                'sub_action', 'list',
                help='List playlists in a dump') as playlist_list:
            playlist_list.add_argument(
                'path', help='Location of the playlist dump to read')

    parser.add_common_sp_arg(
        '--server_path_root',
        '-r',
        metavar='PATH',
        help=
        'The root of the path to use from this computer to generate paths to files from the path used by Plex.  When you click on the "..." for a song in Plex and click "Get Info", there will be a path in the "Files" box - for example, "/media/Music/a_song.mp3".  If you were to access that file from this computer, and the path to that same file is "//my_nas/media/Music/a_song.mp3", then the server_path_root would be "//my_nas/" (only needed when not already cached)'
    )
    parser.add_common_sp_arg(
        '--server_url',
        '-u',
        metavar='URL',
        help=
        'The proto://host:port to use to connect to your local Plex server - for example: "https://10.0.0.100:12000" (only needed when not already cached)'
    )
    parser.add_common_sp_arg(
        '--username',
        '-n',
        help='Plex username (only needed when a token is not already cached)')
    parser.add_common_sp_arg(
        '--config_path',
        '-c',
        metavar='PATH',
        default='~/.config/plexapi/config.ini',
        help=
        'Config file in which your token and server_path_root / server_url are stored (default: %(default)s)'
    )
    parser.add_common_sp_arg(
        '--music_library',
        '-m',
        default=None,
        help='Name of the Music library to use (default: Music)')

    parser.include_common_args('verbosity', 'dry_run')
    return parser
示例#2
0
def parser():
    # fmt: off
    parser = ArgParser(description='Music Manager')

    # region File Actions
    with parser.add_subparser('action',
                              'show',
                              help='Show song/tag information') as show_parser:
        for name, help_text in SHOW_ARGS.items():
            with show_parser.add_subparser('sub_action', name,
                                           help=help_text) as _parser:
                _parser.add_argument(
                    'path',
                    nargs='*',
                    default=['.'],
                    help=
                    'Paths for music files or directories containing music files'
                )
                if name in ('info', 'unique', 'table'):
                    _parser.add_argument('--tags',
                                         '-t',
                                         nargs='+',
                                         help='The tags to display',
                                         required=(name == 'unique'))
                if name == 'info':
                    _parser.add_argument('--no_trim',
                                         '-T',
                                         action='store_true',
                                         help='Do not trim tag IDs')
                if name == 'processed':
                    _parser.add_argument(
                        '--expand',
                        '-x',
                        action='count',
                        default=0,
                        help=
                        'Expand entities with a lot of nested info (may be specified multiple times to increase expansion level)'
                    )
                    _parser.add_argument(
                        '--only_errors',
                        '-E',
                        action='store_true',
                        help='Only print entries with processing errors')

    with parser.add_subparser(
            'action',
            'path2tag',
            help='Update tags based on the path to each file') as p2t_parser:
        p2t_parser.add_argument(
            'path',
            nargs='+',
            help=
            'One or more paths of music files or directories containing music files'
        )
        p2t_parser.add_argument('--title',
                                '-t',
                                action='store_true',
                                help='Update title based on filename')
        p2t_parser.add_argument('--yes',
                                '-y',
                                action='store_true',
                                help='Skip confirmation prompts')

    with parser.add_subparser(
            'action',
            'update',
            help=
            'Set the value of the given tag on all music files in the given path'
    ) as set_parser:
        set_parser.add_argument(
            'path',
            nargs='+',
            help=
            'One or more paths of music files or directories containing music files'
        )
        set_parser.add_argument(
            '--no_album_move',
            '-M',
            action='store_true',
            help='Do not rename the album directory (only applies to --load/-L)'
        )
        set_parser.add_argument(
            '--replace_genre',
            '-G',
            action='store_true',
            help='Replace genre instead of combining genres')

        set_from_file = set_parser.add_argument_group(
            'Load File Options',
            'Options for loading updates from a file - may notbe combined with arguments from other option groups'
        )
        set_from_file.add_argument(
            '--load',
            '-L',
            metavar='PATH',
            help=
            'Load updates from a json file (may not be combined with other options)'
        )
        set_from_file.add_argument(
            '--destination',
            '-d',
            metavar='PATH',
            help=
            f'Destination base directory for sorted files (default: {DEFAULT_DEST_DIR})'
        )

        set_from_args = set_parser.add_argument_group('Tag Update Options')
        set_from_args.add_argument('--tag',
                                   '-t',
                                   nargs='+',
                                   help='Tag ID(s) to modify (required)')
        set_from_args.add_argument(
            '--value',
            '-V',
            help='Value to replace existing values with (required)')
        set_from_args.add_argument(
            '--replace',
            '-r',
            nargs='+',
            help=
            'If specified, only replace tag values that match the given patterns(s)'
        )
        set_from_args.add_argument(
            '--partial',
            '-p',
            action='store_true',
            help=
            'Update only parts of tags that match a pattern specified via --replace/-r'
        )

        set_parser.add_mutually_exclusive_arg_sets(set_from_file,
                                                   set_from_args)

    with parser.add_subparser(
            'action',
            'clean',
            help='Clean undesirable tags from the specified files'
    ) as clean_parser:
        clean_parser.add_argument(
            'path',
            nargs='+',
            help=
            'One or more paths of music files or directories containing music files'
        )
        bpm_group = clean_parser.add_mutually_exclusive_group()
        bpm_group.add_argument(
            '--bpm',
            '-b',
            action='store_true',
            default=None,
            help=
            'Add a BPM tag if it is not already present (default: True if aubio is installed)'
        )
        bpm_group.add_argument(
            '--no_bpm',
            '-B',
            dest='bpm',
            action='store_false',
            help='Do not add a BPM tag if it is not already present')

    with parser.add_subparser(
            'action',
            'remove',
            help='Remove the specified tags from the specified files'
    ) as rm_parser:
        rm_parser.add_argument(
            'path',
            nargs='+',
            help=
            'One or more paths of music files or directories containing music files'
        )
        rm_group = rm_parser.add_mutually_exclusive_group()
        rm_group.add_argument('--tag',
                              '-t',
                              nargs='+',
                              help='Tag ID(s) to remove')
        rm_group.add_argument('--all',
                              '-A',
                              action='store_true',
                              help='Remove ALL tags')

    with parser.add_subparser(
            'action', 'bpm',
            help='Add BPM info to the specified files') as bpm_parser:
        bpm_parser.add_argument(
            'path',
            nargs='+',
            help=
            'One or more paths of music files or directories containing music files'
        )
        bpm_parser.include_common_args(parallel=4)
        # bpm_parser.add_argument('--parallel', '-P', type=int, default=1, help='Maximum number of workers to use in parallel (default: %(default)s)'))

    with parser.add_subparser(
            'action',
            'dump',
            help='Dump tag info about the specified files to json'
    ) as dump_parser:
        dump_parser.add_argument(
            'path',
            help='A path for a music file or a directory containing music files'
        )
        dump_parser.add_argument('output', help='The destination file path')
        dump_parser.add_argument(
            '--title_case',
            '-T',
            action='store_true',
            help=
            'Fix track and album names to use Title Case when they are all caps'
        )

    with parser.add_subparser('action',
                              'cover',
                              help='Extract or add cover art') as cover_parser:
        cover_parser.add_argument(
            'path',
            help='A path for a music file or a directory containing music files'
        )

        dump_cover_group = cover_parser.add_argument_group(
            'Save Cover Options')
        dump_cover_group.add_argument(
            '--save',
            '-s',
            metavar='PATH',
            help='Path to save the cover images from the specified file(s)')

        load_cover_group = cover_parser.add_argument_group(
            'Load Cover Options')
        load_cover_group.add_argument('--load',
                                      '-L',
                                      metavar='PATH',
                                      help='Path to an image file')
        load_cover_group.add_argument(
            '--max_width',
            '-w',
            type=int,
            default=1200,
            help='Resize the provided image if it is larger than this value')

        del_cover_group = cover_parser.add_argument_group('Save Cover Options')
        del_cover_group.add_argument('--remove',
                                     '-R',
                                     action='store_true',
                                     help='Remove all cover images')

        cover_parser.add_mutually_exclusive_arg_sets(dump_cover_group,
                                                     load_cover_group,
                                                     del_cover_group)
    # endregion

    with parser.add_subparser(
            'action', 'wiki',
            help='Wiki matching / informational functions') as wiki_parser:
        with wiki_parser.add_subparser(
                'sub_action',
                'pprint',
                help='Pretty-print the parsed page content') as pp_parser:
            pp_parser.add_argument('url', help='A wiki entity URL')
            pp_parser.add_argument('--mode',
                                   '-m',
                                   choices=('content', 'processed', 'reprs',
                                            'headers', 'raw'),
                                   default='content',
                                   help='Pprint mode (default: %(default)s)')

        with wiki_parser.add_subparser(
                'sub_action', 'raw',
                help='Print the raw page content') as ppr_parser:
            ppr_parser.add_argument('url', help='A wiki entity URL')

        with wiki_parser.add_subparser(
                'sub_action',
                'show',
                help='Show info about the entity with the given URL'
        ) as ws_parser:
            ws_parser.add_argument('identifier',
                                   help='A wiki URL or title/name')
            ws_parser.add_argument(
                '--expand',
                '-x',
                action='count',
                default=0,
                help=
                'Expand entities with a lot of nested info (may be specified multiple times to increase expansion level)'
            )
            ws_parser.add_argument(
                '--limit',
                '-L',
                type=int,
                default=0,
                help=
                'Maximum number of discography entry parts to show for a given album (default: unlimited)'
            )
            ws_parser.add_argument(
                '--types',
                '-t',
                nargs='+',
                help=
                'Filter albums to only those that match the specified types')
            ws_parser.add_argument(
                '--type',
                '-T',
                help=
                'An EntertainmentEntity subclass to require that the given page matches'
            )

        with wiki_parser.add_subparser(
                'sub_action',
                'update',
                help='Update tracks in the given path(s) based on wiki info'
        ) as upd_parser:
            upd_parser.add_argument(
                'path',
                nargs='+',
                help=
                'One or more paths of music files or directories containing music files'
            )
            upd_parser.add_argument(
                '--destination',
                '-d',
                metavar='PATH',
                default=DEFAULT_DEST_DIR,
                help=
                'Destination base directory for sorted files (default: %(default)s)'
            )
            upd_parser.add_argument(
                '--url',
                '-u',
                help=
                'A wiki URL (can only specify one file/directory when providing a URL)'
            )
            upd_parser.add_argument(
                '--soloist',
                '-S',
                action='store_true',
                help=
                'For solo artists, use only their name instead of including their group, and do not sort them with their group'
            )
            upd_parser.add_argument(
                '--collab_mode',
                '-c',
                choices=('title', 'artist', 'both'),
                default='artist',
                help=
                'List collaborators in the artist tag, the title tag, or both (default: %(default)s)'
            )
            upd_parser.add_argument(
                '--hide_edition',
                '-E',
                action='store_true',
                help=
                'Exclude the edition from the album title, if present (default: include it)'
            )
            upd_parser.add_argument(
                '--title_case',
                '-T',
                action='store_true',
                help=
                'Fix track and album names to use Title Case when they are all caps'
            )
            upd_parser.add_argument(
                '--artist',
                '-a',
                metavar='URL',
                help=
                'Force the use of the given artist instead of an automatically discovered one'
            )
            upd_parser.add_argument(
                '--update_cover',
                '-C',
                action='store_true',
                help=
                'Update the cover art for the album if it does not match an image in the matched wiki page'
            )
            upd_parser.add_argument('--no_album_move',
                                    '-M',
                                    action='store_true',
                                    help='Do not rename the album directory')
            upd_parser.add_argument(
                '--artist_only',
                '-I',
                action='store_true',
                help=
                'Only match the artist / only use the artist URL if provided')
            upd_parser.add_argument(
                '--replace_genre',
                '-G',
                action='store_true',
                help='Replace genre instead of combining genres')

            upd_sites = upd_parser.add_argument_group(
                'Site Options').add_mutually_exclusive_group()
            upd_sites.add_argument('--sites',
                                   '-s',
                                   nargs='+',
                                   default=None,
                                   help='The wiki sites to search')
            upd_sites.add_argument('--all',
                                   '-A',
                                   action='store_const',
                                   const=ALL_SITES,
                                   dest='sites',
                                   help='Search all sites')
            upd_sites.add_argument('--ost',
                                   '-O',
                                   action='store_const',
                                   const=['wiki.d-addicts.com'],
                                   dest='sites',
                                   help='Search only wiki.d-addicts.com')

            bpm_group = upd_parser.add_argument_group(
                'BPM Options').add_mutually_exclusive_group()
            bpm_group.add_argument(
                '--bpm',
                '-b',
                action='store_true',
                default=None,
                help=
                'Add a BPM tag if it is not already present (default: True if aubio is installed)'
            )
            bpm_group.add_argument(
                '--no_bpm',
                '-B',
                dest='bpm',
                action='store_false',
                help='Do not add a BPM tag if it is not already present')

            upd_data = upd_parser.add_argument_group(
                'Track Data Options').add_mutually_exclusive_group()
            upd_data.add_argument(
                '--dump',
                '-P',
                metavar='PATH',
                help=
                'Dump track updates to a json file instead of updating the tracks'
            )
            upd_data.add_argument(
                '--load',
                '-L',
                metavar='PATH',
                help=
                'Load track updates from a json file instead of from a wiki')

        with wiki_parser.add_subparser(
                'sub_action',
                'match',
                help='Match tracks in the given path(s) with wiki info'
        ) as match_parser:
            match_parser.add_argument(
                'path',
                nargs='+',
                help=
                'One or more paths of music files or directories containing music files'
            )

        with wiki_parser.add_subparser(
                'sub_action',
                'test',
                help=
                'Test matching of tracks in a given path with a given wiki URL'
        ) as test_parser:
            test_parser.add_argument(
                'path',
                help=
                'One path of music files or directories containing music files'
            )
            test_parser.add_argument(
                'url',
                help=
                'A wiki URL for a page to test whether it matches the given files'
            )

    parser.include_common_args('verbosity', 'dry_run')
    parser.add_common_sp_arg(
        '--match_log',
        action='store_true',
        help='Enable debug logging for the album match processing logger')
    # fmt: on
    return parser