123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- import mutagen
- import mutagen.id3
- import mutagen.mp4
- import os
- import subprocess
- # http://id3.org/id3v2.4.0-frames#4.1.
- TRACK_UUID_ID3_OWNER_ID = 'symuid'
- # freeform keys start with '----'
- # http://mutagen.readthedocs.io/en/latest/api/mp4.html
- TRACK_UUID_MP4_TAG = '----:symuid:uuid'
- def generate_uuid():
- return subprocess.check_output(['uuid', '-v', '4', '-F', 'BIN']).strip()
- def get_uuid_id3(id3_tags):
- assert isinstance(id3_tags, mutagen.id3.ID3), type(id3_tags)
- ufids = id3_tags.getall('UFID')
- for ufid in ufids:
- if ufid.owner == TRACK_UUID_ID3_OWNER_ID:
- return ufid.data
- return None
- def get_or_assign_uuid_id3(id3_tags):
- uuid = get_uuid_id3(id3_tags)
- if uuid is None:
- # mutagen.id3._specs.EncodedTextSpec.write encodes 'owner'
- id3_tags.add(mutagen.id3.UFID(
- owner=TRACK_UUID_ID3_OWNER_ID,
- data=generate_uuid(),
- ))
- id3_tags.save()
- id3_tags.load(filename=id3_tags.filename)
- uuid = get_uuid_id3(id3_tags)
- 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 symuid_sync(path):
- if os.path.isdir(path):
- for dirpath, dirnames, filenames in os.walk(path):
- for filename in filenames:
- symuid_sync(os.path.join(dirpath, filename))
- else:
- try:
- f = mutagen.File(filename=path)
- except Exception:
- raise Exception(path)
- if not f:
- print("{!r}: unsupported filetype, ignored".format(path))
- elif isinstance(f, mutagen.mp4.MP4):
- get_or_assign_uuid_mp4(f)
- elif isinstance(f.tags, mutagen.id3.ID3):
- get_or_assign_uuid_id3(f.tags)
- else:
- raise Exception(f)
- def _init_argparser():
- import argparse
- argparser = argparse.ArgumentParser(description=None)
- argparser.add_argument('path')
- return argparser
- def main(argv):
- argparser = _init_argparser()
- args = argparser.parse_args(argv[1:])
- symuid_sync(**vars(args))
- return 0
- if __name__ == "__main__":
- import sys
- sys.exit(main(sys.argv))
|