Net::DAAP::DMAP(3pm)

πŸ“¦
version 1.27 Β· 2 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble

πŸ’– NAME πŸ’–

Net::DAAP::DMAP β€” it's a Perl module for reading and writing DAAP structures. That's the whole vibe, bestie. πŸŽ€


✨ 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! 🚨

Okay so, heads up, this is important: until 2.0, the author reserves the right to change the interface. And I mean reserve reserve. Specifically, they think dmap_flatten, dmap_to_hash_ref, and dmap_to_array_ref are likely to disappear. πŸ’€ And they also suspect the hive brain of Perl can come up with a better data structure than they have. So like… don't get too attached, iykyk.


πŸ¦‹ Back to the Description πŸ¦‹

So a DMAP structure is a binary record, and it's the thing Apple's DAAP protocol uses. And here's the cute part: a DMAP structure can contain other DMAP structures. It's containers all the way down, literally. 🫢

Fields in a DMAP structure get identified by a short name, like "msdc". That short name is what's actually sitting there in the binary record β€” but a content codes list is what hands you the long name ("dmap.databasescount") plus a data type for that record (in this case, a 32-bit integer). So the short name is the ✨mystery✨ and the content codes list is the translator.

A parsed DMAP structure is built out of arrays. For example:

[
  [
    'dmap.loginresponse',
    [
       [
         'dmap.status',
         200
       ],
       [
         'dmap.sessionid',
          2393
       ]
    ]
  ]
]

(dmap_unpack is the one that returns this kind of structure, btw.)

There are two rules here, and that's it, two: a field is wrapped in an array, and a container's values are wrapped in an array. So the structure gets programmatically built like:

$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 like this one does. It's giving optional extra nesting, and honestly? Fair.

In XML you'd write that response as:

<dmap.loginresponse>
    <dmap.status>200</dmap.status>
    <dmap.sessionid>2393</dmap.sessionid>
</dmap.loginresponse>

And that right there 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,
    },
}

That's 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. So yeah, that hash is going to eat your duplicates, no cap.

The array structure that dmap_to_array_ref hands back is complex, but the dmap_seek function makes it easier. You give it a structure and a path, where the path is a slash-separated list of field names:

dmap.loginresponse/dmap.sessionid

The return value is the value of the first dmap.sessionid found in the first dmap.loginresponse structure. With the sample record up there, that would be 2393. Cute, right? 🌸

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. Like:

[
  '/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. (And if that is the way your mind works β€” main character energy, honestly. πŸ’…)

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 β€” the duplicates thing again, it always comes back 😭), 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. That's the whole thing. πŸ“


🧚 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, because not every platform has 64-bit int support available. Bestie said portability, and honestly we respect it. 🫢

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? Nobody knows, it's a mystery ✨. 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.


Okay that's the whole page, we ate and left no crumbs πŸ’… β€” this was perl v5.32.0, dated 2021-01-08, and you read it in Net::DAAP::DMAP(3pm). Periodt. πŸŽ€βœ¨

πŸ“¦ where this page comes from