diff --git a/README.md b/README.md index 0cf2405..81d06d6 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,37 @@ A rudimentary audio tagger based on Discogs metadata. # Installation - Install [`poetry`](https://python-poetry.org/docs/#installation) - `poetry install && poetry build && pip install .` -- `discogs-tag [--dir /path/to/audio/files] [--dry]` + +# Usage +```shell +NAME + discogs-tag - Tag the audio files with the given Discogs release. + +SYNOPSIS + discogs-tag RELEASE + +DESCRIPTION + Tag the audio files with the given Discogs release. + +POSITIONAL ARGUMENTS + RELEASE + +FLAGS + --dir=DIR + Default: './' + --dry=DRY + Default: False + -i, --ignore=IGNORE + Default: False + --skip_artist=SKIP_ARTIST + Default: False + --skip_title=SKIP_TITLE + Default: False + --skip_composer=SKIP_COMPOSER + Default: False + --skip_position=SKIP_POSITION + Default: False + +NOTES + You can also use flags syntax for POSITIONAL ARGUMENTS +``` diff --git a/src/discogs_tag/cli.py b/src/discogs_tag/cli.py index a70c6a4..619c089 100644 --- a/src/discogs_tag/cli.py +++ b/src/discogs_tag/cli.py @@ -9,8 +9,18 @@ import re from discogs_tag import __NAME__, __VERSION__ -def tag(release, dir = './', dry = False, ignore = False): - """Tag the audio files in dir with the given Discogs release.""" +def tag( + release, + dir='./', + dry=False, + ignore=False, + skip_artist=False, + skip_title=False, + skip_composer=False, + skip_position=False +): + """Tag the audio files with the given Discogs release.""" + options = locals() request = urllib.request.Request(f'https://api.discogs.com/releases/{release}', headers = { 'User-Agent': f'{__NAME__} {__VERSION__}' }) @@ -20,49 +30,56 @@ def tag(release, dir = './', dry = False, ignore = False): glob.glob(os.path.join(glob.escape(dir), '**', '*.flac'), recursive=True) + glob.glob(os.path.join(glob.escape(dir), '**', '*.mp3'), recursive=True) ) - apply_metadata(data, files, dry, ignore) + apply_metadata(data, files, options) -def apply_metadata(data, files, dry, ignore): +def apply_metadata(data, files, options): tracks = list(filter(lambda t: t['type_'] == 'track', data['tracklist'])) - if (len(files) != len(tracks)): - if (not ignore): - raise Exception(f'Expecting {len(tracks)} files but found {len(files)}. Aborting.') - else: + if len(files) != len(tracks): + if options['ignore']: print(f'Expecting {len(tracks)} files but found {len(files)}. Ignoring.', file=sys.stderr) + else: + raise Exception(f'Expecting {len(tracks)} files but found {len(files)}. Aborting.') for n, track in enumerate(tracks): try: audio = mutagen.File(files[n], easy=True) - merge_metadata(track, audio) - if (dry): + merge_metadata(track, audio, options) + if options['dry']: pprint(audio) else: audio.save() except Exception as e: - if (not ignore): - raise e - else: + if options['ignore']: print(e, file=sys.stderr) + else: + raise e - if (not dry): + if not options['dry']: print(f'Processed {len(files)} audio files.') -def merge_metadata(track, audio): - audio['title'] = track['title'] - artists = [] - if 'artists' in track: - artists += [artist_name(artist) for artist in track['artists']] - if 'extraartists' in track: - artists += [artist_name(artist) for artist in filter(lambda a: a['role'].casefold() != 'Written-By'.casefold(), track['extraartists'])] - if (artists): - audio['artist'] = ', '.join(artists) - positions = track['position'].split('-') - audio['tracknumber'] = positions[-1] - if (len(positions) > 1): - audio['discnumber'] = positions[0] - composers = [artist_name(composer) for composer in filter(lambda a: a['role'].casefold() == 'Written-By'.casefold(), track['extraartists'])] if 'extraartists' in track else None - if (composers): - audio['composer'] = ', '.join(composers) +def merge_metadata(track, audio, options): + if not options['skip_title']: + audio['title'] = track['title'] + + if not options['skip_artist']: + artists = [] + if 'artists' in track: + artists += [artist_name(artist) for artist in track['artists']] + if 'extraartists' in track: + artists += [artist_name(artist) for artist in filter(lambda a: a['role'].casefold() != 'Written-By'.casefold(), track['extraartists'])] + if artists: + audio['artist'] = ', '.join(artists) + + if not options['skip_composer']: + composers = [artist_name(composer) for composer in filter(lambda a: a['role'].casefold() == 'Written-By'.casefold(), track['extraartists'])] if 'extraartists' in track else None + if composers: + audio['composer'] = ', '.join(composers) + + if not options['skip_position']: + positions = track['position'].split('-') + audio['tracknumber'] = positions[-1] + if len(positions) > 1: + audio['discnumber'] = positions[0] def artist_name(artist): name = None diff --git a/tests/test_discogs_tag.py b/tests/test_discogs_tag.py index 9bb83ae..626c2ce 100644 --- a/tests/test_discogs_tag.py +++ b/tests/test_discogs_tag.py @@ -22,7 +22,12 @@ def test_merge_metadata(): 'role': 'Written-By', 'name': 'Composer' }] - }, audio) + }, audio, { + 'skip_artist': False, + 'skip_composer': False, + 'skip_title': False, + 'skip_position': False, + }) assert audio['title'] == 'Title' assert audio['artist'] == 'Artist 1, Artist 2, Artist 3, Guitarist' assert audio['discnumber'] == '1' @@ -34,6 +39,6 @@ def test_apply_metadata(): data = json.load(release) # Test that files must match API results. - with pytest.raises(Exception) as e1: - apply_metadata(data, [], False, False) - assert "Expecting 28 files" in str(e1.value) + with pytest.raises(Exception) as error: + apply_metadata(data, [], { 'dry': False, 'ignore': False }) + assert "Expecting 28 files" in str(error.value)