#!/usr/bin/env python3 # -*- coding: utf-8 -*- import datetime as dt import mutagen import mutagen.id3 import mutagen.mp4 import os import re import symuid.library.itunes import sys def generate_play_count_tag_label(player, library_id, reg_dt): return 'symuid:pcnt:{}:{}:{}'.format(player, library_id, int(reg_dt.timestamp())) def set_play_count_tag(track_path, player, library_id, reg_dt, play_count): assert isinstance(reg_dt, dt.datetime), reg_dt tag_label = generate_play_count_tag_label( player=player, library_id=library_id, reg_dt=reg_dt, ) track = mutagen.File(filename=track_path) if isinstance(track.tags, mutagen.id3.ID3): tag_label_id3 = 'TXXX:' + tag_label if not tag_label_id3 in track.tags: # mutagen.id3._specs.EncodedTextSpec.write encodes # 'desc' and 'text' tag = mutagen.id3.TXXX( encoding=mutagen.id3.Encoding.LATIN1, desc=tag_label, text=[str(play_count)], ) track.tags.add(tag) track.save() print('{!r}: set ID3 tag {!r}'.format(track_path, tag)) elif isinstance(track.tags, mutagen.mp4.MP4Tags): tag_label_mp4 = '----:' + tag_label if not tag_label_mp4 in track.tags: track.tags[tag_label_mp4] = tag = mutagen.mp4.MP4FreeForm( # "a signed big-endian integer with length one of { 1,2,3,4,8 } bytes" # TODO set byte length properly # setting signed=True just to be explicit here # (irrelevant for positive integers) data=play_count.to_bytes(1, byteorder='big', signed=True), dataformat=mutagen.mp4.AtomDataType.INTEGER, ) track.save() print('{!r}: set MP4 tag {!r}'.format(track_path, tag)) else: raise Exception(track_path) def symuid_import_itunes(xml_library_path, path_regex_sub): lib = symuid.library.itunes.XmlLibrary(xml_library_path) for track in lib.tracks: # TODO create tag if last_play_dt is None if track.last_play_dt and track.local: track_path = track.local_path for pattern, repl in path_regex_sub: track_path = re.sub(pattern, repl, track_path) if not os.path.exists(track_path): sys.stderr.write('{!r}: not found\n'.format(track_path)) else: # TODO dt=dt.datetime.now() set_play_count_tag( track_path=track_path, player='itunes', library_id=lib.id, reg_dt=track.last_play_dt, play_count=track.play_count, ) def _init_argparser(): import argparse argparser = argparse.ArgumentParser(description=None) argparser.add_argument('xml_library_path') argparser.add_argument( '--path-regex-sub', nargs=2, action='append', metavar=('regex', 'replacement'), default=[], help='(default: %(default)r)', ) return argparser def main(argv): argparser = _init_argparser() args = argparser.parse_args(argv[1:]) symuid_import_itunes(**vars(args)) return 0 if __name__ == "__main__": sys.exit(main(sys.argv))