utility to sync an itunes playlist to a directory (like a mounted sd card/phone)

add

jcs 1a3a0929

+119
+119
itunes-rsync.rb
··· 1 + #!/usr/bin/ruby 2 + # $Id: itunes-rsync.rb,v 1.1 2009/01/27 08:49:12 jcs Exp $ 3 + # 4 + # rsync an itunes playlist with another directory, most likely a usb music 5 + # device 6 + # 7 + # requires the rubyosa gem ("sudo gem install rubyosa") 8 + # 9 + # Copyright (c) 2009 joshua stein <jcs@jcs.org> 10 + # 11 + # Redistribution and use in source and binary forms, with or without 12 + # modification, are permitted provided that the following conditions 13 + # are met: 14 + # 15 + # 1. Redistributions of source code must retain the above copyright 16 + # notice, this list of conditions and the following disclaimer. 17 + # 2. Redistributions in binary form must reproduce the above copyright 18 + # notice, this list of conditions and the following disclaimer in the 19 + # documentation and/or other materials provided with the distribution. 20 + # 3. The name of the author may not be used to endorse or promote products 21 + # derived from this software without specific prior written permission. 22 + # 23 + # THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR 24 + # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 + # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 + # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 + # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 + # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 + # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 + # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 + # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 + # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 + # 34 + 35 + require "rubygems" 36 + require "rbosa" 37 + 38 + if !ARGV[1] 39 + puts "usage: #{$0} <itunes playlist> <destination directory>" 40 + exit 41 + end 42 + 43 + playlist = ARGV[0] 44 + 45 + if Dir[ARGV[1]].any? 46 + destdir = ARGV[1] 47 + 48 + if !destdir.match(/\/$/) 49 + destdir += "/" 50 + end 51 + else 52 + puts "error: directory \"#{destdir}\" does not exist, exiting" 53 + exit 54 + end 55 + 56 + print "querying itunes for playlist \"#{playlist}\"... " 57 + 58 + # disable a stupid xml deprecation warning 59 + $VERBOSE = nil 60 + itunes = OSA.app("iTunes") 61 + 62 + itpl = itunes.sources.select{|s| s.name == "Library" }.first. 63 + user_playlists.select{|p| p.name.downcase == playlist.downcase }.first 64 + 65 + if !itpl 66 + puts "could not locate, exiting" 67 + exit 68 + end 69 + 70 + tracks = itpl.file_tracks.map{|t| t.location } 71 + 72 + puts "found #{tracks.length} track#{tracks.length == 1 ? '' : 's'}." 73 + 74 + # figure out where all of them are stored by checking for the greatest common 75 + # directory of every track 76 + gcd = "" 77 + (1 .. tracks.map{|t| t.length }.max).each do |s| 78 + piece = tracks[0][0 .. s - 1] 79 + 80 + ok = true 81 + tracks.each do |t| 82 + if t[0 .. s - 1] != piece 83 + ok = false 84 + end 85 + end 86 + 87 + if ok 88 + gcd = piece 89 + else 90 + break 91 + end 92 + end 93 + 94 + # setup work dir 95 + td = `mktemp -d /tmp/itunesrsync.XXXXX`.strip 96 + 97 + # mirror directory structure and create symlinks 98 + print "linking files under #{td}/... " 99 + 100 + tracks.each do |t| 101 + shortpath = t[gcd.length .. t.length - 1] 102 + tmppath = "#{td}/#{shortpath}" 103 + 104 + if !Dir[File.dirname(tmppath)].any? 105 + system("mkdir", "-p", File.dirname(tmppath)) 106 + end 107 + 108 + system("ln", "-s", t, tmppath) 109 + end 110 + 111 + puts "done." 112 + 113 + # times don't ever seem to match up, so only check size 114 + puts "rsyncing to #{destdir}... " 115 + system("rsync", "-Lrv", "--size-only", "--delete", "#{td}/", destdir) 116 + 117 + print "cleaning up... " 118 + system("rm", "-rf", td) 119 + puts "done."