#!/usr/bin/env python
#
#            music-album-renamer: Renames music files consistently.
#                     by Eron Hennessey (eron@abstrys.com)
#
# The plan:
# ---------
#
# Search for files in the following formats:
#
# * mp3, ogg, flac, wav, aac, alac
#
# Rename the files so that they follow this pattern:
#
# artist/album/nn-name_of_track.ext
#
# Notes:
# ------
#
# * Filenames are converted to lowercase.
# * Spaces are converted to underscores, *except* between an initial number and
#   title in a track name, in which case, they are converted to a dash
#   character.
# * Directory names are converted to lowercase. Spaces within them are
#   converted to underscores.

import os
import sys
import re
from abstrys.txt_utils import snakeify


TRACK_FILE_TYPES = ['.aac', '.alac', '.flac', '.m4a', '.m4b', '.mp3', '.ogg',
    '.wav', '.wma']


def rename_audio_file(file_name):
    """Process the passed-in file_name as an audio file; return the fixed audio
    file_name."""
    # sanity: there are no filesep characters in the name.
    if os.sep in file_name:
        raise BadFileNameException

    # convert spaces to underscores.
    new_file_name = snakeify(file_name)

    # if the name begins with a number followed by an underscore, convert the
    # underscore to a dash.
    if '_' in new_file_name:
        (first_word, rest) = new_file_name.split('_', 1)
        try:
            float(first_word)
        except:
            return new_file_name

        new_file_name = '-'.join([first_word, rest])

    return new_file_name


def process_file(path):
    """Rename a file"""
    # cut the filename from the path.
    (dpath, file_name) = os.path.split(path)
    if dpath == '':
        dpath = os.getcwd()

    new_file_name = rename_audio_file(file_name)

    # if the renamed file has the same name as the original, return success
    # without doing anything.
    if new_file_name == file_name:
        return True

    if os.path.exists(new_file_name):
        # if the new_file_name already exists, it could be the result of:
        # * a case-insensitive file system
        # * an actual naming conflict

        # first, check to see if it's the same file.
        if os.path.samefile(file_name, new_file_name):
            # do two renames: one to a temp file, then the second to the
            # desired filename.
            tmp_file_name = new_file_name[::-1]
            os.rename(file_name, tmp_file_name)
            os.rename(tmp_file_name, new_file_name)
        else:
            # it's not the same file... hmm.
            print "A file already exists with the name %s" % '/'.join([dpath,
                new_file_name])
    else:
        os.rename(file_name, new_file_name)
        print 'renamed file: %s to %s' % (file_name, new_file_name)


def process_dir(dir_name):
    """Iterate through a directory, processing files and subdirectories as
    appropriate."""
    os.chdir(dir_name)
    names = os.listdir('.')
    for fname in names:
        if os.path.isdir(fname):
            process_dir(fname)
        else:
            (name, ext) = os.path.splitext(fname)
            if ext.lower() in TRACK_FILE_TYPES:
                process_file(fname)
            # else: do nothing...
    os.chdir('..')
    new_dir_name = snakeify(dir_name)
    if new_dir_name != dir_name:
        # if the dir already exists, it doesn't matter, But if it *doesn't
        # exist*, # then create it.
        if not os.path.exists(new_dir_name):
            os.rename(dir_name, new_dir_name)
            print 'renamed dir: %s to %s' % (dir_name, new_dir_name)


if __name__ == '__main__':
    if len(sys.argv) > 1:
        for arg in sys.argv[1:]:
            process_dir(arg)
    else:
        process_dir('.')

