From a0e50ceaf7672ef25a36958ba7aca8c41395d917 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 3 Jul 2016 23:43:32 -0400 Subject: [PATCH] Initial commit --- .project | 17 + musicman.ini | 11 + musicman.py | 564 ++++++++++++++++++ musicman/__init__.py | 2 + musicman/__init__.pyc | Bin 0 -> 169 bytes musicman/__pycache__/__init__.cpython-33.pyc | Bin 0 -> 183 bytes musicman/utils/__init__.py | 93 +++ musicman/utils/__init__.pyc | Bin 0 -> 812 bytes .../utils/__pycache__/__init__.cpython-33.pyc | Bin 0 -> 5382 bytes .../__pycache__/constants.cpython-33.pyc | Bin 0 -> 414 bytes .../utils/__pycache__/metadata.cpython-33.pyc | Bin 0 -> 4343 bytes .../utils/__pycache__/tagmap.cpython-33.pyc | Bin 0 -> 6813 bytes musicman/utils/constants.py | 7 + musicman/utils/constants.pyc | Bin 0 -> 405 bytes musicman/utils/metadata.py | 103 ++++ musicman/utils/metadata.pyc | Bin 0 -> 3770 bytes musicman/utils/tagmap.py | 289 +++++++++ musicman/utils/tagmap.pyc | Bin 0 -> 6795 bytes 18 files changed, 1086 insertions(+) create mode 100644 .project create mode 100644 musicman.ini create mode 100755 musicman.py create mode 100644 musicman/__init__.py create mode 100644 musicman/__init__.pyc create mode 100644 musicman/__pycache__/__init__.cpython-33.pyc create mode 100644 musicman/utils/__init__.py create mode 100644 musicman/utils/__init__.pyc create mode 100644 musicman/utils/__pycache__/__init__.cpython-33.pyc create mode 100644 musicman/utils/__pycache__/constants.cpython-33.pyc create mode 100644 musicman/utils/__pycache__/metadata.cpython-33.pyc create mode 100644 musicman/utils/__pycache__/tagmap.cpython-33.pyc create mode 100644 musicman/utils/constants.py create mode 100644 musicman/utils/constants.pyc create mode 100644 musicman/utils/metadata.py create mode 100644 musicman/utils/metadata.pyc create mode 100644 musicman/utils/tagmap.py create mode 100644 musicman/utils/tagmap.pyc diff --git a/.project b/.project new file mode 100644 index 0000000..7401a8e --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + mediamanager_media4 + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/musicman.ini b/musicman.ini new file mode 100644 index 0000000..2d86b17 --- /dev/null +++ b/musicman.ini @@ -0,0 +1,11 @@ +[origin] +Path = /srv/public/Music-Lossless +format = flac + +[target] +Path = /srv/public/Music-iTunes +format = m4a + +[working] +Path = /srv/public/Music-iTunes.new + diff --git a/musicman.py b/musicman.py new file mode 100755 index 0000000..5f7cb0f --- /dev/null +++ b/musicman.py @@ -0,0 +1,564 @@ +#!/usr/bin/env python3 + +from __future__ import division, absolute_import, print_function, unicode_literals + +import os +import signal +import sys +#import mutagen +import time +#from path import path + +import musicman + +#from musicman.utils.constants import ( +# VERSION, +# NO_TAGS +#) +#from musicman.utils import ( +# parse_args, +# load_config +#) +#from musicman.utils.metadata import MetaTag + +def spinning_cursor(): + while True: + for cursor in '|/-\\': + yield cursor + +spinner = spinning_cursor() + +def supports_color(): + """ + Returns True if the running system's terminal supports color, and False + otherwise. + """ + plat = sys.platform + supported_platform = plat != 'Pocket PC' and (plat != 'win32' or + 'ANSICON' in os.environ) + # isatty is not always implemented, #6223. + is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() + if not supported_platform or not is_a_tty: + return False + return True + +def clearLine(): + if supports_color(): + sys.stdout.write('\033[K') + sys.stdout.flush() + +def sanitize(text): + newtext = text + newtext = newtext.replace('<', '').replace('>', '').replace(':', '').replace('"', '').replace('|', '').replace('?', '').replace('*', '') + newtext = newtext.replace('/', '-') + newtext = newtext.strip() + #if newtext != text: + # clearLine() + # print("text:", text) + # print(" new:", newtext) + return newtext + +def getLibrary(path, excludeDirs=[]): + if os.path.isdir(path): + for root, directories, filenames in sorted(os.walk(path)): + if root not in excludeDirs: + for filename in filenames: + yield os.path.join(root, filename) + +# files=[] +# +# if os.path.isdir(path): +# for libraryDir, artists, dummy in os.walk(path): +# for artist in sorted(artists): +# for artistDir, albums, dummy in os.walk(os.path.join(libraryDir, artist)): +# if artistDir in excludeDirs: +# #clearLine() +# #print("Excluding:", artistDir) +# #print() +# continue +# for album in sorted(albums): +# for albumDir, dummy, songs in os.walk(os.path.join(artistDir, album)): +# if albumDir in excludeDirs: +# #clearLine() +# #print("Excluding:", albumDir) +# #print() +# continue +# #print("AlbumDir:", albumDir) +# #print(" Artist:", artist) +# #print(" Album:", album) +# for song in songs: +# print("Scanning", path, next(spinner), end="\r") +# files.append(os.path.join(albumDir, song)) +# #print("Path:", os.path.join(albumDir, song)) +# clearLine() +# print("Scan complete!") +# return files + +def getEmptyDirs(path, excludeDirs=[]): + if os.path.isdir(path): + for root, directories, filenames in sorted(os.walk(path)): + if root not in excludeDirs: + if not directories and not filenames: + yield root + +def getSong(file): + from musicman.utils.metadata import MetaTag + from musicman.utils.constants import INTERNAL_FORMATS + global config + song = {} + + #if file.endswith(INTERNAL_FORMATS): + if (os.path.splitext(file)[1][1:] in INTERNAL_FORMATS): + metadata = MetaTag(file) + + if metadata.tags.get("artist") is None or len(metadata.tags.get("artist")) < 1: + return {'metadata': None} + elif metadata.tags.get("albumartist") is None or len(metadata.tags.get("albumartist")) < 1: + return {'metadata': None} + elif metadata.tags.get('album') is None or len(metadata.tags.get("album")) < 1: + return {'metadata': None} + elif metadata.tags.get("musicbrainz_albumid") is None or len(metadata.tags.get("musicbrainz_albumid")) < 5: + return {'metadata': None} + else: + song['metadata'] = metadata + song['artistName'] = sanitize(metadata.tags["albumartist"]) + song['albumName'] = sanitize(metadata.tags["album"]) + song['titleName'] = sanitize(metadata.tags["title"]) + if isinstance(metadata.tags.get("totaldiscs", 0), tuple): + song['discnumber'] = int(metadata.tags.get('totaldiscs', 0)[0]) + song['totaldiscs'] = int(metadata.tags.get("totaldiscs", 0)[1]) + else: + song['discnumber'] = int(metadata.tags.get('discnumber', 0)) + song['totaldiscs'] = int(metadata.tags.get("totaldiscs", 0)) + if isinstance(metadata.tags.get('totaltracks', 0), tuple): + song['tracknumber'] = int(metadata.tags.get('totaltracks', 0)[0]) + song['totaltracks'] = int(metadata.tags.get('totaltracks', 0)[1]) + else: + song['tracknumber'] = int(metadata.tags.get('tracknumber', 0)) + song['totaltracks'] = int(metadata.tags.get('totaltracks', 0)) + + if song['totaldiscs'] > 1: + song['outPath'] = os.path.join(song['artistName'], song['albumName']) + song['outFile'] = '{0:d}-{1:02d}-{2}'.format(song['discnumber'], + song['tracknumber'], + song['titleName']) + else: + if metadata.tags.get("tracknumber") is not None: + song['outPath'] = os.path.join(song['artistName'], song['albumName']) + song['outFile'] = '{0:02d}-{1}'.format(song['tracknumber'], song['titleName']) + else: + song['outPath'] = os.path.join(song['artistName'], song['albumName']) + outFile = '{0}'.format(song['titleName']) + + return song + else: + clearLine() + print("FATAL: File extension \"{0}\" is not supported.".format(os.path.splitext(file)[1])) + print(" Supported:", ", ".join(INTERNAL_FORMATS)) + sys.exit(2) + +def getSongPaths(song): + global originDir + global workingDir + global targetDir + + print("DEBUG:", originDir) + print("DEBUG:", workingDir) + print("DEBUG:", targetDir) + + sys.exit(0) + +def cleanLibrary(originDir, excludeDirs=[], act=False, verbose=0): + global config + + if excludeDirs is None: + excludeDirs=[] + + print("Clean Dirs") + + #while getEmptyDirs(originDir, excludeDirs) != [] and act == True and tries > 0: + + if act: + try: + while getEmptyDirs(originDir, excludeDirs).__next__(): + for path in getEmptyDirs(originDir, excludeDirs): + print("Removing:", path) + + if act: + try: + os.rmdir(path) + except OSError as err: + print("ERROR: Failed to remove directory:", err) + sys.exit(5) + except StopIteration: + pass + else: + for path in getEmptyDirs(originDir, excludeDirs): + print("Empty:", path) + + print("Processing Complete!") + +def renameLibrary(originDir, excludeDirs=[], act=False, verbose=0): + global config + + if excludeDirs is None: + excludeDirs=[] + + print("Rename") + for file in getLibrary(originDir, excludeDirs): + #print("File:", file) + if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): + clearLine() + print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") + + song = getSong(file) + + #print("song", song) + if song['metadata'] is None: + if verbose > 2: + clearLine() + print("Skipping: {0} due to lack of metadata".format(file)) + else: + filename = os.path.basename(file) + file_ext = os.path.splitext(filename)[1] + + if not os.path.isfile(os.path.join(originDir, song['outPath'], song['outFile'] + file_ext)): + clearLine() + print("Found:", file) + if verbose > 0: + print(" New:", os.path.join(originDir, song['outPath'], song['outFile'] + file_ext)) + + if (act): + if verbose > 1: + print("Renaming: \"{0}\" -> \"{1}\"".format(file, os.path.join(originDir, song['outPath'], song['outFile'] + file_ext))) + try: + os.renames(file, + os.path.join(originDir, + song['outPath'], + song['outFile'] + file_ext)) + except OSError as err: + print("ERROR: Failed to move:", err) + sys.exit(5) + + clearLine() + print("Processing Complete!") + +def findUntagged(originDir, excludeDirs=[], verbose=0): + global config + + if excludeDirs is None: + excludeDirs=[] + + print("Find Untagged") + for file in getLibrary(originDir, excludeDirs): + if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): + clearLine() + print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") + + song = getSong(file) + + if song['metadata'] is None: + clearLine() + print("Untagged: {0}".format(file)) + clearLine() + print("Processing Complete!") + +def findNew(originDir, workingDir, targetDir, targetFormat, excludeDirs=[], verbose=0): + global config + + if excludeDirs is None: + excludeDirs=[] + + print("Find New Media") + for file in getLibrary(originDir, excludeDirs): + if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): + clearLine() + print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") + + song = getSong(file) + + getSongPaths(song) + + if song['metadata'] is None: + if verbose > 2: + clearLine() + print("Skipping: {0} due to lack of metadata".format(file)) + else: + filename = os.path.basename(file) + file_ext = os.path.splitext(filename)[1] + + if not (os.path.isfile(os.path.join(workingDir, song['outPath'], song['outFile'] + '.' + config['target']['format'])) or + os.path.isfile(os.path.join(targetDir, song['outPath'], song['outFile'] + '.' + config['target']['format']))): + print("New:", file) + if verbose > 0: + if not os.path.isfile(os.path.join(targetDir, song['outPath'], song['outFile'] + '.' + config['target']['format'])): + print(" No:", os.path.join(targetDir, song['outPath'], song['outFile'] + '.' + config['target']['format'])) + elif not os.path.isfile(os.path.join(workingDir, song['outPath'], song['outFile'] + '.' + config['target']['format'])): + print(" No:", os.path.join(workingDir, song['outPath'], song['outFile'] + '.' + config['target']['format'])) + #if not os.path.isfile(os.path.join(originDir, song['outPath'], song['outFile'] + file_ext)): + # clearLine() + # print("New:", file) + + clearLine() + print("Processing Complete!") + +def syncWorking(workingDir, targetDir, excludeDirs=[], act=False, verbose=0): + global config + + if excludeDirs is None: + excludeDirs=[] + + print("Sync Target Media") + + for file in getLibrary(workingDir, excludeDirs): + if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): + clearLine() + #print("Checking:", file) + print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") + + song = getSong(file) + + if song['metadata'] is None: + if verbose > 2: + clearLine() + print("Skipping: {0} due to lack of metadata".format(file)) + else: + filename = os.path.basename(file) + file_ext = os.path.splitext(filename)[1] + + if not os.path.isfile(os.path.join(targetDir, song['outPath'], song['outFile'] + file_ext)): + print("Sync:", file) + if verbose > 0: + print(" To:", os.path.join(targetDir, song['outPath'], song['outFile'] + file_ext)) + + if act: + print("would've acted") + + clearLine() + print("Processing Complete!") + + +if __name__ == '__main__': + global opt + global config + global originPath, targetPath, targetFormat, workPath + import configparser + #opt, files = parse_args() + opt = musicman.utils.parse_args() + config = musicman.utils.load_config() + + print("opt:", opt) + + try: + originDir = config['origin']['path'] if opt.originDir is None else opt.originDir + except AttributeError: + originDir = config['origin']['path'] + + try: + targetDir = config['target']['path'] if opt.targetDir is None else opt.targetDir + except AttributeError: + targetDir = config['target']['path'] + + try: + targetFormat = config['target']['format'] if opt.targetFormat is None else opt.targetFormat + except AttributeError: + targetFormat = config['target']['format'] + + try: + workingDir = config['working']['path'] if opt.workingDir is None else opt.workingDir + except AttributeError: + workingDir = config['working']['path'] + + if opt.mode is None: + print("ERROR: No command provided.") + sys.exit(1) + + + try: + if opt.mode == 'clean': + cleanLibrary(originDir, opt.excludeDirs, opt.act, opt.verbose) + + elif opt.mode == 'rename': + renameLibrary(originDir, opt.excludeDirs, opt.act, opt.verbose) + + elif opt.mode == 'scan': + if opt.scanMode is None: + print("ERROR: Subcommand for scan not provided.") + sys.exit(1) + elif opt.scanMode == 'untagged': + findUntagged(originDir, opt.excludeDirs, opt.verbose) + elif opt.scanMode == 'new': + findNew(originDir, workingDir, targetDir, targetFormat, opt.excludeDirs, opt.verbose) + elif opt.mode == 'sync': + syncWorking(workingDir, targetDir, opt.excludeDirs, opt.act, opt.verbose) + except KeyboardInterrupt: + clearLine() + print(end='\r') + print("Aborted by user") + + + + #for file in getLibrary(config['origin']['path']): + # #print("File:", file) + # if (os.path.isdir(os.path.dirname(file)) and os.path.isfile(file)): + # clearLine() + # print("Processing:", os.path.dirname(os.path.dirname(file)), next(spinner), end="\r") + # #time.sleep(0.01) + # + # #print("Path:", os.path.dirname(file)) + # #print("File:", os.path.basename(file)) + #clearLine() + #print("Processing Complete!") + + sys.exit(0) + #config = configparser.ConfigParser() + #config.read('library.ini') + + #print(config.sections()) + #print(config["lossless"]["Path"]) + + #try: + # #print("Test1:", config.get('lossless', 'test')) + # config.get('lossless', 'path') + # config.get('lossless', 'format') + # config.get('converted', 'path') + # config.get('converted', 'format') + #except configparser.NoOptionError as err: + # print("ERROR: Configuration of required settings are missing:", err) + # sys.exit(1) + + LosslessLibraryRoot = '/srv/public/Music-Lossless' + #LosslessLibraryRoot = '/srv/public/Music-iTunes' + iTunesLibraryRoot = '/srv/public/Music-iTunes' + + #artistWalker = os.walk(LosslessLibraryRoot) + #dest_dir, artists, files = artistWalker.next() + + #print "dest_dir: %s" % dest_dir + #print "artists: %s" % artists + #print "files: %s" % files + + #for artist in artists: + # print "Artist: %s" % artist + + # albumWalker = os.walk(os.path.join(dest_dir, artist)) + # artist_dir, albums, artist_files = albumWalker.next() + + # print "Albums: %s" % albums + # print "Album Dir: %s" % artist_dir + + # for album in albums: + # songWalker = os.walk(os.path.join(artist_dir, album) + # album_dir, dummy, songs = songWalker.next() + + for libraryDir, artists, dummy in os.walk(config['origin']['path']): + for artist in sorted(artists): + for artistDir, albums, dummy in os.walk(os.path.join(libraryDir, artist)): + for album in sorted(albums): + for albumDir, dummy, songs in os.walk(os.path.join(artistDir, album)): + #print("AlbumDir:", albumDir) + #print(" Artist:", artist) + #print(" Album:", album) + for song in songs: + if song.endswith('.flac'): + #print " Song: %s" % song + metadata = MetaTag(os.path.join(albumDir, song)) + #if os.path.isfile(os.path.join() + #print "MetaData: %s" % metadata.tags + #os.path.join(iTunesLibraryRoot, [metadata.tags["artist"], metadata.tags["album"]]) + #print("\033[KArtist:", metadata.tags["artist"], end="\r") + + clearLine() + print("Scanning", artistDir, next(spinner), end="\r") + + #if int(metadata.tags["totaldiscs"]) > 1: + if metadata.tags.get("artist") is None: + continue + if metadata.tags.get("albumartist") is None: + continue + if metadata.tags.get('album') is None: + continue + if metadata.tags.get("musicbrainz_albumid") is None or len(metadata.tags.get("musicbrainz_albumid")) < 5: + clearLine() + print("Skipping:", os.path.join(albumDir, song)) + continue + + #if 'Centennial' in song: + # print + # print + # print("DEBUG") + # print("Path:", artistDir) + # print("Song:", song) + # print("musicbrainz_albumid:", metadata.tags.get("musicbrainz_albumid")) + # print(type(metadata.tags.get("musicbrainz_albumid"))) + # sys.exit(0) + + artistName = sanitize(metadata.tags["albumartist"]) + albumName = sanitize(metadata.tags["album"]) + titleName = sanitize(metadata.tags["title"]) + outPath = '' + outFile = '' + + if int(metadata.tags.get("totaldiscs", 0)) > 1: + outPath = os.path.join(config['converted']['path'], + artistName, + albumName) + outFile = '{0:d}-{1:02d}-{2}.{3}'.format(int(metadata.tags["discnumber"]), + int(metadata.tags["tracknumber"]), + titleName, + 'm4a') + #print("iTunes:", os.path.join(iTunesLibraryRoot, + # metadata.tags["artist"], + # metadata.tags["album"], + # '{0:d}-{1:02d}-{2}.{3}'.format(int(metadata.tags["discnumber"]), + # int(metadata.tags["tracknumber"]), + # metadata.tags["title"], + # 'm4a'))) + # #int(metadata.tags["discnumber"]) + '-' + '{0:02d}'.format(int(metadata.tags["tracknumber"])) + '-' + metadata.tags["title"] + ".m4a") + else: + if metadata.tags.get("tracknumber") is not None: + outPath = os.path.join(config['converted']['path'], + artistName, + albumName) + outFile = '{0:02d}-{1}.{2}'.format(int(metadata.tags["tracknumber"]), titleName, 'm4a') + #print("iTunes:", os.path.join(iTunesLibraryRoot, + # metadata.tags["artist"], + # metadata.tags["album"], + # '{0:02d}'.format(int(metadata.tags["tracknumber"])) + '-' + metadata.tags["title"] + ".m4a")) + else: + outPath = os.path.join(config['converted']['path'], + artistName, + albumName) + outFile = '{0}.{1}'.format(titleName, 'm4a') + #print("iTunes:", os.path.join(iTunesLibraryRoot, + # metadata.tags["artist"], + # metadata.tags["album"], + # metadata.tags["title"] + ".m4a")) + #print "iTunes: %s" % os.path.join(iTunesLibraryRoot, metadata.tags["artist"], metadata.tags["album"]) + if not os.path.isfile(os.path.join(outPath, outFile)): + print("NEW:", os.path.join(outPath, outFile)) + if song.endswith('.m4a'): + #print " Song: %s" % song + metadata = MetaTag(os.path.join(albumDir, song)) + #print "MetaData: %s" % metadata.tags + clearLine() + print() + + +#LosslessLibraryRoot = '/srv/public/Music-Lossless' +# +#for subdir, dirs, files in os.walk(LosslessLibraryRoot): +# print subdir +# +# for file in files: +# if file.endswith('.flac'): +# audio = mutagen.File(os.path.join(subdir, file)) +# print file +# #print audio.tags.pprint() +# print "Artist: %s" % audio['ARTIST'][0] +# print "Album: %s" % audio['ALBUM'][0] +# print "Title: %s" % audio['TITLE'][0] +# +# #for file in files: +# # print os.path.join(subdir, file) + diff --git a/musicman/__init__.py b/musicman/__init__.py new file mode 100644 index 0000000..d52db2d --- /dev/null +++ b/musicman/__init__.py @@ -0,0 +1,2 @@ +import musicman.utils + diff --git a/musicman/__init__.pyc b/musicman/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f9e57d340109864fa8ba90270b758827cb9ede2 GIT binary patch literal 169 zcmZSn%*&N^xhOoD0ScIav;zA-eVB<1_OzOXB18 Z3MxxDfGTZr^HWN5QtcRl>|!8h008?hCrSVS literal 0 HcmV?d00001 diff --git a/musicman/__pycache__/__init__.cpython-33.pyc b/musicman/__pycache__/__init__.cpython-33.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c430d567e3d5239915547081a69a704d7484e80 GIT binary patch literal 183 zcmbQo!^;&txiVawfq@|zh~a<<$Z`PUVgVqL0z`}qISdR@j37p^21qs&h#CCA%u+re zGqHLZG%t97rOkfaI#O+;|fVw%6JjgCfDH zya%tvd+-48?S_;)ws-vPcs<{5Cj51K{Nty6zoNfafcFb5{)ABAQB)D7H(F7u_W6L) zV6Q_;!@cfN+S}_srG2jhst%|gP&%M`Na>JN5z=>*9yyO?)PrC78`msy3~~=)`F63G zC+BCU*{gZ-=H>ZP2FSiUOO~(aGmosWA7Pzf@hO53eFPvLpoJs>T~qLl=qvI71Q&1) zNFWAO45@(qn686Rxf6Yy2FooJnn@qA<9ws}KGdVun$6_Gv{IUeFDDo4veJC%i9Tz~ zF6+RL*F1EuqeKjc03t(SD5WeGyASiOtUUGyRPI2hj#8$?>@y)Sn@_V!58vfdy0Qe a{8uL)?OAP#uF}uleE1H+p}M()qu>wrjIsRy literal 0 HcmV?d00001 diff --git a/musicman/utils/__pycache__/__init__.cpython-33.pyc b/musicman/utils/__pycache__/__init__.cpython-33.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba5ef8c5c8c0b08301b1fc4a0abb612726180ede GIT binary patch literal 5382 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 literal 0 HcmV?d00001 diff --git a/musicman/utils/__pycache__/metadata.cpython-33.pyc b/musicman/utils/__pycache__/metadata.cpython-33.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de8e9f537f5507a42827f9493bf711a549e8434e GIT binary patch literal 4343 zcmbtXUvC@75uZI$lt@vQEXj^&w>8_e1s@tJbp#hJ1f#Yj2SH+5h*&5QVjNC)C;2Ge z9d&yrS%CE9qIMpOycT^c(0+hE_EYr36n*P&X79)o;}(TYahEguXLfdGezT)LHy0Y) z-(BqgtwH2c>C*)LTQvPobRNEnhC~NrTG4@`i?d?Nq64d-Dfzyc@Wm5uOmfYv7#0Y7b#q#gPWv2^(y35>Gv3}lD|x^ zpxX-HWv?@fdj)9J>nQYD6@^xW@3i(wU%+M>0 z)X1;VU{>fk^6R8OQo%g=4I0eTpvjs&$e9I#Vg+-9l`EJ<@@B@&65(#e%uVuU%go%E zSthSOW^ld6m{}!nzRWb~!NDyGW<{ynH28qL1v9@U%nie|gjqDq9buLXbC(7`An&GO zeke1`hWU{&D>&dDMNKv>pM?G%c~IjPJK>;B-fi+e5cK1=Q!H|-Kf8CB5&yHHr&(pU#5xUra>IFG1?9y?I*$oQ5y5mdYrV6k~nNTnHR+E zqX0V_^{fU-4p!6K|1t>I`*T)mpHiaog|ybGCu1h%ECiG=i}5F%An5X zFz?3UD24_iet3}tt`-4apxq3RA|~?v9AMpW^ocC!o`;Sh!VO1G?;Z^uH{*coo}cAY zBi9h|@v&=RhClqvZ3|6*jP6o#WZF0jfoZWh!44E{zcLe+@D&bAg}~kG@EN^vn6oTa zZ4XVi(7|UA@O+W3B+w!LJBvh;J!zrP-4I&ecLTF=s8sMkYy+jCNWAIQo(|te=wrCd^a|GpV*-@8U@j@7?^P{ahFGz zxfRRN{_to@)_Yx#gQigaS1h07l2l!_RJH_a`hwTo!~w<5(^YsP6(|0zuKY7QOt3#Kj!1Yz^wXZTtCO=o^c^#XQj)iKY{al0VYL zs;q7B+BX)xvPkWp{+n~}d7r)p_0?&g-e8X<$NilNuq`CH7N0rK(l&VwgjR`#%6g$poAkFhJ8d{@vae{cA4Zm{>v6>*#W%{4BTk0GbkOnbg<-VprH} zf*)+5jsS)|sZ*S)&x7=Zi9zUZCUZ1#WFSy+2|;XuF8RgaIB+{G_PvqTV~uj8eF(Y; z#N4WH16r@Mz$f#j*rdIYgUigZq|q=)v2_YMlIVu5xB}V{YF8Dy1X74j z<|z3&6MNt<4G#asYYpb{Fl>SWAQ^Bkc?H(BuBvp2`USmY24HiO`I&;B0c!UYZ>-72 zKNTAROhGozo~&sI`mx>!=QUR|WKz#yj?y{}kU~%_8P#iaiL6vUi}|uHI6L}=FOloL zr(Ql~4Yr>@rmI=HoaNMldobo2ax5SDf842|P8&+H2tSDY()X?<0|6P_DA6>2w8J1Y z^#MOt`P7DE5(hh~i+6hRmQSK?YMe{!sRyYX;q3ln-yF?PtSIKjXMR$I_jNru;>rM$ zI)Vpx@rq`sXgHA^I@*7*k#W8}e6Zp9ZtVF2&Pf*PaJZ%iRsdi{G~tcssy%AzA+d9jV*EQx$Cit)Z6G3VzqnZm7H9cyi%GUHOoNg_%;m-? zWUf~nWe0Ppz&0jjBFIb*>^-ylk3QX#GNf=PTM#dq-Yvemrg46n?hd z0r$?;COa_?i&c>NH|UlsHLHpITtl8-c+20Cb$xuLQd8J5(_Ah8>++=EVTr}-Si)c# zubg>uXLs4rWuCooo!_{|vr{-Ki~Zsh#$++zh;z8AEJntzxmg;VxQIRa1l6=J7byag zb@&Yb<|OG(lEmia?kAb4sf$X!E0uh=1Okw{3=FnFd^qrtlS)>fcu%NzcyKv{HVsT4 Wa(L)AI`&@$_@Dl3z@PKhlJy^1XRT}i literal 0 HcmV?d00001 diff --git a/musicman/utils/__pycache__/tagmap.cpython-33.pyc b/musicman/utils/__pycache__/tagmap.cpython-33.pyc new file mode 100644 index 0000000000000000000000000000000000000000..830f5b6e0cd9b94be49eed0e20c6e3854ac85990 GIT binary patch literal 6813 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 aefL}GCGRDBGNN0GjZ=M}`o3tRd*wd}%GJ#P literal 0 HcmV?d00001 diff --git a/musicman/utils/constants.py b/musicman/utils/constants.py new file mode 100644 index 0000000..cf696bc --- /dev/null +++ b/musicman/utils/constants.py @@ -0,0 +1,7 @@ +NO_TAGS = False +SILENT = False +VERSION = "0.0.1" +SUPPORTED_FORMATS = {'mp3', 'wma', 'wav', 'ogg', 'flac', 'm4a', 'mpc', 'wv', 'avi'} +EXTERNAL_FORMATS = {'mpc', 'wv'} +INTERNAL_FORMATS = SUPPORTED_FORMATS - EXTERNAL_FORMATS + diff --git a/musicman/utils/constants.pyc b/musicman/utils/constants.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52f0ba93c3dace36b28a88d4e3b6b7e74c3dd1d0 GIT binary patch literal 405 zcmY*R%W8u_6ulEQiMEAy*KK!M6ipw?QW~f!fn*G7AGisFW0Qb7lDSF9PxR;dDgA+7 zwNNs{oYy^bjh{2;>wEK_!r!rppXlisK?abp20I{08z31-(j-if7RWBh9>_jO8&)7j^tPp9f*=KQG2X zI2*+go>S_7#D2&})1z?0kHY~K;UDxhS#P5hdOF>#E7V?@?Qgx}fJ)`w7T3AFq-ME0 zvzneIMSwghh~!&e zDcU6+&B=aKo+Q_$8qLeGE7>2#7!xf}oEZ6>emc@$l);khUl9{lBwCh3BVkp3E%H+2 zSNkjclun!97yk=CJ8c6puW|~GY(4|xQIrRJ!O;Ya3S{>%>_g~AfB_aU!=sAys;M7&=U6Zg`tY24VzF=-BvrsT=$}ASlJJP!;;ZnhT zLsza8%r});#sTk2(v%p0;3UlNO1MJk_istKszgh|t4e%JyiWTXQGI~PZ9EH-upikL zqR@(7H`tX05|49khZ`7^khn7(3lYUWcUsnl{fF;Glmn z8Yq>=c|TI>C`z(O!+jP(bq;Dg>o^$=4seL_X&&@dX!BXDchkWzr3WJw zT*SvYhRA{L8M*(mkUGj&n0N+B*=ck8PM1kdivDyk>>4IS1e>2O%7rE{?EcP?=XGqN z`cx(Cq)DWPa@tt`Ag9O2(ODb+L?`Zjlo%-ru~gk9{26W z0Cf}wd9Xe_v&3mDjJr9}{OD)kENwTWO&1l$I2n#|pRvrZLgtUl(D&o;Lw^tqhjDV` zVL`M=$2n5CNgFomA9_n5{(mnu+&C|ZbKn07Z8GFy8s?grKuQtuumaR_4MuMg1psSg zt!N5Bxd>gaCdYNj{wc_y-b@)VBVRi5;?K&?Qgf}>U>NT|{eyhr$d_0bc@2Y(96OTz zR?b#c*QM@jM_xK&o;?3MTj29O0r5Hhy~Y;kaOz-Co#sy^Bb z?eD4aIq5a!mB=e2uN*n7uqcIMNZIyv%G25b0 z^Jmdiu|hd!&MDwprYu_s#ABjpdHu5UaBl)S1H6J^H!zL!_l-U2lkUE~`aZ!^f=B88+N(d27`mam!qk7%Mu z|DEwlWOhnZi2k$2q*yq}JxuPfyW?hy}#OT{RfNu_#gD$NybASS(8?Z(tt` zW*7sTFN5>P5%VVQ!c%(I+1K?rjYF1gXP}*4DU&W{8?Y_%AAQj)X<0nw9K55*_g8 zMLGO~dBIK4aAXS%>VkRyf*R0UyQs=}mG}8rs|qdfwUV!)cshm~>$>sxbP2j&@YTr=b|=l0KHj3Fz_%quhDP1UYB$9hUv4&Py2$ilP~xYW4}A*#Upxf|LHxs zKvS7vh2uL|2d*B=BmakQa?NwsXg%}#;03tjvHyDS5o?0Z3r!Pj5TmjwVOO8rY+ARI zJM8kF)7<$gd6U+^jY)IrD9Syp0mitSol-7Nb>QH*? zdiTDIO}pIqLvF|Q(KJic2Z1x*A{;qA9M$$7Klo8W z+Lo_A|0(&@K*!1{t zP@IEdbaGp%yN@4$;x`XQHX7nJrt!ps=p@Y!@S5YTe)$|oVK&OVRS30*DA<>=QtGXn z5pN?}v_HpGuhg9;+D{#AX#OjHmz+1{E0wybJB?anrLA7wq-*bUYGKkx<+x9;6Lr>D z$rGzL`OP4C?a$ literal 0 HcmV?d00001 diff --git a/musicman/utils/tagmap.py b/musicman/utils/tagmap.py new file mode 100644 index 0000000..2356d3b --- /dev/null +++ b/musicman/utils/tagmap.py @@ -0,0 +1,289 @@ +tags = { + '.mp3': { + 'album' : 'TALB', + 'albumsort' : 'TSOA', + 'title' : 'TIT2', + 'titlesort' : 'TSOT', + 'work' : 'TOAL', + 'artist' : 'TPE1', + 'artistsort' : 'TSOP', + 'albumartist' : 'TPE2', + 'albumartistsort' : 'TSO2', + 'date' : 'TDRC', + 'originaldate' : 'TDOR', + 'composer' : 'TCOM', + 'composersort' : 'TSOC', + 'lyricist' : 'TEXT', + 'writer' : 'TXXX:Writer', + 'conductor' : 'TPE3', + 'performer:instrument': 'TMCL:instrument', + 'remixer' : 'TPE4', + 'arranger' : 'TIPL:arranger', + 'engineer' : 'TIPL:engineer', + 'producer' : 'TIPL:producer', + 'djmixer' : 'TIPL:DJ-mix', + 'mixer' : 'TIPL:mix', + 'label' : 'TPUB', + 'grouping' : 'TIT1', + 'subtitle' : 'TIT3', + 'discsubtitle' : 'TSST', + 'tracknumber' : 'TRCK', + 'totaltracks' : 'TRCK', + 'discnumber' : 'TPOS', + 'totaldiscs' : 'TPOS', + 'compilation' : 'TCMP', + 'comment:description' : 'COMM:description', + 'genre' : 'TCON', + '_rating' : 'POPM', + 'bpm' : 'TBPM', + 'mood' : 'TMOO', + 'lyrics:description' : 'USLT:description', + 'media' : 'TMED', + 'catalognumber' : 'TXXX:CATALOGNUMBER', + 'releasestatus' : 'TXXX:MusicBrainz Album Status', + 'releasetype' : 'TXXX:MusicBrainz Album Type', + 'releasecountry' : 'TXXX:MusicBrainz Album Release Country', + 'script' : 'TXXX:SCRIPT', + 'language' : 'TLAN', + 'copyright' : 'TCOP', + 'license' : 'WCOP', + 'encodedby' : 'TENC', + 'encodersettings' : 'TSSE', + 'barcode' : 'TXXX:BARCODE', + 'isrc' : 'TSRC', + 'asin' : 'TXXX:ASIN', + 'albumart' : 'APIC:', + 'musicbrainz_recordingid' : 'UFID:http://musicbrainz.org', + 'musicbrainz_releasetrackid' : 'TXXX:MusicBrainz Release Track Id', + 'musicbrainz_albumid' : 'TXXX:MusicBrainz Album Id', + 'musicbrainz_artistid' : 'TXXX:MusicBrainz Artist Id', + 'musicbrainz_albumartistid' : 'TXXX:MusicBrainz Album Artist Id', + 'musicbrainz_releasegroupid' : 'TXXX:MusicBrainz Release Group Id', + 'musicbrainz_workid' : 'TXXX:MusicBrainz Work Id', + 'musicbrainz_trmid' : 'TXXX:MusicBrainz TRM Id', + 'musicbrainz_discid' : 'TXXX:MusicBrainz Disc Id', + 'acoustic_id' : 'TXXX:Acoustic Id', + 'acousticid_fingerprint' : 'TXXX:Acousticid Fingerprint', + 'musicip_puid' : 'TXXX:MusicIP PUID', + 'website' : 'WOAR', + }, + '.wma': { + 'artist' : 'Author', + 'album' : 'WM/AlbumTitle', + 'title' : 'Title', + 'genre' : 'WM/Genre', + 'year' : 'WM/Year', + 'tracknumber' : 'WM/TrackNumber', + 'composer' : 'WM/Composer', + 'publisher' : 'WM/Publisher', + 'lyrics' : 'WM/Lyrics', + 'albumartist' : 'WM/AlbumArtist', + }, + '.wmv': { + 'artist' : 'Author', + 'album' : 'WM/AlbumTitle', + 'title' : 'Title', + 'genre' : 'WM/Genre', + 'year' : 'WM/Year', + 'tracknumber' : 'WM/TrackNumber', + 'composer' : 'WM/Composer', + 'publisher' : 'WM/Publisher', + 'lyrics' : 'WM/Lyrics', + }, + '.m4a': { + 'album' : '\xa9alb', + 'albumsort' : 'soal', + 'title' : '\xa9nam', + 'titlesort' : 'sonm', + 'artist' : '\xa9ART', + 'artistsort' : 'soar', + 'albumartist' : 'aART', + 'albumartistsort' : 'soaa', + 'date' : '\xa9day', + 'composer' : '\xa9wrt', + 'composersort' : 'soco', + 'lyricist' : '----:com.apple.iTunes:LYRICIST', + 'conductor' : '----:com.apple.iTunes:CONDUCTOR', + 'remixer' : '----:com.apple.iTunes:REMIXER', + 'engineer' : '----:com.apple.iTunes:ENGINEER', + 'producer' : '----:com.apple.iTunes:PRODUCER', + 'djmixer' : '----:com.apple.iTunes:DJMIXER', + 'mixer' : '----:com.apple.iTunes:MIXER', + 'label' : '----:com.apple.iTunes:LABEL', + 'grouping' : '\xa9grp', + 'subtitle' : '----:com.apple.iTunes:SUBTITLE', + 'discsubtitle' : '----:com.apple.iTunes:DISCSUBTITLE', + 'tracknumber' : 'trkn', + 'totaltracks' : 'trkn', + 'discnumber' : 'disk', + 'totaldiscs' : 'disk', + #'compilation' : 'cpil', + 'comment' : '\xa9cmt', + 'genre' : '\xa9gen', + 'bpm' : 'tmpo', + 'mood' : '----:com.apple.iTunes:MOOD', + 'lyrics' : '\xa9lyr', + 'media' : '----:com.apple.iTunes:MEDIA', + 'catalognumber' : '----:com.apple.iTunes:CATALOGNUMBER', + 'show' : 'tvsh', + 'showsort' : 'sosn', + 'podcast' : 'pcst', + 'podcasturl' : 'purl', + 'releasestatus' : '----:com.apple.iTunes:MusicBrainz Album Status', + 'releasetype' : '----:com.apple.iTunes:MusicBrainz Album Type', + 'releasecountry' : '----:com.apple.iTunes:MusicBrainz Album Release Country', + 'script' : '----:com.apple.iTunes:SCRIPT', + 'language' : '----:com.apple.iTunes:LANGUAGE', + 'copyright' : 'cprt', + 'license' : '----:com.apple.iTunes:LICENSE', + 'encodedby' : '\xa9too', + 'barcode' : '----:com.apple.iTunes:BARCODE', + 'isrc' : '----:com.apple.iTunes:ISRC', + 'asin' : '----:com.apple.iTunes:ASIN', + 'albumart' : 'covr', + 'musicbrainz_recordingid' : '----:com.apple.iTunes:MusicBrainz Track Id', + 'musicbrainz_releasetrackid' : '----:com.apple.iTunes:MusicBrainz Release Track Id', + 'musicbrainz_albumid' : '----:com.apple.iTunes:MusicBrainz Album Id', + 'musicbrainz_artistid' : '----:com.apple.iTunes:MusicBrainz Artist Id', + 'musicbrainz_albumartistid' : '----:com.apple.iTunes:MusicBrainz Album Artist Id', + 'musicbrainz_releasegroupid' : '----:com.apple.iTunes:MusicBrainz Release Group Id', + 'musicbrainz_workid' : '----:com.apple.iTunes:MusicBrainz Work Id', + 'musicbrainz_trmid' : '----:com.apple.iTunes:MusicBrainz TRM Id', + 'musicbrainz_discid' : '----:com.apple.iTunes:MusicBrainz Disc Id', + 'acoustic_id' : '----:com.apple.iTunes:Acoustid Id', + 'acousticid_fingerprint' : '----:com.apple.iTunes:Acoustid Fingerprint', + 'musicip_puid' : '----:com.apple.iTunes:MusicIP PUID', + }, + '.mp4': { + 'artist' : '\xa9ART', + 'album' : '\xa9alb', + 'title' : '\xa9nam', + 'genre' : '\xa9gen', + 'year' : '\xa9day', + 'tracknumber' : 'trkn', + 'composer' : '\xa9wrt', + 'lyrics' : '\xa9lyr', + 'albumartist' : 'aART', + 'disk' : 'disk', + 'comment' : '\xa9cmt', + 'albumart' : 'covr', + }, + '.aac': { + 'artist' : '\xa9ART', + 'album' : '\xa9alb', + 'title' : '\xa9nam', + 'genre' : '\xa9gen', + 'year' : '\xa9day', + 'tracknumber' : 'trkn', + 'composer' : '\xa9wrt', + 'lyrics' : '\xa9lyr', + 'albumartist' : 'aART', + 'disk' : 'disk', + 'comment' : '\xa9cmt', + }, + '.ogg': { + 'artist' : 'artist', + 'album' : 'album', + 'title' : 'title', + 'genre' : 'genre', + 'year' : 'date', + 'tracknumber' : 'tracknumber', + 'composer' : 'composer', + 'albumart' : 'metadata_block_picture', + 'lyrics' : 'lyrics', + }, + '.flac': { + 'artist' : 'artist', + 'album' : 'album', + 'title' : 'title', + 'genre' : 'genre', + 'year' : 'date', + 'originaldate' : 'originaldate', + 'originalyear' : 'originalyear', + 'tracknumber' : 'tracknumber', + 'totaltracks' : 'totaltracks', + 'discnumber' : 'discnumber', + 'totaldiscs' : 'totaldiscs', + 'bpm' : 'bpm', + 'mood' : 'mood', + 'composer' : 'composer', + 'albumartist' : 'albumartist', + 'lyricist' : 'lyricist', + 'writer' : 'writer', + 'conductor' : 'conductor', + 'remixer' : 'remixer', + 'arranger' : 'arranger', + 'engineer' : 'engineer', + 'producer' : 'producer', + 'djmixer' : 'djmixer', + 'mixer' : 'mixer', + 'label' : 'label', + 'catalognumber' : 'catalognumber', + 'grouping' : 'grouping', + 'subtitle' : 'subtitle', + 'discsubtitle' : 'discsubtitle', + 'compilation' : 'compilation', + 'comment' : 'comment', + 'albumart' : 'metadata_block_picture', + 'lyrics' : 'lyrics', + 'albumsort' : 'albumsort', + 'titlesort' : 'titlesort', + 'artistsort' : 'artistsort', + 'albumartistsort' : 'albumartistsort', + 'composersort' : 'composersort', + 'work' : 'work', + 'releasestatus' : 'releasestatus', + 'releasetype' : 'releasetype', + 'releasecountry' : 'releasecountry', + 'script' : 'script', + 'language' : 'language', + 'copyright' : 'copyright', + 'license' : 'license', + 'barcode' : 'barcode', + 'isrc' : 'isrc', + 'asin' : 'asin', + 'musicbrainz_trackid' : 'musicbrainz_trackid', + 'musicbrainz_releasetrackid' : 'musicbrainz_releasetrackid', + 'musicbrainz_albumid' : 'musicbrainz_albumid', + 'musicbrainz_artistid' : 'musicbrainz_artistid', + 'musicbrainz_albumartistid' : 'musicbrainz_albumartistid', + 'musicbrainz_releasegroupid' : 'musicbrainz_releasegroupid', + 'musicbrainz_workid' : 'musicbrainz_workid', + 'musicbrainz_trmid' : 'musicbrainz_trmid', + 'musicbrainz_discid' : 'musicbrainz_discid', + 'acoustic_id' : 'acoustic_id', + 'acousticid_fingerprint' : 'acousticid_fingerprint', + 'musicip_puid' : 'musicp_puid', + }, + '.mpc': { + 'artist' : 'Artist', + 'album' : 'Album', + 'title' : 'Title', + 'genre' : 'Genre', + 'year' : 'Year', + 'tracknumber' : 'Track', + 'composer' : 'Composer', + }, + '.3gp': { + 'artist' : '\xa9ART', + 'album' : '\xa9alb', + 'title' : '\xa9nam', + 'genre' : '\xa9gen', + 'year' : '\xa9day', + 'tracknumber' : 'trkn', + 'composer' : '\xa9wrt', + 'lyrics' : '\xa9lyr', + 'albumartist' : 'aART', + 'disk' : 'disk', + 'comment' : '\xa9cmt', + 'albumart' : 'covr', + }, + '.wv': { + 'artist' : 'artist', + 'album' : 'album', + 'title' : 'title', + 'genre' : 'genre', + 'year' : 'year', + 'tracknumber' : 'track', + } + } diff --git a/musicman/utils/tagmap.pyc b/musicman/utils/tagmap.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ab9de98fbd5ddd4382201fbd586dc667b6639aa GIT binary patch literal 6795 zcmc&%iF+H>5g*4SA#p+o0YV5NhLn&HVmEe3s5y!(DXxMpB)N8=t-IRU$crWIvhOLz zP z&?9Qb1^ee5!dvK{dT=d(KLq|HAOH@)AzTGGNN^6|YJxR@YYEN;Tt~1TaEM?7;6{S; z05=hw54f4&0>CW<7Xn^Ha53Okf=d9m5nKwmo!~OS9R!yH?j#rnyn^6Lz+D7S0KAG| zH{jI-djR(mTm!g|;99`_1S5c>1WyD!KrjY4PH+(LI)X!hhXt-DcoN`*zzqa90^US$ zGvJd6ZUKxD(1GIw3BV-55x^;ervRo1jso6FkOs^UWC3#odBEEU3V=m|65uq!QvqiP zW&w{8+zxmL!P5YrPVfxCIf7>bIs|1vmjD4hg5!V{f_Xrn;7-5=f+}E*pbi)ign)Mu zM1YDw12zaw04@^T4Y)*b58%B7&jP%U;MsuBA$Ts}^9Y^~hkQ!c3nXJNl-#~ZlKNt~ zf|mf^Pns{4t9zMb?B#&3AmJ+kA0T*8Qv511w(h?+_G+?n6X0tkme&f;UMC^FUU>Ef z3HOacus4bKHw(euA_RM@#Q8R1*4u?y?+{A8Q@HdlAqdfg#aJArXNg(T4yZCit*i-A9Br9~IhsOfKu=LYq$rZ9WP3DcbO9z|RnT z7VvWfp9lN`!54*6UlK}vSt#`tq10D}QeP8FeO)N^4WZOGg-hQOE`3|L^c~^ScZEye z6E1yUxby?z(hr48KN2qeSh(~P;nGiqOFt7X{am>8h;Zo_l(7wfHY+x9He>cCC@Y3U z_UdeM?1k9;(G8v?6?=m=++ULEp~uv@=55V?x?HgtU&Z)fKjS!q&$CAEQ_ngr+s2X&^McOK7Tu zrVXL#qR@0nXnL>E^gf~KZ-u76qm0o3elL0dLr1UsqvZWhlJ`GL-v1(b|10hLAJO}7 zWO|K>r#BVvmyrH0A^igm-41~NgyNom(O}kQLYHFcNxhmLoNBpI6Hzf2)9acd6+~LZ zsnQs0YF}5qre=vXS=7?Z#nQaK7(@%Am`@(iYmCWA`^wrY=JI;|vaGc`pB&ReP4}G} z#`LDnh1RY_A)zwT69EAd=LpGzxlPzwX)Ma{#+S-&Z5*-p-u zSiiay`7Xt;On9Z)+1ZJi$k(*r9Bok^`BL-AalRKfsAI#`L8MIFrA$0M;n$Up8a1!3 z)rBMrz36xl)x4-}p?PHoHGMfjp2_omlU%>*Xg{c{^GT1WkeNVFxse~Ta0>}Z$t0*^397mM3WG9ef5q3^IJlP?~l z>dj4Mr!$kuf;Rkc9lD*MBKM$nkc1U84duI&k>l6z8IGAcA1-P~Hx%!QysGCYPq9R8 z@4ctA6ncDzc88{SwY#@|K@1PagGOCPOZq%gwOd^gwd$xCFQoD%t|Li>96TM%O5qXV z8&1U*cAOfA=}&GDQbAVcrJu|gu_>6U?|OA*b(7gR>w0xJKo84HVz^jLaw`#WMA{~K zN|}DaS2h_d#B+%x8!S7K*=%>(KI5y%RVE5nj}=o{UUw7~xG9oYJ{6yk5;ZqYeT?sR z`ba77zN6*yIq)E(*3DyY{)5-XINIugFq zCJ}=t(ODAZMf-WA0yj{XQ@H!-&jd}75ho0*-iTjn)IBwkK2}J@Q^k_n*;6H+%OB&;6luoKEd-_VGit(lzXGTX0b$*~iS`a60O*4P(?6#h1iW~lE^POkJ@?9pUx5fT?5Cz82t zo1f`aJee&fC22YcR#3H*e1-I)L@+tG^VE_+}7 zvR2#pU7>3+AMDU&qK?!K+Hq714~oQbTpr*xPdoJc)R`++19xFA^j+PcIm$ygs8l+B z$C_b!+Q*vx#N+RC9_}*R`BQY{c$M6k->AX>{w=}W;U^y1Ty#9Id8C^UO72q2qnSgg zQJ><6@k+>h*{w1^bw(CXWcLiv16$Hgg)<2zo$?P!XFAM@ literal 0 HcmV?d00001