NAME¶
Net::DAAP::DMAP - Perl module for reading and writing DAAP structures
SYNOPSIS¶
use Net::DAAP::DMAP qw(:all);
$hash_ref = dmap_to_hash_ref($dmap); # crude
$array_ref = dmap_to_array_ref($dmap); # crude
$array_ref = dmap_unpack($dmap); # knows about data types
$node = dmap_seek($array_ref, $path);
$flattened = dmap_flatten($array_ref); # convert to path = data formta
$flat_list = dmap_flat_list($array_ref); # convert to [ path, data ] format
$xml = dmap_to_xml($dmap); # convert to XML fragment
$dmap = dmap_pack($dmap); # convert to DMAP packet
update_content_codes($unpacked_content_codes_response);
DESCRIPTION¶
WARNING!¶
Until 2.0, I reserve the right to change the interface. In particular, I think
"dmap_flatten", "dmap_to_hash_ref", and
"dmap_to_array_ref" are likely to disappear. And I suspect the hive
brain of Perl can come up with a better data structure than I have.
Back to the Description¶
A DMAP structure is a binary record used in Apple's DAAP protocol. A DMAP
structure may contain other DMAP structures. Fields in a DMAP structure are
identified by a short name ("msdc"). The short name is what's in the
binary record, but a content codes list gives a long name
("dmap.databasescount") and a data type for the record (32-bit
integer).
A parsed DMAP structure is built out of arrays. For example:
[
[
'dmap.loginresponse',
[
[
'dmap.status',
200
],
[
'dmap.sessionid',
2393
]
]
]
]
("dmap_unpack" returns this kind of structure)
There are two rules here: a field is wrapped in an array, and a container's
values are wrapped in an array. So the structure is programmatically built as:
$status_field = [ 'dmap.status', 200 ];
$session_id_field = [ 'dmap.sessionid', 2393 ];
$response_value = [ $status_field, $session_id_field ];
$login_response_field = [ 'dmap.loginresponse', $response_value ];
$entire_response = [ $login_response_field ];
The outer array is necessary because not every response has only one top-level
container as this does.
In XML you'd write the response as:
<dmap.loginresponse>
<dmap.status>200</dmap.status>
<dmap.sessionid>2393</dmap.sessionid>
</dmap.loginresponse>
This is what "dmap_to_xml" returns.
A much more convenient structure for representing this data would be:
{
'dmap.loginresponse' => {
{ 'dmap.status' => 200,
'dmap.sessionid' => 2393,
},
}
This is the output of "dmap_to_hash_ref", but beware! This isn't
suitable for every response. The hash is indexed by field name and a structure
may contain many elements of the same name. For example, requesting the
content codes list gives you a list of records that have the field name
"dmap.dictionary".
The array structure returned by "dmap_to_array_ref" is complex, but
the "dmap_seek" function makes it easier. This takes a structure and
a path expressed as a slash-separated list of field names:
dmap.loginresponse/dmap.sessionid
The return value is the the value of the first "dmap.sessionid" found
in the first "dmap.loginresponse" structure. In the case of the
sample record above, it would be 2393.
Another way to handle these complex arrays is to "dmap_flatten" them.
This returns an array of "
path = value" lines, where
path is a slash-separated path. For example:
[
'/dmap.loginresponse/dmap.status = 200',
'/dmap.loginresponse/dmap.sessionid = 2393'
]
You can use "grep" and regexps to find data if that's the way your
mind works.
"dmap_flatten" has a similar looking cousin called
"dmap_flat_list", which returns an array of "
path =>
value" pairs. For example:
[
'/dmap.loginresponse/dmap.status' => 200,
'/dmap.loginresponse/dmap.sessionid' => 2393,
]
You can then turn this into a hash (which may of course lose you the first
elements), or iterate over it in pairs, if that's easier.
You can, but don't have to, update the tables of field names ("content
codes") and data types. DAAP offers a request that returns a packet of
content codes. Feed that packet to "update_content_codes".
Implementation Details¶
It's all implementation details. Here are the various data types.
1, 3, 5, 7 = ints, size 8,16,32,64 bit
9 = string, 10 = time_t-style time
11 = version (two 16-bit ints, I think)
12 = container
This uses Math::BigInt for 64-bit quantities, as not every platform has 64-bit
int support available.
There's no support for types 2, 4, 6, 8 yet because nobody'd found examples of
them in the field: are they endian changes, or signedness changes. The
assumption is that all numbers are unsigned (why allow the possibility of a
negative number of songs?).
AUTHOR¶
Nathan Torkington, <nathan AT torkington.com>. For support, join the DAAP
developers mailing list by sending mail to <daap-dev-subscribe AT
develooper.com>.
Richard Clamp <richardc@unixbeard.net> is the current maintainer, and took
over in July 2004.