#!/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 <kono@ie.u-ryukyu.ac.jp>

=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(<OSA>);
my @files = <OSA>;

# print "@files\n";

my $albums = &make_albums(@files);

&copy($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;
	    &copy_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
