git.fiddlerwoaroof.com
sorter.py
3451cc25
 #!/usr/bin/env python
 
 # Copyright (c) 2015 Edward Langley
 # All rights reserved.
 # 
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
 # are met:
 # 
 # Redistributions of source code must retain the above copyright notice,
 # this list of conditions and the following disclaimer.
 # 
 # Redistributions in binary form must reproduce the above copyright
 # notice, this list of conditions and the following disclaimer in the
 # documentation and/or other materials provided with the distribution.
 # 
 # Neither the name of the project's author nor the names of its
 # contributors may be used to endorse or promote products derived from
 # this software without specific prior written permission.
 # 
 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 '''Sorts music into my preferred directory layout.
 Usage: find -name '*.mp3' -o '*.m4a' . . . | python sorter.py <destination_directory>
 
 Requires mutagen for tag handling (and, therefore, taglib)'''
 
1b985a35
 import os
84c56ae8
 import shutil
1b985a35
 import string
 import mutagen
 import argparse
 
 parser=argparse.ArgumentParser()
 parser.add_argument('destination')
 args = parser.parse_args()
 
 destination = args.destination
a46f1513
 print('copying music to, %s' % destination)
1b985a35
 
 import sys
 
 class PathFinder(object):
     def __init__(self):
         pass
 
a46f1513
     def construct_tag_map(self, data):
         tag_map = {}
         for (tag, _) in data.tags:
             tag_map[tag.lower()] = tag
         return tag_map
 
1b985a35
     def get_path(self, data, orig_name):
a46f1513
         tag_map = self.construct_tag_map(data)
         print(tag_map, tag_map['title'], data.get(tag_map['title']))
1b985a35
         orig_name = orig_name.rpartition('.')[0]
a46f1513
         title=self.clean(data.get(tag_map['title'], ['Unknown Title'])[0])
1b985a35
         if title.strip() == '': title = orig_name
a46f1513
         album=self.clean(data.get(tag_map['album'], ['Unknown Album'])[0])
1b985a35
         if album.strip() == '': album = 'Unknown Album'
 
a46f1513
         if 'albumartist' in data: artist = data[tag_map['albumartist']][0]
         elif 'performer' in data: artist = data[tag_map['performer']][0]
         elif 'conductor' in data: artist = data[tag_map['conductor']][0]
         else: artist = data.get(tag_map['artist'], ['Unknown Artist'])[0]
1b985a35
         artist = self.clean(artist)
         if artist.strip() == '': artist = 'Unknown Artist'
 
a46f1513
         tracknumber=self.clean(data.get(tag_map['tracknumber'], ['--'])[0])
1b985a35
         if tracknumber != '--':
            try:
                tracknumber = tracknumber.partition('_')[0]
                tracknumber = '%02d' % int(tracknumber)
            except ValueError:
                pass
a46f1513
         discnumber=self.clean(data.get(tag_map['discnumber'], ['--'])[0].partition('/')[0])
         print(f'discnumber is: {discnumber}')
         if discnumber.strip() not in {'', '--'}: discnumber = '%02d' % int(discnumber)
1b985a35
         result =[ os.path.join(artist[0].upper(),artist,album), '%s_%s' % (tracknumber, title) ]
a46f1513
         if discnumber.strip() not in {'', '--'}:
1b985a35
             result[1] = '%s_%s' % (discnumber, result[1])
         return result
 
     validchars = frozenset('_.- %s%s' % (string.ascii_letters, string.digits))
     def clean(self, strin):
         strin = ''.join((c if c in self.validchars else " ") for c in strin)
         strin = '_'.join(strin.split())
         if len(strin) > 40:
             pa = strin[:28]
             pb = strin[-31:]
             strin = '...'.join([pa,pb])
         return strin
 
 
 
 
 
 pf = PathFinder()
 for line in sys.stdin:
     line = line.strip()
     try:
         file = mutagen.File(line, easy=True)
         x = 0
         suf = ''
         path, name = pf.get_path(file, os.path.basename(line))
         ext = line.rpartition('.')[-1]
 
         path = os.path.join(destination, path)
 
a46f1513
         checkname = '%s.%s' % ('__'.join([_f for _f in [name, suf] if _f]), ext)
1b985a35
 
a46f1513
         while os.path.exists(os.path.join(path, '%s.%s' % ('__'.join([_f for _f in [name, suf] if _f]), ext))):
1b985a35
             x += 1
             suf = '%02d' % x
 
a46f1513
         name = '%s.%s' % ('__'.join([_f for _f in [name, suf] if _f]), ext)
1b985a35
         try:
             os.makedirs(path)
a46f1513
         except OSError as e:
1b985a35
             if e.errno == 17:
                 pass
             else:
                 raise
 
a46f1513
         print(os.path.join(path, name))
84c56ae8
         shutil.copy(line, os.path.join(path, name))
a46f1513
     except Exception as e:
         print('Error Processing: %s' % line,e, file=sys.stderr)
         print('Error Processing: %s' % line,e)