123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- import mutagen
- import mutagen.id3
- import mutagen.mp4
- import os
- import re
- import subprocess
- import sys
- import symuid
- # freeform keys start with '----'
- # http://mutagen.readthedocs.io/en/latest/api/mp4.html
- TRACK_UUID_MP4_TAG = '----:symuid:uuid'
- PATH_DEFAULT_IGNORE_REGEX = r'\.(itdb|itc2|itl|jpg|midi?|plist|xml|zip)$'
- def generate_uuid():
- return subprocess.check_output(['uuid', '-v', '4', '-F', 'BIN']).strip()
- def get_or_assign_uuid_id3(track):
- uuid = track._iface.get_uuid()
- if uuid is None:
- id3_tags = track._iface._mutagen_file.tags
- # mutagen.id3._specs.EncodedTextSpec.write encodes 'owner'
- id3_tags.add(mutagen.id3.UFID(
- owner=track._iface._UFID_OWNER_ID,
- data=generate_uuid(),
- ))
- id3_tags.save()
- id3_tags.load(filename=id3_tags.filename)
- uuid = track._iface.get_uuid()
- print("{!r}: assigned uuid {!r}".format(id3_tags.filename, uuid))
- assert uuid is not None
- return uuid
- def get_or_assign_uuid_mp4(mp4_file):
- if not TRACK_UUID_MP4_TAG in mp4_file:
- mp4_file[TRACK_UUID_MP4_TAG] = mutagen.mp4.MP4FreeForm(
- data=generate_uuid(),
- # https://mutagen.readthedocs.io/en/latest/api/mp4.html#mutagen.mp4.AtomDataType.UUID
- dataformat=mutagen.mp4.AtomDataType.UUID,
- )
- mp4_file.save()
- mp4_file.load(filename=mp4_file.filename)
- print("{!r}: assigned uuid {!r}".format(
- mp4_file.filename, mp4_file[TRACK_UUID_MP4_TAG][0],
- ))
- return mp4_file[TRACK_UUID_MP4_TAG][0]
- def log_path(track_path, msg, stream=sys.stdout):
- stream.write("{!r}: {}\n".format(track_path, msg))
- def log_path_error(track_path, msg):
- log_path(track_path, msg, stream=sys.stderr)
- def symuid_sync(path, path_ignore_regex, show_ignored=False):
- for track in symuid.Track.walk(
- root_path=path,
- path_ignore_regex=path_ignore_regex,
- ignored_cb=lambda p: show_ignored and log_path(p, 'ignored'),
- unsupported_cb=lambda p, e: log_path_error(p, 'unsupported type, skipped'),
- ):
- if isinstance(track._iface, symuid.tag_interface.MP4):
- get_or_assign_uuid_mp4(track._iface._mutagen_file)
- elif isinstance(track._iface, symuid.tag_interface.ID3):
- get_or_assign_uuid_id3(track)
- else:
- raise Exception(track)
- def _init_argparser():
- import argparse
- argparser = argparse.ArgumentParser(description=None)
- argparser.add_argument('path')
- argparser.add_argument(
- '--path-ignore-regex',
- default=PATH_DEFAULT_IGNORE_REGEX,
- nargs=1,
- metavar='pattern',
- dest='path_ignore_regex',
- type=lambda pattern: re.compile(pattern),
- help='(default: %(default)s)',
- )
- argparser.add_argument(
- '--show-ignored',
- action='store_true',
- )
- return argparser
- def main(argv):
- argparser = _init_argparser()
- args = argparser.parse_args(argv[1:])
- symuid_sync(**vars(args))
- return 0
- if __name__ == "__main__":
- sys.exit(main(sys.argv))
|