Skip to content

Commit

Permalink
Add copy command and sub_track option
Browse files Browse the repository at this point in the history
  • Loading branch information
infojunkie committed May 24, 2024
1 parent f42857a commit a52e974
Show file tree
Hide file tree
Showing 8 changed files with 1,391 additions and 45 deletions.
44 changes: 42 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ A rudimentary audio tagger based on Discogs metadata.
# Usage
```shell
NAME
discogs-tag - Tag the audio files with the given Discogs release.
discogs-tag tag - Tag the audio files with the given Discogs release.

SYNOPSIS
discogs-tag RELEASE <flags>
discogs-tag tag RELEASE <flags>

DESCRIPTION
Tag the audio files with the given Discogs release.
Expand All @@ -36,6 +36,46 @@ FLAGS
Default: False
--skip_position=SKIP_POSITION
Default: False
--skip_year=SKIP_YEAR
Default: False
--skip_subtrack=SKIP_SUBTRACK
Default: False

NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
```
```shell
NAME
discogs-tag copy - Copy the audio tags from source to destination folders.

SYNOPSIS
discogs-tag copy SRC <flags>

DESCRIPTION
Copy the audio tags from source to destination folders.

POSITIONAL ARGUMENTS
SRC

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
--skip_year=SKIP_YEAR
Default: False
--skip_subtrack=SKIP_SUBTRACK
Default: False

NOTES
You can also use flags syntax for POSITIONAL ARGUMENTS
Expand Down
58 changes: 29 additions & 29 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "discogs-tag"
version = "0.1.10"
version = "0.2.0"
description = "A rudimentary audio tagger based on Discogs metadata."
authors = ["infojunkie <[email protected]>"]
readme = "README.md"
Expand Down
89 changes: 78 additions & 11 deletions src/discogs_tag/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import glob
import sys
import re
from functools import reduce
from discogs_tag import __NAME__, __VERSION__

def tag(
Expand All @@ -18,7 +19,8 @@ def tag(
skip_title=False,
skip_composer=False,
skip_position=False,
skip_year=False
skip_year=False,
skip_subtrack=False
):
"""Tag the audio files with the given Discogs release."""
options = locals()
Expand All @@ -33,8 +35,70 @@ def tag(
)
apply_metadata(data, files, options)

def copy(
src,
dir='./',
dry=False,
ignore=False,
skip_artist=False,
skip_title=False,
skip_composer=False,
skip_position=False,
skip_year=False,
skip_subtrack=False
):
"""Copy the audio tags from source to destination folders."""
options = locals()
src_files = sorted(
glob.glob(os.path.join(glob.escape(src), '**', '*.flac'), recursive=True) +
glob.glob(os.path.join(glob.escape(src), '**', '*.mp3'), recursive=True)
)
dst_files = sorted(
glob.glob(os.path.join(glob.escape(dir), '**', '*.flac'), recursive=True) +
glob.glob(os.path.join(glob.escape(dir), '**', '*.mp3'), recursive=True)
)
data = read_metadata(src_files, options)
if options['dry']:
pprint(data)
else:
apply_metadata(data, dst_files, options)

def read_metadata(files, options):
"""Read metadata from OS audio files and return data structure that mimics Discogs release."""
def safe_position(audio, n):
try:
return audio['tracknumber'][0].split('/')[0].lstrip('0')
except:
return str(n)

tracklist = []
for n, file in enumerate(files):
audio = mutagen.File(file, easy=True)
tracklist.append({
'type_': 'track',
'position': safe_position(audio, n+1),
'artists': [{
'anv': audio['artist'][0]
}],
'title': audio['title'][0],
'year': audio['date'][0],
})
return {
'tracklist': sorted(tracklist, key=lambda track: int(track['position']))
}

def apply_metadata(release, files, options):
tracks = list(filter(lambda t: t['type_'] == 'track', release['tracklist']))
"""Apply Discogs release metadada to OS audio files."""
def get_tracks(tracklist):
def reduce_track(tracks, track):
if track['type_'] == 'track':
tracks.append(track)
if not options['skip_subtrack'] and 'sub_tracks' in track:
tracks = tracks + get_tracks(track['sub_tracks'])
return tracks
return reduce(reduce_track, tracklist, [])

tracks = get_tracks(release['tracklist'])
if len(files) != len(tracks):
if options['ignore']:
print(f'Expecting {len(tracks)} files but found {len(files)}. Ignoring.', file=sys.stderr)
Expand All @@ -59,6 +123,14 @@ def apply_metadata(release, files, options):
print(f'Processed {len(files)} audio files.')

def merge_metadata(release, track, audio, options):
def artist_name(artist):
name = None
if 'anv' in artist and artist['anv']:
name = artist['anv']
elif 'name' in artist and artist['name']:
name = artist['name']
return re.sub(r"\s+\(\d+\)$", '', name) if name else None

if not options['skip_title']:
audio['title'] = track['title']

Expand Down Expand Up @@ -86,13 +158,8 @@ def merge_metadata(release, track, audio, options):
if 'year' in release and release['year']:
audio['date'] = str(release['year'])

def artist_name(artist):
name = None
if 'anv' in artist and artist['anv']:
name = artist['anv']
elif 'name' in artist and artist['name']:
name = artist['name']
return re.sub(r"\s+\(\d+\)$", '', name) if name else None

def cli():
fire.Fire(tag)
fire.Fire({
'tag': tag,
'copy': copy,
})
Loading

0 comments on commit a52e974

Please sign in to comment.