symuid 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. import mutagen
  4. import mutagen.id3
  5. import mutagen.mp4
  6. import os
  7. import subprocess
  8. # http://id3.org/id3v2.4.0-frames#4.1.
  9. TRACK_UUID_ID3_OWNER_ID = 'symuid'
  10. # TODO http://mutagen.readthedocs.io/en/latest/api/mp4.html
  11. def generate_uuid():
  12. return subprocess.check_output(['uuid', '-v', '4', '-F', 'BIN']).strip()
  13. def get_uuid(id3_tags):
  14. assert isinstance(id3_tags, mutagen.id3.ID3), type(id3_tags)
  15. ufids = id3_tags.getall('UFID')
  16. for ufid in ufids:
  17. if ufid.owner == TRACK_UUID_ID3_OWNER_ID:
  18. return ufid.data
  19. return None
  20. def get_or_assign_uuid(id3_tags):
  21. uuid = get_uuid(id3_tags)
  22. if uuid is None:
  23. # mutagen.id3._specs.EncodedTextSpec.write encodes 'owner'
  24. id3_tags.add(mutagen.id3.UFID(
  25. owner=TRACK_UUID_ID3_OWNER_ID,
  26. data=generate_uuid(),
  27. ))
  28. id3_tags.save()
  29. id3_tags.load(filename=id3_tags.filename)
  30. uuid = get_uuid(id3_tags)
  31. print("{!r}: assigned uuid {!r}".format(id3_tags.filename, uuid))
  32. assert uuid is not None
  33. return uuid
  34. def symuid(path):
  35. if os.path.isdir(path):
  36. for dirpath, dirnames, filenames in os.walk(path):
  37. for filename in filenames:
  38. symuid(os.path.join(dirpath, filename))
  39. else:
  40. try:
  41. f = mutagen.File(filename=path)
  42. except Exception:
  43. raise Exception(path)
  44. if not f:
  45. print("{!r}: unsupported filetype, ignored".format(path))
  46. elif isinstance(f.tags, mutagen.mp4.MP4Tags):
  47. print("{!r}: mp4 tags not supported, ignored".format(path))
  48. else:
  49. get_or_assign_uuid(f.tags)
  50. def _init_argparser():
  51. import argparse
  52. argparser = argparse.ArgumentParser(description=None)
  53. argparser.add_argument('path')
  54. return argparser
  55. def main(argv):
  56. argparser = _init_argparser()
  57. args = argparser.parse_args(argv[1:])
  58. symuid(**vars(args))
  59. return 0
  60. if __name__ == "__main__":
  61. import sys
  62. sys.exit(main(sys.argv))