#!/usr/bin/perl # Make N704iμ music data from iTunes playlist # # ver. 0.1 2007/10/9 Shinji Kono kono@ie.u-ryukyu.ac.jp # # Everyone is permitted to do anything on this program # including copying, modifying, improving, # as long as you don't try to pretend that you wrote it. # i.e., the above copyright notice has to appear in all copies. # Binary distribution requires original version messages. # You don't have to ask before copying, redistribution or publishing. # THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. =head1 NMAE n704iu.pl -- Make N704iu music data from iTunes playlist =head1 SYNOPSIS n704iu.pl [-a] [-t destination] [-m max-size-megabyte] playlist-name =head1 DESCRIPTION This makes directories PRIVATE/DOCOMO/TABLE/SD_VIDEO/ SD_VIDEO/PRL001 copies AAC music data from iTunes playlist-name and creates necessary playlist file. Then you can copy the directory to the micro-SD of N704iu. i-motion based mobile phones should work in a same way. cp -r MUSIC/* /Volume/NO\ NAME You may need check micro SD in tools menu on N704iu. Only AAC files are copyied. MP3 needs conversion to AAC. Music bought in iTMS cannot played in N704iu. -a option makes play lists for each albums. Default makes play lists for each artists. NKF module and MP4::Info is required. =head1 AUTHORS Shinji KONO =cut use strict; use MP4::Info; use Getopt::Std; $MP4::Info::use_mp4_utf8 = 1; my $dest = "MUSIC"; my $opts = {}; getopts('dm:st:',$opts); my $debug = $opts->{d}; my $playlist_album = $opts->{a}; my $max = 250*430; $max = $opts->{m}*250; # max size in megabyte ($max is 4k blocks) my $shuffle = $opts->{s}; my $dest_data; my $dest_list; if (! $debug) { if ( -d $opts->{t}) { $dest = $opts->{t}; } $dest_data = "$dest/SD_VIDEO"; system("mkdir -p $dest_data"); if ( ! -d $dest ) { die("can't create $dest\n"); } $dest_list = "$dest/PRIVATE/DOCOMO/TABLE/SD_VIDEO"; system("rm -rf $dest_data/*"); system("mkdir -p $dest_list"); system("rm -rf $dest_list/*"); } binmode(STDOUT, ":utf8"); my $data_id; my $playlist_id; my $num =0 ; my $song = 0 ; my %album; my $total_size; # if ($shuffle) { # while(@ARGV) { # push(@dirs,splice(@ARGV,rand(@ARGV),1)); # } # } else { # @dirs = @ARGV; # } # for my $dir (@dirs) { # if (-d $dir) { # &directory($dir); # last if ($max && $total_size>$max); # } # } # # get iTunes music file list using AppleScript # print "Get files in $ARGV[0] play list\n"; my $arg = ' -e "tell application \"iTunes\""'. ' -e " set aPlaylist to some playlist whose name is \"'.$ARGV[0].'\""'. ' -e " set aList to tracks of aPlaylist"'. ' -e " -- set pathList to {}"'. ' -e " set outputString to \"\""'. ' -e " repeat with anItem in aList"'. ' -e " set aPath to POSIX path of (location of anItem as string)"'. ' -e " -- set pathList to pathList & aPath"'. ' -e " -- set outputString to outputString & quoted form of aPath & \"\\n\""'. ' -e " set outputString to outputString & aPath & \"\\n\""'. ' -e " end repeat"'. ' -e " outputString"'. ' -e "end tell"'; # print "$arg\n"; open(OSA,"osascript $arg |"); binmode(OSA, ":utf8"); # print while(); my @files = ; # print "@files\n"; my $albums = &make_albums(@files); ©($albums); # all done sub copy_file { my ($from,$to) = @_; my ($buf); open(IN,'<',$from) || return; open(OUT,'>',$to) || return; while(sysread(IN,$buf,4096)>0) { syswrite(OUT,$buf); } close(OUT); } # # AppleScript has odd UTF encode for ~ mark # sub fix_tilde { my ($input) = @_; $input =~ s/\x{301C}/\x{FF5E}/g; $input; } sub filename { my ($original) = @_; chop($original); print "processing $original\n"; $original = fix_tilde($original); } # # copy AAC file from iTunes # sub copy { my ($albums) = @_; my $dir = 1; while( my ($playlist_name, $album) =each %{$albums->hash()} ) { my $songs = $#{$album->{list}}+1; my $dirname = "PRL".sprintf("%03X",$dir); my $dest_dir = "$dest_data/$dirname"; system("mkdir -p $dest_dir"); $album->{dest_dir} = $dest_dir; for my $song (@{$album->list()}) { my $name = "MOL".sprintf("%03X",$songs).".MP4"; $song->{dest_file_name} = $name; ©_file($song->filename(),"$dest_dir/$name"); $songs--; } $album->make_playlist("$dest_list/$dirname.TBL"); $dir++; } $albums; } # # scan file name # make song object # make album and album list # sub make_albums { my @files = @_; my $numsong = 0; my $albums = AlbumList->new(); for my $original (@files) { $original = &filename($original); next if ($original !~ /\.m4a$/); my $size = -s $original; $total_size += (($size+4095)/4096); if ($max && $total_size>$max) { print "$original ingnored due to the size\n"; next; } $numsong++; $num++; my $song = Song->new($original); my $album_title = $song->mp4(); if ($playlist_album) { $albums->add($album_title,$song); } else { $albums->add($song->artist,$song); } } return $albums; } sub num { $num; } package Song; sub new { my ($class,$filename) = @_; my $self = {filename=>$filename}; bless $self; } sub filename { my ($self) = @_; $self->{filename}; } sub dest_file_name { my ($self) = @_; $self->{dest_file_name}; } sub mp4 { my ($self) = @_; my $mp4 = new MP4::Info $self->{filename}; my $track; my $disk; my $album; $self->{track} = defined($mp4->TRKN)?${$mp4->TRKN}[0]:0; $self->{disk} = defined($mp4->DISK)?${$mp4->DISK}[0]:0; $self->{album} = $mp4->album; $self->{mp4} = $mp4; $self->{artist} = $mp4->artist; if (! $self->{track} ) { $self->{album} = "NO_NAME"; } if (! $self->{artist}) { $self->{artisit} = "NO_NAME"; } if ($playlist_album) { $self->{line} = $mp4->title."-".$mp4->artist; } else { $self->{line} = $mp4->title."-".$mp4->album; } $self->{album}; } sub title { my ($self) = @_; $self->{line} ; } sub artist { my ($self) = @_; $self->{artist} ; } package Album; use NKF; sub new { my ($class,$title) = @_; my $self = {count=>0,list=>[],title=>$title}; bless $self; } sub make_name { my ($self,$name) = @_; $name =~ s/.*SD_VIDEO\///; $name =~ s/\.TBL$//; "\\SD_VIDEO\\$name"; } sub make_song_filename { my ($self,$name) = @_; $name =~ s=.*/==; my ($root,$ext) = ($name=~/(.*)\.(.*)/); my $out = "\000" x 11; substr($out,0,length($root)) = $root; substr($out,8,length($ext)) = $ext; $out; } sub make_playlist { my ($self,$file) = @_; my $count = $#{$self->list}+1; my $list = "\000" x (($count+2)*256); substr($list,0,1) = "\001"; my $name = $self->make_name($file); substr($list,2,length($name)) = $name; my $title = nkf("-s",$self->{title}); substr($list,0x100,2) = "\000\021"; substr($list,0x100+2,length($title)) = $title; if ($name =~ "PRL001") { substr($list,0x157,1) = sprintf("%c",&::num()+1); } my $position = 0x200; for(my $i=0;$i<$count;$i++) { my $song= $self->{list}->[$i]; my $song_file = $self->make_song_filename($song->dest_file_name()); my $song_title = nkf("-s",$song->title()); substr($list,$position,3) = "\001\000\001"; substr($list,$position+0xf,1) = "\021"; substr($list,$position+3,length($song_file)) = $song_file; substr($list,$position+0x10,length($song_title)) = $song_title; $position += 0x100; } open(PLAYLIST,">$file") or die("Can't open $file"); syswrite(PLAYLIST,$list); } sub add { my ($self,$song) = @_; push(@{$self->{list}},$song); } sub list { my ($self) = @_; $self->{list}; } package AlbumList; sub new { my $self = {hash=>{}}; bless $self; } sub add { my ($self,$album_title,$song) = @_; my $album = $self->{hash}->{$album_title}; if (! defined $album) { $album = Album->new($album_title); $self->{hash}->{$album_title} = $album; } $album->add($song); } sub hash { my ($self) = @_; $self->{hash}; } # end