^ home ^ programming

Create iTunes Playlists from Directories

I've always found it easier to organize my music hierarchally in directories with categories and subcategories than to use playlists. Unfortunately, iTunes and the iPod ignores the disk layout and only work with playlists

This is a small ruby script to bridge the gap. It traverses a directory structure and generates playlists for all directories and subdirectories. It then imports all these generated playlists into iTunes.

The script has not been cleaned up enough to be "click-and-go", you probably have to tweak it slightly to use it on your system. Note that:


# Generates m3u playlists from directory structure. A playlist is generated
# for each folder with the name of that folder.

VOLUME = "Macintosh HD"

require 'find'
require 'iconv'

def is_sound_file?(path)
    exts = %w(.mp3 .wav .aiff .m4a)
    ext = File.extname(path).downcase
    return exts.include?(ext)
end

def sound_files_in_dir(dir)
    files = []
    Find.find(dir) {|file| files << file if is_sound_file?(file)}
    return files.sort
end

def unix_path_to_mac_path(path)
    if path[0,1] == "/"
        path = File.join(VOLUME, path[1,path.size])
    else
        path = "/#{path}"
    end
    path = path.gsub("/", ":")
    return path
end

def utf_to_roman(path)
    # Replace separated characters with joined

    # ä
    path = path.gsub("a\314\210", "\303\244")
    path = path.gsub("o\314\210", "\303\266")
    path = path.gsub("u\314\210", "\303\274")
    path = path.gsub("A\314\210", "\303\204")
    path = path.gsub("O\314\210", "\303\226")
    path = path.gsub("U\314\210", "\303\234")

    # å
    path = path.gsub("a\314\212", "\303\245")
    path = path.gsub("A\314\212", "\303\205")

    # á
    path = path.gsub("a\314\201", "\303\241")
    path = path.gsub("e\314\201", "\303\251")
    path = path.gsub("i\314\201", "\303\255")

    # à
    path = path.gsub("a\314\200", "\303\240")
    path = path.gsub("c\314\247", "\303\247")

    begin 
        path = Iconv.conv("MacRoman", "UTF-8", path)
    rescue
        puts "ERROR -- CANNOT CONVERT PATH"
        puts path
        raise
    end
    return path
end

def generate_playlist_for_dir(dir)
    result = ""
    sound_files_in_dir(dir).each { |file|
        path = unix_path_to_mac_path(file)
        path = utf_to_roman(path)
        result << path << "\n"
    }
    return result
end

def playlist_name(dir, root)
    dir = dir.clone
    dir[root] = "" if dir[root]
    dir = dir[1,dir.size] if dir[0,1] == "/"
    dir[-1,1] = "" if dir[-1,1] == "/"
    dir = dir.gsub("/", " - ")
    return dir + ".m3u"
end

def add_playlist_to_itunes(playlist)
    path = utf_to_roman(unix_path_to_mac_path(File.expand_path(playlist)))
    name = utf_to_roman(File.basename(playlist, ".*"))
    out = <<ENDOF
tell application "iTunes"
    set pl to make new playlist
    set name of pl to "#{name}"
    add "#{path}" to pl
end tell
ENDOF
    `osascript -e '#{out}'`
end

# add #{unix_path_to_mac_path(playlist)} to play

def generate_playlists_for_root(root)
    Find.find(root) {|file|
        Find.prune if File.basename(file)[0,1] == "_"
        next unless File.directory?(file)
        next if file == root
        files = Dir.glob(File.join(file, "*"))
        next if files.size == 0 || (files.size == 1 && !is_sound_file?(files[0]))

        name = playlist_name(file, root)
        playlist = generate_playlist_for_dir(file)
        if playlist.size > 0
            puts name
            File.open(name, "w") {|f| f.write(playlist)}
            add_playlist_to_itunes(name)
        end
    }
end

def clear_playlists
    script = <<SCRIPT
tell application "iTunes"
    repeat while (count of playlists) >= 3
        set pl to item 3 of playlists
        delete playlist (name of pl)
    end repeat
end tell
SCRIPT
    `osascript -e '#{script}'`
end

clear_playlists
generate_playlists_for_root(ARGV[0])

 

programming ^ home ^


© 2012, Niklas Frykholm, administrator@frykholm.se