Why not just set the iPhone to automatically update? Because I want the finite control that manually managing affords me. (UPDATE: to all you haters out there wondering why I don't just use auto-sync...because it "locks" the iPod so you can't manually manage any playlists. I want to manually manage most things and just have selected playlists update automatically. If you don't like this script don't use it!)
So I created a script that updates a specified list of playlists to all attached iPods/iPhones. It can run on a schedule (with cron), so I never have to worry that my iPhone won't have the most recent versions of my playlists!
Features
- Adds specified playlists to all iPods attached to your mac
- Will skip the playlist if there's not enough space on an attached device
- Retains the order in which the tracks appear on the library playlist
- Outputs useful debug info
First, the script requires the iPod/iPhone be set to "Manually manage music and videos". I named the script "updateipod.pl" and placed in my user's "bin" directory. It's recommended you specify the playlists that you want to sync in the @playlists array at the top of the script:
my @playlists = ( "jamz", "0pods", "punk best", "stoner best" );After chmod-ing the script to 755 just invoke it as you would any script:
/Users/user/bin/updateipod.plOptionally, you can specify the names of the playlists as arguments on the command line. Useful for one-off updating. Note that if playlists are passed as command-line arguments they will be processed instead of whatever is specified in the @playlists array at the top of the script. Make sure to enclose playlist names in quotes if the playlists have spaces or special characters. For example, here's how I'd send three playlists to my connected devices on the command line:
/User/user/bin/updateipod.pl "michael best" 0pods "80's by rory"I schedule it to run hourly with cron:
0 * * * * /Users/user/bin/updateipod.pl > /dev/null 2>&1But you can do this if you want more output:
0 * * * * /Users/user/bin/updateipod.pl >> /Users/user/Desktop/ipod_update_log.txt 2>&1That's it! Here's the script. Click "expand source" below to view:
#!/usr/bin/perl
# 2009-07-05
# Auto-sync specified playlists to all attached iPods/iPhones
# Specify playlist names in the @playlist array OR enter them
# as arguments (in single quotes) on the command line like this:
#
# $ ./updateipod.pl "michael best" "0pods" "80's by rory"
#
# I run this hourly with the cronjob:
#
# 0 * * * * /Users/user/bin/updateipod.pl > /dev/null 2>&1
#
# use something like this if you want to see the output for each run:
#
# 0 * * * * /Users/user/bin/updateipod.pl >> /Users/user/Desktop/ipod_update_log.txt 2>&1
#
# some applescript in this script was borrowed from "Selected Playlists To iPod v2.1" at the very excellent http://www.dougscripts.com/
use strict;
use POSIX qw(strftime);
my @playlists = (
"jamz",
"0pods",
"punk best",
"stoner best"
);
# if there are playlists entered as arguments on the cmd line
# those are gonna trump whatever is specified above
if ($ARGV[0]) {
@playlists = @ARGV;
}
my $time = strftime( "%Y-%m-%d %H:%M:%S",localtime(time));
print "syncing of playlists started at $time\n";
# check if itunes is running, start if not
checkIfRunning();
# get the sources of any attached ipods. exit if there are none attached.
my ($id_ref,$name_ref) = getiPodSources();
my @ipod_ids = @$id_ref;
my @ipod_names = @$name_ref;
my $devices = @ipod_ids;
# exit if we have no attached ipods
if (!$ipod_ids[0]) {
print "no manually managed iPods detected, exiting...\n";
exit;
}
# make sure the playlists exist in the itunes lib.
my @confirmed_playlists = ();
foreach my $playlist (@playlists) {
my $exists = checkPlaylistExists($playlist);
if($exists) {
push(@confirmed_playlists,$playlist);
} else {
print "\"$playlist\" is not a valid playlist in the iTunes library, skipping...\n";
}
}
# make sure we have at least one valid playlist
if (!$confirmed_playlists[0]) {
print "no valid playlists entered, exiting...\n";
exit;
}
# loop thru our ipods first, then within that,
# loop thru our playlists array and sync them to each ipod
for (my $i=0;$i<$devices;$i++) {
foreach my $playlist (@confirmed_playlists) {
print "telling itunes to sync playlist \"$playlist\" to $ipod_names[$i]\n";
my $output = syncPlaylist($ipod_ids[$i],$ipod_names[$i],$playlist);
if ($output ne '1') {
print "${output}, skipping...\n";
next;
}
} # end loop thru playlists
} # end for thru ipods
print "done.\n\n";
exit;
#################################################################################################
################### SUBROUTINES #####################
#################################################################################################
sub stripNonAlph {
# strips non-alphanumeric characters from strings
my($text) = shift;
$text =~ s/([^0-9a-zA-z ])//g;
return ($text);
} # end sub strip non alph
sub checkIfRunning {
# checks if iTunes is running
# starts it if not
my $start=`/usr/bin/osascript <<END
--start the program if necessary
set isRunning to 0
tell application "System Events"
set isRunning to ((application processes whose (name is equal to "iTunes")) count)
end tell
if isRunning is 0 then
tell application "iTunes"
activate
end tell
end if
`;
} # end sub check if running
sub getiPodSources {
# gets the system source IDs of any attached ipods
# (assumes there's more than 1 in case there actually are)
# returns in semicolon-delimited list
my $source=`/usr/bin/osascript <<END
tell application "iTunes"
set validiPods to {}
set myPods to ""
set myPodsNames to ""
set myPodReturn to ""
set ipodSources to (get every source whose kind is iPod)
if ipodSources is {} then
return 0
end if
repeat with s in ipodSources
try -- can't make a playlist on an iPod set to sync automatically, so this will fail.
set tp to make new user playlist at s with properties {name:("p)" & my get_temp_name() & "-^")}
delete tp
set end of validiPods to s
end try
end repeat
if validiPods is {} then
return 0
end if
repeat with i from 1 to (length of validiPods)
set myPods to myPods & ";" & id of item i of ipodSources
set myPodsNames to myPodsNames & ";" & name of item i of ipodSources
end repeat
set myPodsReturn to myPods & "|" & myPodsNames
return myPodsReturn
end tell
to get_temp_name()
return (get time of (get current date)) as text
end get_temp_name
END`;
chomp($source);
$source =~ /^;(.*?)\|;(.*?)$/;
my ($id,$name) = ($1,$2);
my @ids = split(/;/,$id);
my @names = split(/;/,$name);
return (\@ids,\@names);
} # end sub getiPodSources
sub checkPlaylistExists {
# checks if the itunes playlist exists
my $playlist = shift;
my $exists=`/usr/bin/osascript <<END
tell application "iTunes"
if (exists playlist "$playlist") then
return 1
else
return 0
end if
end tell
END`;
$exists = &stripNonAlph($exists);
return ($exists);
} # end check if playlist exist
sub syncPlaylist {
# takes the id of the source (ipod) and the name of the playlist to sync as arguments
my ($ipod_id,$ipod_name,$playlist) = @_;
my $sync = `/usr/bin/osascript <<END
tell application "iTunes"
set playlistName to "$playlist"
set main_lib to library playlist 1
set ipod_src to item 1 of (get every source whose id is $ipod_id)
set ipod_lib to library playlist 1 of ipod_src
set playlistSize to 0
set iPodFreeSpace to free space of ipod_src
set copySuccess to 0
set playlistSuccess to 0
set myPlaylists to (get every user playlist)
repeat with thisPlaylist in myPlaylists
if name of thisPlaylist is playlistName then
--loop thru the playlist to determine if there is enough
--free space on the ipod for any songs not already on it
set eop to index of last track of thisPlaylist
repeat with i from 1 to eop
--determine if the song is already on ipod lib
set i_track to track i of thisPlaylist
if (get class of i_track) is file track then
tell i_track to set {nom, art, alb, siz, kin, tim, pid} to {get name, get artist, get album, get size, get kind, get time, get persistent ID}
try
-- is track already in iPod library? setting this will fail if not
set new_i_track to (some track of ipod_lib whose name is nom and artist is art and album is alb and size is siz and kind is kin and time is tim)
on error -- if not, add size of track to running total of tracks not on ipod
set playlistSize to playlistSize + siz
end try
end if
end repeat --thru selected playlist
if free space of ipod_src is greater than playlistSize then
-- establish iPod playlist
try
--does the iPod playlist already exist? if so we delete it.
--why? because if we add additional tracks to the end of it, the track list will no be in the
--same order as they are on our iTunes playlist
set new_ipodplaylist to user playlist playlistName of ipod_src
delete new_ipodplaylist
set new_ipodplaylist to (make new user playlist at ipod_src with properties {name:playlistName})
on error
-- if not, create the new iPod playlist
set new_ipodplaylist to (make new user playlist at ipod_src with properties {name:playlistName})
end try
--I'm not checking to see if the tracks already exist on the ipod before copying
--because it *appears* itunes won't duplicate them. maybe this is a new feature
try
duplicate (every track of thisPlaylist) to ipod_lib
set copySuccess to 1
on error
return "error copying tracks to iPod $ipod_name"
set copySuccess to 0
end try
try
duplicate (every track of thisPlaylist) to new_ipodplaylist
set playlistSuccess to 1
on error
return "error duplicating track listing to playlist $playlist on iPod $ipod_name"
set playlistSuccess to 0
end try
if copySuccess is 1 and playlistSuccess is 1
return 1
end if
else
return "not enough free space on iPod $ipod_name to copy all tracks on $playlist"
end if
end if --current playlist = selected playlist
end repeat
end tell
`;
$sync = &stripNonAlph($sync);
return ($sync);
} # end sub syncPlaylist
__END__
Some of the applescript was borrowed from this script at the very awesome Doug's Applescripts for iTunes.


No comments:
Post a Comment