Passed
Push — dev ( ed36f9...ede60a )
by Konstantinos
05:48 queued 58s
created

MetadataDealer.set_album_metadata()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 8
dl 0
loc 3
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1 1
import glob
2 1
import logging
3 1
import os
4 1
import re
5 1
from collections import defaultdict
6
7 1
import click
8 1
from music_album_creation.tracks_parsing import StringParser
9 1
from mutagen.id3 import ID3, TALB, TDRC, TIT2, TPE1, TPE2, TRCK
10
11
# The main notable classes in mutagen are FileType, StreamInfo, Tags, Metadata and for error handling the MutagenError exception.
12
13
14 1
logger = logging.getLogger(__name__)
15
16
17 1
class MetadataDealerType(object):
18
19 1
    _filters = defaultdict(lambda: lambda y: y, track_number=lambda y: MetadataDealerType._parse_year(y))
20
21 1
    @classmethod
22
    def _parse_year(cls, year):
23 1
        if year == '':
24
            return ''
25 1
        c = re.match(r'0*(\d+)', year)
26 1
        if not c:
27
            raise InvalidInputYearError("Input year tag '{}' is invalid".format(year))
28 1
        return c.group(1)
29
30
31 1
class MetadataDealer(MetadataDealerType):
32
33
    #############
34
    # simply add keys and constructor pairs to enrich the support of the API for writting tags/frames to audio files
35
    # you can use the cls._filters to add a new post processing filter as shown in MetadataDealerType constructor above
36 1
    _d = {'artist': TPE1,  # 4.2.1   TPE1    [#TPE1 Lead performer(s)/Soloist(s)]  ; taken from http://id3.org/id3v2.3.0
37
          #  in clementine temrs, it affects the 'Artist' tab but not the 'Album artist'
38
          'album_artist': TPE2,  # 4.2.1   TPE2    [#TPE2 Band/orchestra/accompaniment]
39
          # in clementine terms, it affects the 'Artist' tab but not the 'Album artist'
40
          'album': TALB,  # 4.2.1   TALB    [#TALB Album/Movie/Show title]
41
          'year': TDRC  # TDRC (recording time) consolidates TDAT (date), TIME (time), TRDA (recording dates), and TYER (year).
42
          }
43
44
    # supported metadata to try and infer automatically
45 1
    _auto_data = [('track_number', TRCK),  # 4.2.1   TRCK    [#TRCK Track number/Position in set]
46
                  ('track_name', TIT2)]   # 4.2.1   TIT2    [#TIT2 Title/songname/content description]
47
48 1
    _all = dict(_d, **dict(_auto_data))
49
50 1
    @classmethod
51 1
    def set_album_metadata(cls, album_directory, track_number=True, track_name=True, artist='', album_artist='', album='', year=''):
52 1
        cls._write_metadata(album_directory, track_number=track_number, track_name=track_name, artist=artist, album_artist=album_artist, album=album, year=str(year))
53
54 1
    @classmethod
55
    def _write_metadata(cls, album_directory, **kwargs):
56 1
        files = glob.glob('{}/*.mp3'.format(album_directory))
57 1
        logger.info("Album directory: {}".format(album_directory))
58 1
        for file in files:
59 1
            logger.info("File: {}".format(os.path.basename(file)))
60 1
            cls.write_metadata(file, **dict(cls._filter_auto_inferred(StringParser.parse_track_number_n_name(file), **kwargs),
61
                                            **{k: kwargs.get(k, '') for k in cls._d.keys()}))
62
63 1
    @classmethod
64
    def write_metadata(cls, file, **kwargs):
65 1
        if not all(map(lambda x: x[0] in cls._all.keys(), kwargs.items())):
66
            raise RuntimeError("Some of the input keys [{}] used to request the addition of metadata, do not correspond"
67
                               " to a tag/frame of the supported [{}]".format(', '.join(kwargs.keys()), ' '.join(cls._d)))
68 1
        audio = ID3(file)
69 1
        for metadata_name, v in kwargs.items():
70 1
            if bool(v):
71 1
                audio.add(cls._all[metadata_name](encoding=3, text=u'{}'.format(cls._filters[metadata_name](v))))
72 1
                logger.info(" {}: {}={}".format(metadata_name, cls._all[metadata_name].__name__, cls._filters[metadata_name](v)))
73
            else:
74 1
                logger.warning("Skipping metadata '{}::'{}' because bool({}) == False".format(metadata_name, cls._all[metadata_name].__name__, v))
75 1
        audio.save()
76
77 1
    @classmethod
78
    def _filter_auto_inferred(cls, d, **kwargs):
79
        """Given a dictionary (like the one outputted by _infer_track_number_n_name), deletes entries unless it finds them declared in kwargs as key_name=True"""
80 1
        for k in cls._auto_data:
81 1
            if not kwargs.get(k, False) and k in d:
82
                del d[k]
83 1
        return d
84
85
86
class InvalidInputYearError(Exception): pass
87
88
89 1
@click.command()
90 1
@click.option('--album-dir', required=True, help="The directory where a music album resides. Currently only mp3 "
91
                                                 "files are supported as contents of the directory. Namely only "
92
                                                 "such files will be apprehended as tracks of the album.")
93 1
@click.option('--track_name/--no-track_name', default=True, show_default=True, help='Whether to extract the track names from the mp3 files and write them as metadata correspondingly.')
94 1
@click.option('--track_number/--no-track_number', default=True, show_default=True, help='Whether to extract the track numbers from the mp3 files and write them as metadata correspondingly.')
95 1
@click.option('--artist', '-a', help="If given, then value shall be used as the TPE1 tag: 'Lead performer(s)/Soloist(s)'.  In the music player 'clementine' it corresponds to the 'Artist' column.")
96 1
@click.option('--album_artist', '-aa', help="If given, then value shall be used as the TPE2 tag: 'Band/orchestra/accompaniment'.  In the music player 'clementine' it corresponds to the 'Album artist' column.")
97 1
@click.option('--album', '-al', help="If given, then value shall be used as the TALB tag: 'Album/Movie/Show title'.  In the music player 'clementine' it corresponds to the 'Album' column.")
98 1
@click.option('--year', 'y', help="If given, then value shall be used as the TDRC tag: 'Recoring time'.  In the music player 'clementine' it corresponds to the 'Year' column.")
99
def main(album_dir, track_name, track_number, artist, album_artist, album, year):
100
    md = MetadataDealer()
101
    md.set_album_metadata(album_dir, track_number=track_number, track_name=track_name, artist=artist, album_artist=album_artist, album=album, year=year)
102
103
104 1
if __name__ == '__main__':
105
    main()
106