sync.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import pickle, os, shutil, pathlib, music_tag
  2. from protolib import remove_empty_folders, correct_path
  3. from data import database, config
  4. class TemplateData(dict):
  5. def __getitem__(self, key):
  6. if key in self:
  7. return super().__getitem__(key)
  8. else:
  9. return key
  10. def get_sync_db_template():
  11. return {
  12. 'type': 'sync',
  13. 'music': {
  14. 'tracks': {},
  15. 'blacklist': [],
  16. 'overwrite': {
  17. 'metadata': True,
  18. 'path': True
  19. }
  20. },
  21. 'templates': {
  22. 'path': '%(album)s by %(albumArtist)s/%(title)s by %(artists)s.%(ext)s',
  23. 'artist': {
  24. 'seperator': {
  25. 'filename': ', ',
  26. 'metadata': ';'
  27. }
  28. }
  29. }
  30. }
  31. def sync(path, callback=None, force_meta_write=False, write_changed_meta=False, update_path=True):
  32. print('Preparing to sync music library')
  33. os.makedirs(path, exist_ok=True)
  34. if os.path.exists(os.path.join(path, 'sync.pkl')):
  35. print('Loading sync database')
  36. with open(os.path.join(path, 'sync.pkl'), 'rb') as f:
  37. db = pickle.load(f)
  38. else:
  39. db = get_sync_db_template()
  40. print('Syncing music library')
  41. added_files = 0
  42. updated_meta = 0
  43. moved_files = 0
  44. removed_files = 0
  45. running = True
  46. for uuid in database['music']:
  47. print('Preparing to proccess %s' % uuid)
  48. if callable(callback):
  49. running = callback(uuid)
  50. track = database['music'][uuid]
  51. if uuid in db['music']['blacklist']:
  52. if uuid in db['music']['tracks']:
  53. if os.path.exists(os.path.join(path, db['music']['tracks'][uuid]['path'])):
  54. print('Removing %s' % uuid)
  55. os.remove(os.path.join(path, db['music']['tracks'][uuid]['path']))
  56. del db['music']['tracks'][uuid]
  57. removed_files += 1
  58. continue
  59. if uuid not in db['music']['tracks']:
  60. db['music']['tracks'][uuid] = {
  61. 'path': track['path']
  62. }
  63. if not os.path.exists(os.path.join(path, db['music']['tracks'][uuid]['path'])):
  64. print('Adding file')
  65. os.makedirs(os.path.join(path, pathlib.Path(track['path']).parent), exist_ok=True)
  66. shutil.copyfile(
  67. os.path.join(
  68. config['files']['audio']['path'],
  69. track['path']),
  70. os.path.join(path, track['path']))
  71. added_files += 1
  72. if database['music'][uuid]['artists']:
  73. file_artists_strs = [database['artists'][artist]['name'] + db['templates']['artist']['seperator']['filename'] for artist in database['music'][uuid]['artists']]
  74. file_artists_str = ''.join(file_artists_strs)
  75. file_artists_str = file_artists_str.strip(db['templates']['artist']['seperator']['filename'])
  76. meta_artists_strs = [database['artists'][artist]['name'] + db['templates']['artist']['seperator']['metadata'] for artist in database['music'][uuid]['artists']]
  77. meta_artists_str = ''.join(meta_artists_strs)
  78. meta_artists_str = meta_artists_str.strip(db['templates']['artist']['seperator']['metadata'])
  79. else:
  80. file_artists_str = 'Unknown'
  81. meta_artists_str = None
  82. if db['music']['overwrite']['metadata'] and (force_meta_write or write_changed_meta):
  83. print('Updating the metadata')
  84. file = music_tag.load_file(os.path.join(path, db['music']['tracks'][uuid]['path']))
  85. overwrote_meta = False
  86. if database['music'][uuid]['title']:
  87. if force_meta_write or (write_changed_meta and file['title'] != database['music'][uuid]['title']):
  88. file['title'] = database['music'][uuid]['title']
  89. overwrote_meta = True
  90. if database['music'][uuid]['artists']:
  91. if force_meta_write or (write_changed_meta and file['artist'] != meta_artists_str):
  92. file['artist'] = meta_artists_str
  93. overwrote_meta = True
  94. if database['music'][uuid]['album']:
  95. if force_meta_write or (write_changed_meta and file['album'] != database['albums'][database['music'][uuid]['album']]['name']):
  96. file['album'] = database['albums'][database['music'][uuid]['album']]['name']
  97. overwrote_meta = True
  98. if force_meta_write or (write_changed_meta and file['albumartist'] != database['artists'][database['albums'][database['music'][uuid]['album']]['artist']]['name']):
  99. if database['albums'][database['music'][uuid]['album']]['artist']:
  100. file['albumartist'] = database['artists'][database['albums'][database['music'][uuid]['album']]['artist']]['name']
  101. if database['music'][uuid]['genre']:
  102. if force_meta_write or (write_changed_meta and file['genre'] != database['genres'][database['music'][uuid]['genre']]['name']):
  103. file['genre'] = database['genres'][database['music'][uuid]['genre']]['name']
  104. overwrote_meta = True
  105. if file['artwork']:
  106. if force_meta_write or not file['artwork'].value:
  107. cover = (database['music'][uuid]['album'] and database['albums'][database['music'][uuid]['album']]['cover']) or database['music'][uuid]['cover']
  108. if cover:
  109. cover_path = os.path.join(config['files']['images']['path'] if config['files']['images']['path'] else config['files']['audio']['path'], database['covers'][cover]['path'])
  110. with open(cover_path, 'rb') as f:
  111. file['artwork'] = f.read()
  112. overwrote_meta = True
  113. if overwrote_meta:
  114. print('Writing changes to metadata...')
  115. file.save()
  116. updated_meta += 1
  117. if db['music']['overwrite']['path'] and update_path:
  118. print('Preparing to move the file')
  119. data = TemplateData()
  120. data['title'] = database['music'][uuid]['title'] if database['music'][uuid]['title'] else 'Unknown'
  121. data['artists'] = file_artists_str
  122. data['album'] = database['albums'][database['music'][uuid]['album']]['name'] if database['music'][uuid]['album'] else 'Unknown'
  123. data['albumArtist'] = database['artists'][database['albums'][database['music'][uuid]['album']]['artist']]['name'] if database['music'][uuid]['album'] and database['albums'][database['music'][uuid]['album']]['artist'] else 'Unknown'
  124. data['ext'] = db['music']['tracks'][uuid]['path'].split('.')[-1]
  125. new_path: str = db['templates']['path'] % data
  126. new_path = correct_path(new_path)
  127. new_path = os.path.relpath(new_path, os.getcwd())
  128. if db['music']['tracks'][uuid]['path'] == new_path:
  129. print('Skipped moving file')
  130. continue
  131. print('Moving file...')
  132. os.makedirs(pathlib.Path(path, new_path).parent, exist_ok=True)
  133. shutil.move(os.path.join(path, db['music']['tracks'][uuid]['path']), os.path.join(path, new_path))
  134. db['music']['tracks'][uuid]['path'] = new_path
  135. moved_files += 1
  136. if not running:
  137. break
  138. print('Removing empty folders')
  139. remove_empty_folders(path)
  140. print('Added %s tracks' % added_files)
  141. print('Updated the metadata of %s tracks' % updated_meta)
  142. print('Moved %s files' % moved_files)
  143. print('Removed %s files' % removed_files)
  144. print('Dumping sync database')
  145. with open(os.path.join(path, 'sync.pkl'), 'wb') as f:
  146. pickle.dump(db, f)