Mac-iPod-GNUpod

 view release on metacpan or  search on metacpan

GNUpod/iTunesDBwrite.pm  view on Meta::CPAN

    foreach my $chr (@{$hs->{data}}) {
        my $string = undef;
        #Fixme: this is ugly (same as read_spldata)
        if($chr->{field} =~ /^(2|3|4|8|9|14|18)$/) {
            $string = Unicode::String::utf8($chr->{string})->utf16;
        }
        else {
            my ($from, $to) = $chr->{string} =~ /(\d+):?(\d*)/;
            $to ||=$from;
            $string  = pack("H8");
            $string .= pack("H8", _x86itop($from));
            $string .= pack("H24");
            $string .= pack("H8", _x86itop(1));
            $string .= pack("H8");
            $string .= pack("H8", _x86itop($to));
            $string .= pack("H24");
            $string .= pack("H8", _x86itop(1));
            $string .= pack("H40");
            #  __hd($string);
        }

        if(length($string) > 254) { #length field is limited to 0xfe!
            $@ .= "Splstring too long for iTunes, cropping\n";
            $string = substr($string,0,254);
        }

        $cr .= pack("H6");
        $cr .= pack("h2", _itop($chr->{field}));
        $cr .= pack("H6", reverse("010000"));
        $cr .= pack("h2", _itop($chr->{action}));
        $cr .= pack("H94");
        $cr .= pack("h2", _itop(length($string)));
        $cr .= $string;
    }

    my $ret = "mhod";
    $ret .= pack("h8", _itop(24));    #Size of header
    $ret .= pack("h8", _itop(length($cr)+160));    #header+body size
    $ret .= pack("h8", _itop(51));    #type
    $ret .= pack("H16");
    $ret .= "SLst";                   #Magic
    $ret .= pack("H8", reverse("00010001")); #?
    $ret .= pack("h6");
    $ret .= pack("h2", _itop(int(@{$hs->{data}})));     #HTM (Childs from cr)
    $ret .= pack("h6");
    $ret .= pack("h2", _itop($anymatch));     #anymatch rule on or off
    $ret .= pack("h240");


    $ret .= $cr;
    return $ret;
}

# Render a playlist
sub r_mpl { 
    # Expects a hash w/ the following keys:
    #   name => $ name of the pl
    #   type => $ type of the pl
    #   ids => [] list of songids of in pl
    #   curid => $ current id in db
    #   splprefs => {} holds spl prefs
    #   spldata => {} holds spl data
    my %dat = @_;
    my ($pl, $fc, $mhp) = ('', 0, 0);

    # Spls handled here
    if(ref($dat{splprefs}) eq "HASH") {
        my $spl = $dat{splprefs};
        $pl .= mk_splprefmhod({
              item => $spl->{limititem},
              sort => $spl->{limitsort},
              mos => $spl->{moselected},
              liveupdate => $spl->{liveupdate},
              value => $spl->{limitval},
              checkrule => $spl->{checkrule}
        });

        $pl .= mk_spldatamhod({anymatch => $spl->{matchany}, data => $dat{spldata}});
        $mhp=2;
    }

    foreach(@{$dat{ids}}) {
        $dat{curid}++;
        my $cmhip = mk_mhip({childs => 1, plid => $dat{curid}, sid => $_});
        my $cmhod = mk_mhod({fqid => $_});
        next unless (defined($cmhip) && defined($cmhod)); #mk_mhod needs to be ok
        $fc++;
        $pl .= $cmhip . $cmhod;
    }
    my $plsize = length($pl);

    #mhyp appends a listview to itself
    my $mhyp = mk_mhyp({
        size => $plsize, name => $dat{name}, type => $dat{type}, files => $fc, mhods => $mhp
    });
    return $mhyp . $pl, $dat{curid};
}


# header for all files (like you use mk_mhlp for playlists)
sub mk_mhlt {
    my ($hr) = @_;

    my $ret = "mhlt";
    $ret .= pack("h8", _itop(92)); 		    #Header size (static)
    $ret .= pack("h8", _itop($hr->{songs})); #songs in this itunesdb
    $ret .= pack("H160", "00");                      #dummy space

    return $ret;
}

# header for ALL playlists
sub mk_mhlp {
    my ($hr) = @_;

    my $ret = "mhlp";
    $ret .= pack("h8", _itop(92));                   #Static header size
    $ret .= pack("h8", _itop($hr->{playlists}));          #playlists on iPod (including main!)
    $ret .= pack("h160", "00");                     #dummy space
    return $ret;
}

# Creates an header for a new playlist (child of mk_mhlp)
sub mk_mhyp {
    my($hr) = @_;

    # We need to create a listview-layout and an mhod with the name. iTunes
    # prefs for this PL & PL name (default PL has  device name as PL name)
    my $appnd = mk_mhod({stype=>"title", string=>$hr->{name}}).__dummy_listview();   

    ##Child mhods calc..
    ##We create 2 mhod's here.. mktunes may have created more mhods.. so we
    ##have to adjust the childs here
    my $cmh = 2+$hr->{mhods};

    my $ret .= "mhyp";
    $ret .= pack("h8", _itop(108)); #type
    $ret .= pack("h8", _itop($hr->{size}+108+(length($appnd))));          #size
    $ret .= pack("h8", _itop($cmh));			      #mhods
    $ret .= pack("h8", _itop($hr->{files}));   #songs in pl
    $ret .= pack("h8", _itop($hr->{type}));    # 1 = main .. 0=not main
    $ret .= pack("H8", "00"); 			      #?
    $ret .= pack("H8", "00");                  #?
    $ret .= pack("H8", "00");                  #?
    $ret .= pack("H144", "00");       		  #dummy space

    return $ret.$appnd;
}


# header for new Playlist item (child if mk_mhyp)
sub mk_mhip {
    my ($hr) = @_;
    #sid = SongId
    #plid = playlist order ID

    my $ret = "mhip";
    $ret .= pack("h8", _itop(76));
    $ret .= pack("h8", _itop(76));
    $ret .= pack("h8", _itop($hr->{childs})); #Mhod childs !
    $ret .= pack("H8", "00");
    $ret .= pack("h8", _itop($hr->{plid})); #ORDER id
    $ret .= pack("h8", _itop($hr->{sid}));   #song id in playlist
    $ret .= pack("H96", "00");
    return $ret;
}

#Convert utf8 (what we got from XML::Parser) to utf16 (ipod)
sub _ipod_string {
    my $utf8 = shift;
    my $utf16;
    # We got utf8 from parser, the iPod likes utf16.., swapped..
    if (UNIVERSAL::isa($utf8, 'Unicode::String')) {
        $utf16 = $utf8->utf16;
    }
    else {
        $utf16 = Unicode::String::utf8($utf8)->utf16;
    }
    $utf16 = Unicode::String::byteswap2($utf16);
    return $utf16;
}

#returns a (dummy) timestamp in MAC time format
sub _mactime {
    my $x =    1234567890;
    return sprintf("%08X", $x);
}



( run in 0.741 second using v1.01-cache-2.11-cpan-8f98c5d2c55 )