@basmilius/apple-encoding 0.5.4 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daap.d.ts ADDED
@@ -0,0 +1,95 @@
1
+ export declare const ContentCode: {
2
+ readonly mlit: "dmap.listingitem";
3
+ readonly mlcl: "dmap.listing";
4
+ readonly msrv: "dmap.serverinforesponse";
5
+ readonly mcon: "dmap.container";
6
+ readonly miid: "dmap.itemid";
7
+ readonly minm: "dmap.itemname";
8
+ readonly mikd: "dmap.itemkind";
9
+ readonly mper: "dmap.persistentid";
10
+ readonly asal: "daap.songalbum";
11
+ readonly asar: "daap.songartist";
12
+ readonly asaa: "daap.songalbumartist";
13
+ readonly ascp: "daap.songcomposer";
14
+ readonly asgn: "daap.songgenre";
15
+ readonly astm: "daap.songtime";
16
+ readonly astn: "daap.songtracknumber";
17
+ readonly asdc: "daap.songdisccount";
18
+ readonly asdn: "daap.songdiscnumber";
19
+ readonly astc: "daap.songtrackcount";
20
+ readonly asyr: "daap.songyear";
21
+ readonly asbr: "daap.songbitrate";
22
+ readonly assr: "daap.songsamplerate";
23
+ readonly assz: "daap.songsize";
24
+ readonly caps: "daap.songplaystatus";
25
+ readonly cash: "daap.songshufflestate";
26
+ readonly carp: "daap.songrepeatstate";
27
+ readonly cavs: "daap.songvisiblestate";
28
+ readonly aePP: "com.apple.itunes.photo-properties";
29
+ };
30
+ export declare const TagType: {
31
+ readonly mlit: 12;
32
+ readonly mlcl: 12;
33
+ readonly mcon: 12;
34
+ readonly msrv: 12;
35
+ readonly miid: 5;
36
+ readonly minm: 9;
37
+ readonly mikd: 1;
38
+ readonly mper: 7;
39
+ readonly asal: 9;
40
+ readonly asar: 9;
41
+ readonly asaa: 9;
42
+ readonly ascp: 9;
43
+ readonly asgn: 9;
44
+ readonly astm: 5;
45
+ readonly astn: 3;
46
+ readonly asdc: 3;
47
+ readonly asdn: 3;
48
+ readonly astc: 3;
49
+ readonly asyr: 3;
50
+ readonly asbr: 3;
51
+ readonly assr: 5;
52
+ readonly assz: 5;
53
+ readonly caps: 1;
54
+ readonly cash: 1;
55
+ readonly carp: 1;
56
+ readonly cavs: 1;
57
+ readonly aePP: 9;
58
+ };
59
+ export declare function encodeTag(tag: string, value: Buffer | string | number | bigint): Buffer;
60
+ export declare function encodeContainer(tag: string, content: Buffer): Buffer;
61
+ export declare function encodePlaybackStatus(status: PlaybackStatus): Buffer;
62
+ export declare function encodeTagWithSize(tag: string, value: number, byteSize: 1 | 2 | 4 | 8): Buffer;
63
+ export declare function encodeTrackMetadata(metadata: TrackMetadata): Buffer;
64
+ export declare function decode(buffer: Buffer): DecodedTag[];
65
+ export declare function decodeTag(buffer: Buffer): [DecodedTag, Buffer] | null;
66
+ export declare function decodeToObject(buffer: Buffer): Record<string, unknown>;
67
+ export declare function decodeTrackMetadata(buffer: Buffer): TrackMetadata;
68
+ export type ContentCodeKey = keyof typeof ContentCode;
69
+ export type DecodedTag = {
70
+ readonly tag: string;
71
+ readonly length: number;
72
+ readonly value: Buffer;
73
+ };
74
+ export type PlaybackStatus = {
75
+ readonly playing?: boolean;
76
+ readonly shuffle?: boolean;
77
+ readonly repeat?: "off" | "one" | "all";
78
+ };
79
+ export type TrackMetadata = {
80
+ readonly title?: string;
81
+ readonly artist?: string;
82
+ readonly albumArtist?: string;
83
+ readonly album?: string;
84
+ readonly composer?: string;
85
+ readonly genre?: string;
86
+ readonly duration?: number;
87
+ readonly trackNumber?: number;
88
+ readonly trackCount?: number;
89
+ readonly discNumber?: number;
90
+ readonly discCount?: number;
91
+ readonly year?: number;
92
+ readonly bitrate?: number;
93
+ readonly sampleRate?: number;
94
+ readonly size?: number;
95
+ };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * as DAAP from "./daap";
1
2
  export * as NTP from "./ntp";
2
3
  export * as OPack from "./opack";
3
4
  export * as Plist from "./plist";
package/dist/index.js CHANGED
@@ -9,6 +9,284 @@ var __export = (target, all) => {
9
9
  });
10
10
  };
11
11
 
12
+ // src/daap.ts
13
+ var exports_daap = {};
14
+ __export(exports_daap, {
15
+ encodeTrackMetadata: () => encodeTrackMetadata,
16
+ encodeTagWithSize: () => encodeTagWithSize,
17
+ encodeTag: () => encodeTag,
18
+ encodePlaybackStatus: () => encodePlaybackStatus,
19
+ encodeContainer: () => encodeContainer,
20
+ decodeTrackMetadata: () => decodeTrackMetadata,
21
+ decodeToObject: () => decodeToObject,
22
+ decodeTag: () => decodeTag,
23
+ decode: () => decode,
24
+ TagType: () => TagType,
25
+ ContentCode: () => ContentCode
26
+ });
27
+ var ContentCode = {
28
+ mlit: "dmap.listingitem",
29
+ mlcl: "dmap.listing",
30
+ msrv: "dmap.serverinforesponse",
31
+ mcon: "dmap.container",
32
+ miid: "dmap.itemid",
33
+ minm: "dmap.itemname",
34
+ mikd: "dmap.itemkind",
35
+ mper: "dmap.persistentid",
36
+ asal: "daap.songalbum",
37
+ asar: "daap.songartist",
38
+ asaa: "daap.songalbumartist",
39
+ ascp: "daap.songcomposer",
40
+ asgn: "daap.songgenre",
41
+ astm: "daap.songtime",
42
+ astn: "daap.songtracknumber",
43
+ asdc: "daap.songdisccount",
44
+ asdn: "daap.songdiscnumber",
45
+ astc: "daap.songtrackcount",
46
+ asyr: "daap.songyear",
47
+ asbr: "daap.songbitrate",
48
+ assr: "daap.songsamplerate",
49
+ assz: "daap.songsize",
50
+ caps: "daap.songplaystatus",
51
+ cash: "daap.songshufflestate",
52
+ carp: "daap.songrepeatstate",
53
+ cavs: "daap.songvisiblestate",
54
+ aePP: "com.apple.itunes.photo-properties"
55
+ };
56
+ var TagType = {
57
+ mlit: 12,
58
+ mlcl: 12,
59
+ mcon: 12,
60
+ msrv: 12,
61
+ miid: 5,
62
+ minm: 9,
63
+ mikd: 1,
64
+ mper: 7,
65
+ asal: 9,
66
+ asar: 9,
67
+ asaa: 9,
68
+ ascp: 9,
69
+ asgn: 9,
70
+ astm: 5,
71
+ astn: 3,
72
+ asdc: 3,
73
+ asdn: 3,
74
+ astc: 3,
75
+ asyr: 3,
76
+ asbr: 3,
77
+ assr: 5,
78
+ assz: 5,
79
+ caps: 1,
80
+ cash: 1,
81
+ carp: 1,
82
+ cavs: 1,
83
+ aePP: 9
84
+ };
85
+ function encodeTag(tag, value) {
86
+ if (tag.length !== 4) {
87
+ throw new Error(`Invalid DAAP tag: ${tag}. Tags must be exactly 4 characters.`);
88
+ }
89
+ const tagBuffer = Buffer.from(tag, "ascii");
90
+ let valueBuffer;
91
+ if (typeof value === "string") {
92
+ valueBuffer = Buffer.from(value, "utf8");
93
+ } else if (typeof value === "bigint") {
94
+ valueBuffer = Buffer.alloc(8);
95
+ valueBuffer.writeBigUInt64BE(value, 0);
96
+ } else if (typeof value === "number") {
97
+ if (value <= 255 && value >= 0) {
98
+ valueBuffer = Buffer.alloc(1);
99
+ valueBuffer.writeUInt8(value, 0);
100
+ } else if (value <= 65535 && value >= 0) {
101
+ valueBuffer = Buffer.alloc(2);
102
+ valueBuffer.writeUInt16BE(value, 0);
103
+ } else if (value <= 4294967295 && value >= 0) {
104
+ valueBuffer = Buffer.alloc(4);
105
+ valueBuffer.writeUInt32BE(value, 0);
106
+ } else {
107
+ valueBuffer = Buffer.alloc(8);
108
+ valueBuffer.writeBigInt64BE(BigInt(value), 0);
109
+ }
110
+ } else {
111
+ valueBuffer = value;
112
+ }
113
+ const lengthBuffer = Buffer.alloc(4);
114
+ lengthBuffer.writeUInt32BE(valueBuffer.length, 0);
115
+ return Buffer.concat([tagBuffer, lengthBuffer, valueBuffer]);
116
+ }
117
+ function encodeContainer(tag, content) {
118
+ if (tag.length !== 4) {
119
+ throw new Error(`Invalid DAAP tag: ${tag}. Tags must be exactly 4 characters.`);
120
+ }
121
+ const tagBuffer = Buffer.from(tag, "ascii");
122
+ const lengthBuffer = Buffer.alloc(4);
123
+ lengthBuffer.writeUInt32BE(content.length, 0);
124
+ return Buffer.concat([tagBuffer, lengthBuffer, content]);
125
+ }
126
+ function encodePlaybackStatus(status) {
127
+ const tags = [];
128
+ if (status.playing !== undefined) {
129
+ tags.push(encodeTagWithSize("caps", status.playing ? 4 : 3, 1));
130
+ }
131
+ if (status.shuffle !== undefined) {
132
+ tags.push(encodeTagWithSize("cash", status.shuffle ? 1 : 0, 1));
133
+ }
134
+ if (status.repeat !== undefined) {
135
+ let repeatValue = 0;
136
+ if (status.repeat === "one")
137
+ repeatValue = 1;
138
+ else if (status.repeat === "all")
139
+ repeatValue = 2;
140
+ tags.push(encodeTagWithSize("carp", repeatValue, 1));
141
+ }
142
+ return Buffer.concat(tags);
143
+ }
144
+ function encodeTagWithSize(tag, value, byteSize) {
145
+ if (tag.length !== 4) {
146
+ throw new Error(`Invalid DAAP tag: ${tag}. Tags must be exactly 4 characters.`);
147
+ }
148
+ const tagBuffer = Buffer.from(tag, "ascii");
149
+ const valueBuffer = Buffer.alloc(byteSize);
150
+ switch (byteSize) {
151
+ case 1:
152
+ valueBuffer.writeUInt8(value, 0);
153
+ break;
154
+ case 2:
155
+ valueBuffer.writeUInt16BE(value, 0);
156
+ break;
157
+ case 4:
158
+ valueBuffer.writeUInt32BE(value, 0);
159
+ break;
160
+ case 8:
161
+ valueBuffer.writeBigUInt64BE(BigInt(value), 0);
162
+ break;
163
+ }
164
+ const lengthBuffer = Buffer.alloc(4);
165
+ lengthBuffer.writeUInt32BE(byteSize, 0);
166
+ return Buffer.concat([tagBuffer, lengthBuffer, valueBuffer]);
167
+ }
168
+ function encodeTrackMetadata(metadata) {
169
+ const tags = [];
170
+ if (metadata.title !== undefined) {
171
+ tags.push(encodeTag("minm", metadata.title));
172
+ }
173
+ if (metadata.artist !== undefined) {
174
+ tags.push(encodeTag("asar", metadata.artist));
175
+ }
176
+ if (metadata.albumArtist !== undefined) {
177
+ tags.push(encodeTag("asaa", metadata.albumArtist));
178
+ }
179
+ if (metadata.album !== undefined) {
180
+ tags.push(encodeTag("asal", metadata.album));
181
+ }
182
+ if (metadata.composer !== undefined) {
183
+ tags.push(encodeTag("ascp", metadata.composer));
184
+ }
185
+ if (metadata.genre !== undefined) {
186
+ tags.push(encodeTag("asgn", metadata.genre));
187
+ }
188
+ if (metadata.duration !== undefined) {
189
+ tags.push(encodeTagWithSize("astm", Math.floor(metadata.duration * 1000), 4));
190
+ }
191
+ if (metadata.trackNumber !== undefined) {
192
+ tags.push(encodeTagWithSize("astn", metadata.trackNumber, 2));
193
+ }
194
+ if (metadata.trackCount !== undefined) {
195
+ tags.push(encodeTagWithSize("astc", metadata.trackCount, 2));
196
+ }
197
+ if (metadata.discNumber !== undefined) {
198
+ tags.push(encodeTagWithSize("asdn", metadata.discNumber, 2));
199
+ }
200
+ if (metadata.discCount !== undefined) {
201
+ tags.push(encodeTagWithSize("asdc", metadata.discCount, 2));
202
+ }
203
+ if (metadata.year !== undefined) {
204
+ tags.push(encodeTagWithSize("asyr", metadata.year, 2));
205
+ }
206
+ if (metadata.bitrate !== undefined) {
207
+ tags.push(encodeTagWithSize("asbr", metadata.bitrate, 2));
208
+ }
209
+ if (metadata.sampleRate !== undefined) {
210
+ tags.push(encodeTagWithSize("assr", metadata.sampleRate, 4));
211
+ }
212
+ if (metadata.size !== undefined) {
213
+ tags.push(encodeTagWithSize("assz", metadata.size, 4));
214
+ }
215
+ const content = Buffer.concat(tags);
216
+ return encodeContainer("mlit", content);
217
+ }
218
+ function decode(buffer) {
219
+ const tags = [];
220
+ let remaining = buffer;
221
+ while (remaining.length > 0) {
222
+ const result = decodeTag(remaining);
223
+ if (!result)
224
+ break;
225
+ const [tag, rest] = result;
226
+ tags.push(tag);
227
+ remaining = rest;
228
+ }
229
+ return tags;
230
+ }
231
+ function decodeTag(buffer) {
232
+ if (buffer.length < 8) {
233
+ return null;
234
+ }
235
+ const tag = buffer.subarray(0, 4).toString("ascii");
236
+ const length = buffer.readUInt32BE(4);
237
+ if (buffer.length < 8 + length) {
238
+ return null;
239
+ }
240
+ const value = buffer.subarray(8, 8 + length);
241
+ const remaining = buffer.subarray(8 + length);
242
+ return [{ tag, length, value }, remaining];
243
+ }
244
+ function decodeToObject(buffer) {
245
+ const result = {};
246
+ const tags = decode(buffer);
247
+ for (const { tag, value } of tags) {
248
+ const tagType = TagType[tag];
249
+ if (tagType === undefined) {
250
+ result[tag] = value;
251
+ } else if (tagType === 12) {
252
+ result[tag] = decodeToObject(value);
253
+ } else if (tagType === 9) {
254
+ result[tag] = value.toString("utf8");
255
+ } else if (tagType === 1 || tagType === 2) {
256
+ result[tag] = value.readUInt8(0);
257
+ } else if (tagType === 3 || tagType === 4) {
258
+ result[tag] = value.readUInt16BE(0);
259
+ } else if (tagType === 5 || tagType === 6) {
260
+ result[tag] = value.readUInt32BE(0);
261
+ } else if (tagType === 7 || tagType === 8) {
262
+ result[tag] = value.readBigUInt64BE(0);
263
+ } else {
264
+ result[tag] = value;
265
+ }
266
+ }
267
+ return result;
268
+ }
269
+ function decodeTrackMetadata(buffer) {
270
+ const obj = decodeToObject(buffer);
271
+ const mlit = obj.mlit ?? obj;
272
+ return {
273
+ title: mlit.minm,
274
+ artist: mlit.asar,
275
+ albumArtist: mlit.asaa,
276
+ album: mlit.asal,
277
+ composer: mlit.ascp,
278
+ genre: mlit.asgn,
279
+ duration: mlit.astm !== undefined ? mlit.astm / 1000 : undefined,
280
+ trackNumber: mlit.astn,
281
+ trackCount: mlit.astc,
282
+ discNumber: mlit.asdn,
283
+ discCount: mlit.asdc,
284
+ year: mlit.asyr,
285
+ bitrate: mlit.asbr,
286
+ sampleRate: mlit.assr,
287
+ size: mlit.assz
288
+ };
289
+ }
12
290
  // src/ntp.ts
13
291
  var exports_ntp = {};
14
292
  __export(exports_ntp, {
@@ -16,7 +294,7 @@ __export(exports_ntp, {
16
294
  ns: () => ns,
17
295
  now: () => now,
18
296
  encode: () => encode,
19
- decode: () => decode
297
+ decode: () => decode2
20
298
  });
21
299
  var EPOCH = 0x83AA7E80n;
22
300
  function now() {
@@ -34,7 +312,7 @@ function parts(ntp) {
34
312
  Number(ntp & 0xFFFFFFFFn)
35
313
  ];
36
314
  }
37
- function decode(buffer) {
315
+ function decode2(buffer) {
38
316
  if (buffer.length < 24) {
39
317
  throw new RangeError(`NTP packet too small: expected at least 24 bytes, got ${buffer.length}`);
40
318
  }
@@ -72,7 +350,7 @@ __export(exports_opack, {
72
350
  int: () => int,
73
351
  float: () => float,
74
352
  encode: () => encode2,
75
- decode: () => decode2
353
+ decode: () => decode3
76
354
  });
77
355
  var TAG = {
78
356
  TRUE: 1,
@@ -160,7 +438,7 @@ function int(value) {
160
438
  function sizedInteger(value, size) {
161
439
  return new SizedInteger(value, size);
162
440
  }
163
- function decode2(data) {
441
+ function decode3(data) {
164
442
  const [value] = _unpack(data, []);
165
443
  return value;
166
444
  }
@@ -1172,7 +1450,7 @@ function parseResponseHeaders(buffer) {
1172
1450
  var exports_tlv8 = {};
1173
1451
  __export(exports_tlv8, {
1174
1452
  encode: () => encode3,
1175
- decode: () => decode3,
1453
+ decode: () => decode4,
1176
1454
  bail: () => bail,
1177
1455
  Value: () => Value,
1178
1456
  State: () => State,
@@ -1267,7 +1545,7 @@ function encode3(entries) {
1267
1545
  }
1268
1546
  return Buffer.from(chunks);
1269
1547
  }
1270
- function decode3(buf) {
1548
+ function decode4(buf) {
1271
1549
  const map = new Map;
1272
1550
  let i = 0;
1273
1551
  while (i < buf.length) {
@@ -1289,5 +1567,6 @@ export {
1289
1567
  exports_rtsp as RTSP,
1290
1568
  exports_plist as Plist,
1291
1569
  exports_opack as OPack,
1292
- exports_ntp as NTP
1570
+ exports_ntp as NTP,
1571
+ exports_daap as DAAP
1293
1572
  };
package/dist/rtsp.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type Method = "GET" | "OPTIONS" | "POST" | "PUT" | "GET_PARAMETER" | "SET_PARAMETER" | "ANNOUNCE" | "RECORD" | "SETUP" | "TEARDOWN";
1
+ export type Method = "GET" | "OPTIONS" | "POST" | "PUT" | "GET_PARAMETER" | "SET_PARAMETER" | "ANNOUNCE" | "FLUSH" | "RECORD" | "SETUP" | "TEARDOWN";
2
2
  export declare function makeHeader(method: Method, path: string, headers: HeadersInit, cseq: number, activeRemote: string, dacpId: string, sessionId: string): string;
3
3
  export declare function makeRequest(buffer: Buffer): HttpRequest | null;
4
4
  export declare function makeResponse(buffer: Buffer): HttpResponse | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@basmilius/apple-encoding",
3
3
  "description": "Common encoding utilities for Apple Protocols.",
4
- "version": "0.5.4",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {