You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

182 lines
5.6 KiB

  1. #!/usr/bin/env python3
  2. # flacmover.py -i sourcedir -o -destdir -f formatstring
  3. import sys, getopt, os, fnmatch
  4. import shutil, re
  5. from mutagen import File
  6. from mutagen.flac import FLAC, Picture
  7. def walk(root):
  8. for root, dirnames, filenames in os.walk(root):
  9. cover = None
  10. for filename in fnmatch.filter(filenames, '*.flac'):
  11. filepath = root + '/' + filename
  12. print(filename)
  13. tag = File(filepath)
  14. ext = os.path.splitext(filename)[1]
  15. directory, filename = buildpath(tag, ext)
  16. new_path = outpath + directory + '/'
  17. new_filepath = new_path + filename
  18. new_coverpath = new_path + 'folder.jpg'
  19. print(new_filepath)
  20. copy(filepath, new_filepath)
  21. if not os.path.isfile(new_coverpath):
  22. cover = getcover(filepath)
  23. if cover != None:
  24. print('Writing Albumart to {}'.format(new_coverpath))
  25. cdata = open(new_coverpath, 'wb')
  26. cdata.write(cover.data)
  27. cdata.close()
  28. def getcover(filepath):
  29. covers = FLAC(filepath).pictures
  30. for cover in covers:
  31. if cover.type == 3:
  32. print(str(cover))
  33. return(cover)
  34. return None
  35. def buildpath(tag, ext):
  36. parts = filenameformat.split('%')
  37. for index, part in enumerate(parts):
  38. if part != '':
  39. if part in tag:
  40. if part == 'tracknumber':
  41. parts[index] = tag[part][0].zfill(2)
  42. else:
  43. parts[index] = replacer(tag[part][0])
  44. else:
  45. parts[index] = part
  46. else:
  47. parts[index] = part
  48. path = ''.join(parts) + ext
  49. directory = '/'.join(path.split('/')[:-1])
  50. filename = ''.join(path.split('/')[-1])
  51. return directory, filename
  52. def replacer(content):
  53. table = {
  54. ord(u'Å'): 'Aa',
  55. ord(u'Å'): 'aa',
  56. ord(u'Ä'): 'Ae',
  57. ord(u'ä'): 'ae',
  58. ord(u'Æ'): 'Ae',
  59. ord(u'æ'): 'ae',
  60. ord(u'Á'): 'A',
  61. ord(u'á'): 'a',
  62. ord(u'À'): 'A',
  63. ord(u'à'): 'a',
  64. ord(u'Ã'): 'a',
  65. ord(u'ã'): 'a',
  66. ord(u'Ć'): 'C',
  67. ord(u'ć'): 'c',
  68. ord(u'Ç'): 'C',
  69. ord(u'ç'): 'c',
  70. ord(u'¢'): 'c',
  71. ord(u'Ð'): 'D',
  72. ord(u'ð'): 'd',
  73. ord(u'É'): 'E',
  74. ord(u'é'): 'e',
  75. ord(u'È'): 'E',
  76. ord(u'è'): 'e',
  77. ord(u'Ẽ'): 'E',
  78. ord(u'ẽ'): 'e',
  79. ord(u'Í'): 'I',
  80. ord(u'í'): 'i',
  81. ord(u'Í'): 'I',
  82. ord(u'í'): 'i',
  83. ord(u'Ì'): 'I',
  84. ord(u'ì'): 'i',
  85. ord(u'Ĩ'): 'I',
  86. ord(u'ĩ'): 'i',
  87. ord(u'ł'): 'l',
  88. ord(u'Ñ'): 'N',
  89. ord(u'ñ'): 'n',
  90. ord(u'Ö'): 'Oe',
  91. ord(u'ö'): 'oe',
  92. ord(u'Ø'): 'Oe',
  93. ord(u'ø'): 'oe',
  94. ord(u'Œ'): 'Oe',
  95. ord(u'œ'): 'oe',
  96. ord(u'Ó'): 'O',
  97. ord(u'ó'): 'o',
  98. ord(u'Ò'): 'O',
  99. ord(u'ò'): 'o',
  100. ord(u'Õ'): 'O',
  101. ord(u'õ'): 'o',
  102. ord(u'Ü'): 'Ue',
  103. ord(u'ü'): 'ue',
  104. ord(u'Ú'): 'U',
  105. ord(u'ú'): 'u',
  106. ord(u'Ù'): 'U',
  107. ord(u'ù'): 'u',
  108. ord(u'Ũ'): 'U',
  109. ord(u'ũ'): 'u',
  110. ord(u'ß'): 'ss',
  111. }
  112. content = content.translate(table)
  113. content = re.sub('[^\w\-_\. ]','_', content)
  114. return content
  115. def copy(srcfile, dstfile):
  116. if not os.path.exists(os.path.dirname(dstfile)):
  117. try:
  118. os.makedirs(os.path.dirname(dstfile))
  119. except OSError as exc: # Guard against race condition
  120. if exc.errno != errno.EEXIST:
  121. raise
  122. print('Copying {} to {}'.format(srcfile, dstfile))
  123. shutil.copy(srcfile, dstfile)
  124. def usage(help = False):
  125. if help:
  126. print('This program is intended to copy FLAC music libraries into another directory using a defined structure.\n')
  127. print('Copies all files within a source directory recursively to another directory and (re)names directories and filenames according to a specified format taken from the files Metadata.\nFor each folder in source an albumart image is exported from a FLAC file if possible.\n')
  128. print('flacmover.py -i SRCDIR -o DESTDIR -f FORMATSTRING \n'
  129. + ' -i SRCDIR source root of files; defaults to current dir \n'
  130. + ' -o DESTDIR destination root of the renamed dirs and files \n'
  131. + ' -f FORMATSTRING any format of directory file structure \n'
  132. + ' with / as dir delimiter and %variable% for any variable \n'
  133. + ' found in FLAC file tags as used by mutagen. \n'
  134. + ' ex.: \n'
  135. + ' %albumartist%/%albumartist%.%originalyear%.%album%/%discnumber%.%tracknumber%.%artist%.%title%')
  136. exit(1)
  137. def main(argv):
  138. global inpath, outpath, filenameformat
  139. inpath = os.getcwd()
  140. outpath = None
  141. filenameformat = None
  142. try:
  143. opts, args = getopt.getopt(argv,'hi:o:f:',['input=','output=','format='])
  144. except getopt.GetoptError:
  145. usage()
  146. for opt, arg in opts:
  147. if opt == '-h':
  148. usage(True)
  149. elif opt in ('-i', '--input'):
  150. inpath = arg
  151. elif opt in ('-o', '--output'):
  152. outpath = arg
  153. elif opt in ('-f', '--format'):
  154. filenameformat = arg
  155. else:
  156. usage()
  157. print('Input path is {}'.format(inpath))
  158. if outpath == None:
  159. print('No output directory set!')
  160. usage()
  161. if not outpath.endswith('/'):
  162. outpath = outpath + '/'
  163. walk(inpath)
  164. if __name__ == '__main__':
  165. main(sys.argv[1:])