utility to sync an itunes playlist to a directory (like a mounted sd card/phone)
1#!/usr/bin/ruby
2# Copyright (c) 2009, 2012, 2013 joshua stein <jcs@jcs.org>
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions
6# are met:
7#
8# 1. Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# 2. Redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution.
13# 3. The name of the author may not be used to endorse or promote products
14# derived from this software without specific prior written permission.
15#
16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
17# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27
28require File.dirname(__FILE__) << "/lib/itunes"
29
30if !ARGV[1]
31 puts "usage: #{$0} <itunes playlist> <destination directory>"
32 exit
33end
34
35playlist = ARGV[0]
36
37if Dir[ARGV[1]].any?
38 destdir = ARGV[1]
39
40 if !destdir.match(/\/$/)
41 destdir += "/"
42 end
43else
44 puts "error: directory \"#{destdir}\" does not exist, exiting"
45 exit 1
46end
47
48puts "querying itunes for playlist \"#{playlist}\"..."
49
50it = ITunes.new
51pl = it.playlist(playlist)
52
53mbytes = pl.total_bytes.to_f / (1024 * 1024)
54
55puts "found #{pl.tracks.length} track#{pl.tracks.length == 1 ? "" : "s"} " <<
56 "with size " << sprintf("%0.2fMb", mbytes)
57
58# make sure the destination can hold this playlist
59df_m = `df -m #{destdir}`.split("\n").last.split(" ")[1].to_i
60if mbytes > df_m
61 puts "error: #{destdir} has size of #{df_m}Mb, need #{mbytes.ceil}Mb to sync"
62 exit 1
63end
64
65td = `mktemp -d /tmp/itunes-rsync.XXXXX`.strip
66
67# link each track into the workspace
68print "linking files under #{td}/... "
69pl.tracks.each do |t|
70 tmppath = td + "/" + t.safe_filename_without_gcd
71
72 if !Dir[File.dirname(tmppath)].any?
73 system("mkdir", "-p", File.dirname(tmppath))
74 end
75
76 File.symlink(t.location, tmppath)
77end
78
79puts "done."
80
81# file times don't ever seem to match up, so only check size
82puts "rsyncing to #{destdir}... "
83system("rsync", "-Lrv", "--size-only", "--progress", "--delete", "#{td}/",
84 destdir)
85
86print "cleaning up... "
87system("rm", "-rf", td)
88
89puts "done."