Export iTunes playlist songs to an external location (SD Card)

The majority of the time I use my iPhone to play music in my car via USB or Bluetooth. Recently I decided to utilize the SD card slot as well and fill one with MP3s. I soon realized iTunes does not have an easy way of exporting music to an SD card, so I wrote a simple Ruby script to do so.

I created a smart playlist based on my genres and ratings called “Jeep”. I then exported this playlist as plain text via:

File > Library > Export Playlist...
  - Save as: Jeep.txt
  - Format: Plain Text

I created a Yaml configuration file (config.yml):

---
playlist_path: ./Jeep.txt
export_path: /Volumes/ERLJEPMUSIC
output_parts:
- Artist
- Album
- Track Number
- Name
output_delimiter: " - "

I created the following Ruby script to load the config.yml file, parse the exported playlist as plain text, create an export path for each file, and then copy the files to the destination.

#!/usr/bin/env ruby

require 'set'
require 'yaml'

class ItunesFileExporter
  def initialize
    @songs = Set.new
    @headers = nil
    load_config
    playlist_to_hash
  end

  def run
    copy_files
  end

  private

  def load_config(config_path = './config.yml')
    @config = YAML.load_file(config_path)
  end

  def playlist_to_hash
    File.readlines(@config['playlist_path'], "\r").each do |line|
      cols = line.strip.split("\t")
      if @headers.nil?
        @headers = cols
      else
        @songs << song_data(cols)
      end
    end
  end

  def song_data(cols)
    hsh = Hash[@headers.zip(cols)]
    file_extension = hsh['Location'].split(/\./).last
    hsh['Location'] = hsh['Location'].tr(':', '/').gsub(/^Macintosh HD/, '')
    file_parts_joined = @config['output_parts']
                        .each_with_object([]) { |part, arr| arr << hsh[part] }
                        .join(@config['output_delimiter'])
                        .tr('/', '_')
    hsh['_output_path'] = "#{@config['export_path']}/#{file_parts_joined}.#{file_extension}"
    hsh
  end

  def copy_files
    @songs.each do |song|
      puts "Copying: #{song['_output_path']}"
      if File.exist?(song['_output_path'])
        puts 'File already exists.'
        next
      end
      FileUtils.cp(song['Location'], song['_output_path'])
    end
  end
end

ItunesFileExporter.new.run

Source code on Github

Updated: