utility to sync an itunes playlist to a directory (like a mounted sd card/phone)
at master 122 lines 3.5 kB view raw
1#!/usr/bin/env 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 "rubygems" 29require "appscript" 30$: << File.dirname(__FILE__) 31require "appscript_itunes_fix" 32 33class ITunes 34 def initialize 35 @as = Appscript.app.by_name("iTunes", ITunesFix) 36 @_playlists = {} 37 end 38 39 def playlist(name) 40 @_playlists[name] ||= Playlist.new(self, name) 41 end 42 43 def as_playlist(name) 44 @as.playlists[name] 45 end 46end 47 48class Playlist 49 def initialize(itunes, name) 50 @itunes = itunes 51 @as_playlist = @itunes.as_playlist(name) 52 end 53 54 def tracks 55 @tracks ||= @as_playlist.file_tracks.get.map{|t| Track.new(self, t) } 56 end 57 58 def total_bytes 59 tracks.map{|t| File.size(t.location) }.inject(:+) 60 end 61 62 # figure out where all tracks are stored by checking for the greatest common 63 # directory of every track 64 @_gcd = nil 65 def gcd 66 return @_gcd if @_gcd 67 68 @_gcd = "" 69 70 locs = self.tracks.map{|t| t.location }.sort_by{|p| p.length } 71 (0 .. locs[0].length).each do |pos| 72 try = locs[0][0 ... pos] 73 74 ok = true 75 locs.each do |loc| 76 if loc[0 ... try.length] != try 77 ok = false 78 break 79 end 80 end 81 82 if ok 83 @_gcd = try 84 else 85 break 86 end 87 end 88 89 @_gcd 90 end 91end 92 93class Track 94 attr_accessor :album_artist, :artist, :album, :compilation, :disc_number, 95 :location, :name, :track_number 96 97 def initialize(playlist, track) 98 @playlist = playlist 99 100 @album_artist = track.album_artist.get.to_s 101 @artist = track.artist.get.to_s 102 @album = track.album.get.to_s 103 @compilation = !!track.compilation.get 104 @disc_number = track.disc_number.get.to_i 105 @location = track.location.get.path.to_s 106 @name = track.name.get.to_s 107 @track_number = track.track_number.get.to_i 108 end 109 110 def compilation? 111 @compilation 112 end 113 114 def album_artist_or_artist 115 @album_artist.strip == "" ? @artist : @album_artist 116 end 117 118 # restrict pathname to avoid indexing problems on some players/filesystems 119 def safe_filename_without_gcd 120 @location[@playlist.gcd.length .. -1].gsub(/[^A-Za-z0-9\.\/,' -]/, "_") 121 end 122end