git.fiddlerwoaroof.com
Raw Blame History
#!/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)'''

import os
import shutil
import string
import mutagen
import argparse

parser=argparse.ArgumentParser()
parser.add_argument('destination')
args = parser.parse_args()

destination = args.destination
print 'copying music to, %s' % destination

import sys

class PathFinder(object):
    def __init__(self):
        pass

    def get_path(self, data, orig_name):
        orig_name = orig_name.rpartition('.')[0]
        title=self.clean(data.get('title', ['Unknown Title'])[0].encode('utf-8'))
        if title.strip() == '': title = orig_name
        album=self.clean(data.get('album', ['Unknown Album'])[0].encode('utf-8'))
        if album.strip() == '': album = 'Unknown Album'

        if 'performer' in data: artist = data['performer'][0].encode('utf-8')
        elif 'conductor' in data: artist = data['conductor'][0].encode('utf-8')
        else: artist = data.get('artist', ['Unknown Artist'])[0].encode('utf-8')
        artist = self.clean(artist)
        if artist.strip() == '': artist = 'Unknown Artist'

        tracknumber=self.clean(data.get('tracknumber', ['--'])[0].encode('utf-8'))
        if tracknumber != '--':
           try:
               tracknumber = tracknumber.partition('_')[0]
               tracknumber = '%02d' % int(tracknumber)
           except ValueError:
               pass
        discnumber=self.clean(data.get('discnumber', ['--'])[0].partition('/')[0].encode('utf-8'))
        if discnumber != '--': discnumber = '%02d' % int(discnumber)
        result =[ os.path.join(artist[0].upper(),artist,album), '%s_%s' % (tracknumber, title) ]
        if discnumber != '--':
            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)

        checkname = '%s.%s' % ('__'.join(filter(None, [name, suf])), ext)

        while os.path.exists(os.path.join(path, '%s.%s' % ('__'.join(filter(None, [name, suf])), ext))):
            x += 1
            suf = '%02d' % x

        name = '%s.%s' % ('__'.join(filter(None, [name, suf])), ext)
        try:
            os.makedirs(path)
        except OSError, e:
            if e.errno == 17:
                pass
            else:
                raise

        print os.path.join(path, name)
        shutil.copy(line, os.path.join(path, name))
    except Exception, e:
        print >> sys.stderr, 'Error Processing: %s' % line,e
        print 'Error Processing: %s' % line,e