def print_track_info(paths: Paths, tags=None, meta_only=False, trim=True): tags = {tag.upper() for tag in tags} if tags else None suffix = '' if meta_only else ':' for i, music_file in enumerate(iter_music_files(paths)): if i and not meta_only: print() uprint( f'{music_file.path.as_posix()} [{music_file.length_str}] ({music_file.tag_version}){suffix}' ) if not meta_only: if music_file.tags is None: uprint('[No tags]') continue tag_name_map = TYPED_TAG_DISPLAY_NAME_MAP.get( music_file.tag_type, {}) tbl = Table(SimpleColumn('Tag'), SimpleColumn('Tag Name'), SimpleColumn('Value'), update_width=True) rows = [] for tag, val in sorted(music_file.tags.items()): if trim and len(tag) > 4: tag = tag[:4] if not tags or (tag in tags): rows.append({ 'Tag': tag, 'Tag Name': tag_name_map.get(tag, '[unknown]'), 'Value': tag_repr(val) }) if rows: tbl.print_rows(rows)
def print_processed_info(paths: Paths, expand=0, only_errors=False): for album_dir in iter_album_dirs(paths): if not only_errors: uprint(f'- Directory: {album_dir}') _print_one_or_set(album_dir, 'names', 'Album', only_errors=only_errors) single_artist = len(album_dir.all_artists) == 1 if not expand or single_artist: _print_one_or_set(album_dir, 'artists', 'Artist', lambda a: a.artist_str(), only_errors) if expand: print_tracks(album_dir, expand)
def show_matches(paths: Paths, sites: StrOrStrs = None): for album_dir in iter_album_dirs(paths): uprint(f'- Album: {album_dir}') try: artists = find_artists(album_dir, sites=sites) except NoArtistFoundException: log.error(f' - Artist: No artist could be found', extra={'color': 11}) except Exception as e: log.error(f' - Artist: {e}', extra={'color': 'red'}, exc_info=True) else: if len(artists) == 1: artist = artists[0] try: uprint(f' - Artist: {artist} / {artist.names}') except Exception: log.error(f' - Artist: Error parsing name:', extra={'color': 'red'}, exc_info=True) else: uprint(f' - Artists ({len(artists)}):') for artist in artists: uprint(f' - {artist} / {artist.names}') try: album = find_album(album_dir, artists) except Exception as e: log.error(f' - Album: {e}', extra={'color': 'red'}, exc_info=True) else: print_de_part(album, 4)
def _print_one_or_set(obj, attr: str, singular: str, str_fn=str, only_errors=False, indent=4): prefix = ' ' * indent plural = singular if singular == 'Processed' else singular + 's' try: objs = getattr(obj, attr) except Exception as e: if only_errors: uprint(f'- Directory: {obj}') log.error(f'{prefix}{plural:12s}: {e}', extra={'color': 'red'}, exc_info=True) else: if not only_errors: if len(objs) == 1: uprint(f'{prefix}{singular:12s}: {str_fn(next(iter(objs)))}') else: text = f'{plural} ({len(objs)})' uprint(f'{prefix}{text:12s}:') for obj in objs: uprint(f'{prefix} - {str_fn(obj)} ')
def print_tracks(album_dir: AlbumDir, expand=0): single_artist = len(album_dir.all_artists) == 1 uprint(f' - Tracks ({len(album_dir)}):') for track in album_dir.songs: uprint(f' - {track.path.name}:') uprint(f' - Title : {track.tag_title!r}') if not single_artist or expand > 1: uprint(f' - Artist : {track.tag_artist!r} =>') _print_one_or_set(track, 'artists', 'Processed', lambda a: repr(a.artist_str()), indent=11) uprint(f' - Album Artist: {track.tag_album_artist!r} =>') _print_one_or_set(track, 'album_artists', 'Processed', lambda a: repr(a.artist_str()), indent=11)
def show_wiki_entity(identifier: str, expand=0, limit=0, alb_types: Optional[Iterable[str]] = None, etype: Optional[str] = None): alb_types = _album_types(alb_types) cls = EntertainmentEntity if etype: for _cls in EntertainmentEntity._subclasses: if _cls.__name__ == etype and issubclass( _cls, EntertainmentEntity): # _subclasses is from WikiEntity cls = _cls break else: raise ValueError( f'Invalid EntertainmentEntity subclass: {etype!r}') if URL_MATCH(identifier): entity = cls.from_url(identifier) else: entity = cls.from_title( identifier, search=True, research=True, strict=1, # name=Name.from_enclosed(identifier) ) uprint(f'{entity}:') if isinstance(entity, DiscographyEntry): print_disco_entry(entity, 2, expand > 0, limit, expand > 1) elif isinstance(entity, Artist): print_artist(entity, 2, expand, expand > 2, expand > 3, alb_types) elif isinstance(entity, Discography): print_discography(entity, 2, expand > 0, expand > 1, expand > 2, alb_types, True) elif isinstance(entity, TVSeries): print_tv_series(entity, 2) else: uprint( f' - No additional information is configured for {entity.__class__.__name__} entities' )
def main(): args = parser().parse_args() init_logging(args.verbose, log_path=None) cache = DBCache(None, db_path=args.path) if args.action == 'list': for key, orig in normalized_keys(cache): uprint(key) elif args.action == 'delete': prefix = '[DRY RUN] Would delete' if args.dry_run else 'Deleting' for key, orig in normalized_keys(cache): if any(fnmatch(key, pat) for pat in args.patterns): log.info('{}: {}'.format(prefix, key)) if not args.dry_run: del cache[orig] elif args.action == 'get': entry = cache[args.key] log.info(entry) else: raise ValueError('Unconfigured action: {}'.format(args.action))
def print_artist(artist: Artist, indent=0, expand_disco=0, editions=False, track_info=False, alb_types: AlbTypes = None): prefix = ' ' * indent uprint(f'{prefix}- {artist.name}:') if names := artist.names: uprint(f'{prefix} Names:') for name in names: # uprint(f'{prefix} - {name}') uprint(f'{prefix} - {name.full_repr(include_versions=False)}') if name.versions: for version in name.versions: # uprint(f'{prefix} - {version}') uprint(f'{prefix} - {version.full_repr()}')
def print_disco_entry(disco_entry: DiscographyEntry, indent=0, editions=False, limit=0, track_info=False): prefix = ' ' * indent suffix = '' if disco_entry.editions else ' [{} info unavailable]'.format( 'Edition' if editions else 'Part') uprint(f'{prefix}- {disco_entry}:{suffix}') if names := disco_entry.names: uprint(f'{prefix} Names:') for name in names: # uprint(f'{prefix} - {name}') uprint(f'{prefix} - {name.full_repr(include_versions=False)}')
def test_match(paths: Paths, identifier: str): for album_dir in iter_album_dirs(paths): album_name = album_dir.name if not album_name: raise ValueError( f'Directories with multiple album names are not currently handled.' ) if URL_MATCH(identifier): disco_entry = DiscographyEntry.from_url(identifier) else: disco_entry = DiscographyEntry.from_title(identifier, search=True, research=True) uprint(f'Match scores for {album_name!r}:') de_score = album_name.name._score(disco_entry.name) uprint(f' - {disco_entry}: {de_score}') for edition in disco_entry: ed_score = album_name.name._score(edition.name) uprint(f' - {edition}: {ed_score}') for part in edition: p_score = album_name.name._score(part.name) uprint(f' - {part}: {p_score}')
def print_tag_changes(obj, changes: Mapping[str, Tuple[Any, Any]], dry_run: bool, color=None): name_width = max(len(tag_name) for tag_name in changes) if changes else 0 orig_width = max( max(len(r), mono_width(r)) for r in (repr(orig) for orig, _ in changes.values())) if changes else 0 _fmt = ' - {{:<{}s}}{}{{:>{}s}}{}{{}}' if changes: uprint( colored( '{} {} by changing...'.format( '[DRY RUN] Would update' if dry_run else 'Updating', obj), color)) for tag_name, (orig_val, new_val) in changes.items(): if tag_name == 'title': bg, reset, w = 20, False, 20 else: bg, reset, w = None, True, 14 orig_repr = repr(orig_val) fmt = _fmt.format( name_width + w, colored(' from ', 15, bg, reset=reset), orig_width - (mono_width(orig_repr) - len(orig_repr)) + w, colored(' to ', 15, bg, reset=reset), ) uprint( colored( fmt.format( colored(tag_name, 14, bg, reset=reset), colored(orig_repr, 11, bg, reset=reset), colored(repr(new_val), 10, bg, reset=reset), ), bg_color=bg, )) else: prefix = '[DRY RUN] ' if dry_run else '' uprint(colored(f'{prefix}No changes necessary for {obj}', color))
def print_discography( entity: DiscographyMixin, indent=0, expand_disco=False, editions=False, track_info=False, alb_types: AlbTypes = None, header=False, ): prefix = ' ' * indent if header: # noinspection PyUnresolvedReferences uprint(f'{prefix}- {entity.name}:') if discography := entity.discography: uprint(f'{prefix} Discography:') for disco_entry in sorted(discography): if not alb_types or disco_entry.type in alb_types: if expand_disco: print_disco_entry(disco_entry, indent + 6, editions, track_info=track_info) else: uprint(f'{prefix} - {disco_entry}')
def print_tv_series(tv_series: TVSeries, indent=0): prefix = ' ' * indent if links := tv_series.soundtrack_links(): uprint(f'{prefix}Discography Links:') for link in sorted(links): uprint(f'{prefix} - {link!r}')
def print_de_part(part: DiscographyEntryPart, indent=0, track_info=False): prefix = ' ' * indent if part: uprint(f'{prefix}- {part}:') uprint(f'{prefix} Tracks:') for track in part: uprint(f'{prefix} - {track._repr()}') if track_info: uprint(f'{prefix} Full name: {track.full_name()!r}') uprint(f'{prefix} Name: {track.name.full_repr()}') else: uprint(f'{prefix}- {part}: [Track info unavailable]')
elif isinstance(entity, TVSeries): print_tv_series(entity, 2) else: uprint( f' - No additional information is configured for {entity.__class__.__name__} entities' ) def print_tv_series(tv_series: TVSeries, indent=0): prefix = ' ' * indent if links := tv_series.soundtrack_links(): uprint(f'{prefix}Discography Links:') for link in sorted(links): uprint(f'{prefix} - {link!r}') else: uprint(f'{prefix}Discography Links: [Unavailable]') def print_artist(artist: Artist, indent=0, expand_disco=0, editions=False, track_info=False, alb_types: AlbTypes = None): prefix = ' ' * indent uprint(f'{prefix}- {artist.name}:') if names := artist.names: uprint(f'{prefix} Names:') for name in names: # uprint(f'{prefix} - {name}') uprint(f'{prefix} - {name.full_repr(include_versions=False)}')