taglib-wasm 1.5.3 → 1.6.1
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/LICENSE +12 -9
- package/README.md +6 -3
- package/dist/index.browser.js +402 -90
- package/dist/simple.browser.js +400 -89
- package/dist/src/constants/complex-properties.d.ts +18 -12
- package/dist/src/constants/complex-properties.d.ts.map +1 -1
- package/dist/src/constants/properties.d.ts +24 -0
- package/dist/src/constants/properties.d.ts.map +1 -1
- package/dist/src/constants/properties.js +34 -0
- package/dist/src/constants/specialized-properties.d.ts +23 -0
- package/dist/src/constants/specialized-properties.d.ts.map +1 -1
- package/dist/src/constants/specialized-properties.js +20 -0
- package/dist/src/constants/tags.d.ts.map +1 -1
- package/dist/src/constants/tags.js +2 -1
- package/dist/src/msgpack/encoder.d.ts.map +1 -1
- package/dist/src/msgpack/encoder.js +6 -4
- package/dist/src/runtime/detector.d.ts +0 -6
- package/dist/src/runtime/detector.d.ts.map +1 -1
- package/dist/src/runtime/detector.js +1 -14
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts +2 -0
- package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
- package/dist/src/runtime/wasi-adapter/file-handle.js +73 -52
- package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
- package/dist/src/taglib/audio-file-base.js +33 -77
- package/dist/src/taglib/load-audio-data.d.ts.map +1 -1
- package/dist/src/taglib/load-audio-data.js +19 -5
- package/dist/src/taglib/metadata-extent.d.ts +41 -0
- package/dist/src/taglib/metadata-extent.d.ts.map +1 -0
- package/dist/src/taglib/metadata-extent.js +128 -0
- package/dist/src/taglib/mp4-item-names.d.ts +12 -0
- package/dist/src/taglib/mp4-item-names.d.ts.map +1 -0
- package/dist/src/taglib/mp4-item-names.js +4 -0
- package/dist/src/taglib/mutable-tag-impl.d.ts +14 -0
- package/dist/src/taglib/mutable-tag-impl.d.ts.map +1 -0
- package/dist/src/taglib/mutable-tag-impl.js +88 -0
- package/dist/src/taglib/save-reconstruct.d.ts.map +1 -1
- package/dist/src/taglib/save-reconstruct.js +4 -0
- package/dist/src/types/metadata-mappings.d.ts.map +1 -1
- package/dist/src/types/metadata-mappings.js +14 -0
- package/dist/src/types/tags.d.ts +16 -0
- package/dist/src/types/tags.d.ts.map +1 -1
- package/dist/src/utils/mirror-fields.d.ts +79 -0
- package/dist/src/utils/mirror-fields.d.ts.map +1 -0
- package/dist/src/utils/mirror-fields.js +67 -0
- package/dist/src/utils/tag-mapping.d.ts.map +1 -1
- package/dist/src/utils/tag-mapping.js +47 -5
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/taglib-wasi.wasm +0 -0
- package/dist/taglib-web.wasm +0 -0
- package/dist/taglib-wrapper.js +1 -1
- package/package.json +3 -3
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
mp4AtomWireKey,
|
|
6
|
+
mp4FreeformAtomNames,
|
|
7
|
+
remapKeysFromTagLib,
|
|
8
|
+
toTagLibKey
|
|
9
|
+
} from "../constants/properties.js";
|
|
5
10
|
import { MetadataError, UnsupportedFormatError } from "../errors.js";
|
|
11
|
+
import { MP4_ITEM_NAMES_KEY } from "./mp4-item-names.js";
|
|
12
|
+
import { buildMutableTag } from "./mutable-tag-impl.js";
|
|
6
13
|
const LYRICS_PROPERTY_KEY = "lyrics";
|
|
7
14
|
const LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
|
|
8
15
|
const EMPTY_KEY_SET = /* @__PURE__ */ new Set();
|
|
@@ -53,79 +60,7 @@ class BaseAudioFileImpl {
|
|
|
53
60
|
return this.getFormat() === format;
|
|
54
61
|
}
|
|
55
62
|
tag() {
|
|
56
|
-
|
|
57
|
-
let data = handle.getTagData();
|
|
58
|
-
const tag = {
|
|
59
|
-
get title() {
|
|
60
|
-
return data.title;
|
|
61
|
-
},
|
|
62
|
-
get artist() {
|
|
63
|
-
return data.artist;
|
|
64
|
-
},
|
|
65
|
-
get album() {
|
|
66
|
-
return data.album;
|
|
67
|
-
},
|
|
68
|
-
get comment() {
|
|
69
|
-
return data.comment;
|
|
70
|
-
},
|
|
71
|
-
get genre() {
|
|
72
|
-
return data.genre;
|
|
73
|
-
},
|
|
74
|
-
get year() {
|
|
75
|
-
return data.year;
|
|
76
|
-
},
|
|
77
|
-
get track() {
|
|
78
|
-
return data.track;
|
|
79
|
-
},
|
|
80
|
-
get date() {
|
|
81
|
-
return handle.getProperty("DATE") || void 0;
|
|
82
|
-
},
|
|
83
|
-
setTitle: (value) => {
|
|
84
|
-
handle.setTagData({ title: value });
|
|
85
|
-
data = handle.getTagData();
|
|
86
|
-
return tag;
|
|
87
|
-
},
|
|
88
|
-
setArtist: (value) => {
|
|
89
|
-
handle.setTagData({ artist: value });
|
|
90
|
-
data = handle.getTagData();
|
|
91
|
-
return tag;
|
|
92
|
-
},
|
|
93
|
-
setAlbum: (value) => {
|
|
94
|
-
handle.setTagData({ album: value });
|
|
95
|
-
data = handle.getTagData();
|
|
96
|
-
return tag;
|
|
97
|
-
},
|
|
98
|
-
setComment: (value) => {
|
|
99
|
-
handle.setTagData({ comment: value });
|
|
100
|
-
data = handle.getTagData();
|
|
101
|
-
return tag;
|
|
102
|
-
},
|
|
103
|
-
setGenre: (value) => {
|
|
104
|
-
handle.setTagData({ genre: value });
|
|
105
|
-
data = handle.getTagData();
|
|
106
|
-
return tag;
|
|
107
|
-
},
|
|
108
|
-
setYear: (value) => {
|
|
109
|
-
handle.setTagData({ year: value });
|
|
110
|
-
data = handle.getTagData();
|
|
111
|
-
return tag;
|
|
112
|
-
},
|
|
113
|
-
setTrack: (value) => {
|
|
114
|
-
handle.setTagData({ track: value });
|
|
115
|
-
data = handle.getTagData();
|
|
116
|
-
return tag;
|
|
117
|
-
},
|
|
118
|
-
setDate: (value) => {
|
|
119
|
-
if (value === "") {
|
|
120
|
-
handle.setTagData({ year: 0 });
|
|
121
|
-
} else {
|
|
122
|
-
handle.setProperty("DATE", value);
|
|
123
|
-
}
|
|
124
|
-
data = handle.getTagData();
|
|
125
|
-
return tag;
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
return tag;
|
|
63
|
+
return buildMutableTag(this.handle);
|
|
129
64
|
}
|
|
130
65
|
audioProperties() {
|
|
131
66
|
if (!this.cachedAudioProperties) {
|
|
@@ -147,14 +82,29 @@ class BaseAudioFileImpl {
|
|
|
147
82
|
const existing = this.handle.getProperties()[LYRICS_WIRE_KEY];
|
|
148
83
|
if (existing !== void 0) translated[LYRICS_WIRE_KEY] = existing;
|
|
149
84
|
}
|
|
85
|
+
const atomNames = mp4FreeformAtomNames(Object.keys(translated));
|
|
86
|
+
if (atomNames.length > 0) translated[MP4_ITEM_NAMES_KEY] = atomNames;
|
|
150
87
|
this.handle.setProperties(translated);
|
|
151
88
|
}
|
|
152
89
|
getProperty(key) {
|
|
153
90
|
const value = this.handle.getProperty(toTagLibKey(key));
|
|
154
|
-
|
|
91
|
+
if (value !== "") return value;
|
|
92
|
+
if (!this.isMP4()) return void 0;
|
|
93
|
+
const remapped = this.properties()[key]?.[0];
|
|
94
|
+
return remapped === "" ? void 0 : remapped;
|
|
155
95
|
}
|
|
156
96
|
setProperty(key, value) {
|
|
157
|
-
|
|
97
|
+
const wireKey = toTagLibKey(key);
|
|
98
|
+
const atomNames = mp4FreeformAtomNames([wireKey]);
|
|
99
|
+
if (atomNames.length > 0 && this.isMP4()) {
|
|
100
|
+
this.handle.setProperties({
|
|
101
|
+
...this.handle.getProperties(),
|
|
102
|
+
[wireKey]: [value],
|
|
103
|
+
[MP4_ITEM_NAMES_KEY]: atomNames
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.handle.setProperty(wireKey, value);
|
|
158
108
|
}
|
|
159
109
|
isMP4() {
|
|
160
110
|
return this.handle.isMP4();
|
|
@@ -163,13 +113,19 @@ class BaseAudioFileImpl {
|
|
|
163
113
|
if (!this.isMP4()) {
|
|
164
114
|
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
|
|
165
115
|
}
|
|
166
|
-
const
|
|
116
|
+
const wireKey = mp4AtomWireKey(key);
|
|
117
|
+
const value = wireKey !== void 0 ? this.handle.getProperty(wireKey) : this.handle.getMP4Item(key);
|
|
167
118
|
return value === "" ? void 0 : value;
|
|
168
119
|
}
|
|
169
120
|
setMP4Item(key, value) {
|
|
170
121
|
if (!this.isMP4()) {
|
|
171
122
|
throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
|
|
172
123
|
}
|
|
124
|
+
const wireKey = mp4AtomWireKey(key);
|
|
125
|
+
if (wireKey !== void 0) {
|
|
126
|
+
this.handle.setProperty(wireKey, value);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
173
129
|
this.handle.setMP4Item(key, value);
|
|
174
130
|
}
|
|
175
131
|
removeMP4Item(key) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-audio-data.d.ts","sourceRoot":"","sources":["../../../src/taglib/load-audio-data.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load-audio-data.d.ts","sourceRoot":"","sources":["../../../src/taglib/load-audio-data.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,IAAI,EAC/C,IAAI,EAAE;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,iBAAiB,EAAE,OAAO,CAAA;CAAE,CAAC,CAuD3D"}
|
|
@@ -3,6 +3,10 @@ import {
|
|
|
3
3
|
readFileData,
|
|
4
4
|
readPartialFileData
|
|
5
5
|
} from "../utils/file.js";
|
|
6
|
+
import {
|
|
7
|
+
metadataFitsInHeader,
|
|
8
|
+
trailerFitsInFooter
|
|
9
|
+
} from "./metadata-extent.js";
|
|
6
10
|
async function loadAudioData(input, opts) {
|
|
7
11
|
if (opts.partial && typeof File !== "undefined" && input instanceof File) {
|
|
8
12
|
const headerSize = Math.min(opts.maxHeaderSize, input.size);
|
|
@@ -10,12 +14,20 @@ async function loadAudioData(input, opts) {
|
|
|
10
14
|
if (input.size <= headerSize + footerSize) {
|
|
11
15
|
return { data: await readFileData(input), isPartiallyLoaded: false };
|
|
12
16
|
}
|
|
13
|
-
const header =
|
|
17
|
+
const header = new Uint8Array(
|
|
18
|
+
await input.slice(0, headerSize).arrayBuffer()
|
|
19
|
+
);
|
|
20
|
+
if (!metadataFitsInHeader(header, headerSize)) {
|
|
21
|
+
return { data: await readFileData(input), isPartiallyLoaded: false };
|
|
22
|
+
}
|
|
14
23
|
const footerStart = Math.max(0, input.size - footerSize);
|
|
15
|
-
const footer = await input.slice(footerStart).arrayBuffer();
|
|
24
|
+
const footer = new Uint8Array(await input.slice(footerStart).arrayBuffer());
|
|
25
|
+
if (!trailerFitsInFooter(footer, footerSize)) {
|
|
26
|
+
return { data: await readFileData(input), isPartiallyLoaded: false };
|
|
27
|
+
}
|
|
16
28
|
const combined = new Uint8Array(header.byteLength + footer.byteLength);
|
|
17
|
-
combined.set(
|
|
18
|
-
combined.set(
|
|
29
|
+
combined.set(header, 0);
|
|
30
|
+
combined.set(footer, header.byteLength);
|
|
19
31
|
return { data: combined, isPartiallyLoaded: true };
|
|
20
32
|
}
|
|
21
33
|
if (opts.partial && typeof input === "string") {
|
|
@@ -26,7 +38,9 @@ async function loadAudioData(input, opts) {
|
|
|
26
38
|
opts.maxHeaderSize,
|
|
27
39
|
opts.maxFooterSize
|
|
28
40
|
);
|
|
29
|
-
|
|
41
|
+
if (metadataFitsInHeader(data, opts.maxHeaderSize) && trailerFitsInFooter(data, opts.maxFooterSize)) {
|
|
42
|
+
return { data, isPartiallyLoaded: true };
|
|
43
|
+
}
|
|
30
44
|
}
|
|
31
45
|
return { data: await readFileData(input), isPartiallyLoaded: false };
|
|
32
46
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Is a file's metadata provably contained in its first N bytes?
|
|
3
|
+
*
|
|
4
|
+
* Partial loading (`TagLib.open(path, { partial: true })`) hands TagLib the
|
|
5
|
+
* file's first `maxHeaderSize` bytes concatenated with its last `maxFooterSize`
|
|
6
|
+
* bytes, discarding the middle. That is only sound when the metadata lives
|
|
7
|
+
* entirely inside the header window: otherwise the tag is cut mid-structure and
|
|
8
|
+
* unrelated footer bytes are spliced onto the cut, so TagLib parses whatever
|
|
9
|
+
* lands there. Measured on a real library, 18 of 40 large MP3s read back
|
|
10
|
+
* DIFFERENT metadata that way, and before taglib-f5hp the malformed image also
|
|
11
|
+
* tripped a double free that trapped the whole Wasm module (taglib-f5hp).
|
|
12
|
+
*
|
|
13
|
+
* So the decision is made before splicing, and the rule is deliberately
|
|
14
|
+
* asymmetric: return true ONLY when the metadata is provably contained. Anything
|
|
15
|
+
* unrecognised, malformed, or unbounded answers false, which costs a full read
|
|
16
|
+
* and never costs correctness.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* True only when the metadata of `header` provably ends within `limit` bytes.
|
|
20
|
+
*
|
|
21
|
+
* `header` is the start of the file and may be shorter than `limit`; anything
|
|
22
|
+
* this cannot prove — an unknown container, a truncated header, a malformed
|
|
23
|
+
* size — answers false so the caller reads the whole file.
|
|
24
|
+
*
|
|
25
|
+
* NOTE this judges the HEADER window only. Trailer metadata (ID3v1, APE) is the
|
|
26
|
+
* caller's business — see trailerFitsInFooter.
|
|
27
|
+
*/
|
|
28
|
+
export declare function metadataFitsInHeader(header: Uint8Array, limit: number): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* True when a file's TRAILER metadata provably fits in the last `footerSize`
|
|
31
|
+
* bytes, given `tail` — the end of the file, at least that long.
|
|
32
|
+
*
|
|
33
|
+
* Partial loading keeps the file's last `footerSize` bytes, so ID3v1 (always
|
|
34
|
+
* exactly 128 bytes) is safe by construction. APEv2 is not: it is unbounded
|
|
35
|
+
* because it can carry cover art, and an APE tag larger than the footer window
|
|
36
|
+
* is spliced so that TagLib computes its start inside the header window's audio
|
|
37
|
+
* and reads nothing. Measured: a 410 KB APE tag lost EVERY tag value silently,
|
|
38
|
+
* while a 41 KB one round-tripped.
|
|
39
|
+
*/
|
|
40
|
+
export declare function trailerFitsInFooter(tail: Uint8Array, footerSize: number): boolean;
|
|
41
|
+
//# sourceMappingURL=metadata-extent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata-extent.d.ts","sourceRoot":"","sources":["../../../src/taglib/metadata-extent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA2LH;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAiCT;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAmBT"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
function be32(b, offset) {
|
|
2
|
+
return (b[offset] << 24 | b[offset + 1] << 16 | b[offset + 2] << 8 | b[offset + 3]) >>> 0;
|
|
3
|
+
}
|
|
4
|
+
function startsWith(bytes, magic, at = 0) {
|
|
5
|
+
if (bytes.length < at + magic.length) return false;
|
|
6
|
+
for (let i = 0; i < magic.length; i++) {
|
|
7
|
+
if (bytes[at + i] !== magic.charCodeAt(i)) return false;
|
|
8
|
+
}
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
function id3v2End(bytes) {
|
|
12
|
+
if (bytes.length < 10) return void 0;
|
|
13
|
+
const size = bytes[6] << 21 | bytes[7] << 14 | bytes[8] << 7 | bytes[9];
|
|
14
|
+
const hasFooter = (bytes[5] & 16) !== 0;
|
|
15
|
+
return 10 + size + (hasFooter ? 10 : 0);
|
|
16
|
+
}
|
|
17
|
+
function flacEnd(bytes, limit, start) {
|
|
18
|
+
let offset = start + 4;
|
|
19
|
+
for (; ; ) {
|
|
20
|
+
if (offset + 4 > bytes.length) return void 0;
|
|
21
|
+
const isLast = (bytes[offset] & 128) !== 0;
|
|
22
|
+
const length = bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
|
|
23
|
+
offset += 4 + length;
|
|
24
|
+
if (offset > limit) return offset;
|
|
25
|
+
if (isLast) return offset;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function mp4MoovEnd(bytes, limit, start) {
|
|
29
|
+
let offset = start;
|
|
30
|
+
const readable = Math.min(bytes.length, limit);
|
|
31
|
+
while (offset + 8 <= readable) {
|
|
32
|
+
const size = be32(bytes, offset);
|
|
33
|
+
const type = String.fromCharCode(
|
|
34
|
+
bytes[offset + 4],
|
|
35
|
+
bytes[offset + 5],
|
|
36
|
+
bytes[offset + 6],
|
|
37
|
+
bytes[offset + 7]
|
|
38
|
+
);
|
|
39
|
+
if (size < 8) return void 0;
|
|
40
|
+
if (type === "moov") return offset + size;
|
|
41
|
+
offset += size;
|
|
42
|
+
}
|
|
43
|
+
return void 0;
|
|
44
|
+
}
|
|
45
|
+
function oggMetadataEnd(bytes, limit, start) {
|
|
46
|
+
let offset = start;
|
|
47
|
+
let packetsCompleted = 0;
|
|
48
|
+
let first = true;
|
|
49
|
+
for (; ; ) {
|
|
50
|
+
if (offset + 27 > bytes.length || !startsWith(bytes, "OggS", offset)) {
|
|
51
|
+
return void 0;
|
|
52
|
+
}
|
|
53
|
+
const segmentCount = bytes[offset + 26];
|
|
54
|
+
const tableStart = offset + 27;
|
|
55
|
+
if (tableStart + segmentCount > bytes.length) return void 0;
|
|
56
|
+
let payload = 0;
|
|
57
|
+
for (let i = 0; i < segmentCount; i++) {
|
|
58
|
+
const segment = bytes[tableStart + i];
|
|
59
|
+
payload += segment;
|
|
60
|
+
if (segment < 255) packetsCompleted++;
|
|
61
|
+
}
|
|
62
|
+
if (first) {
|
|
63
|
+
const payloadStart = tableStart + segmentCount;
|
|
64
|
+
if (payloadStart + 5 > bytes.length) return void 0;
|
|
65
|
+
if (bytes[payloadStart] === 127 && startsWith(bytes, "FLAC", payloadStart + 1)) {
|
|
66
|
+
return void 0;
|
|
67
|
+
}
|
|
68
|
+
first = false;
|
|
69
|
+
}
|
|
70
|
+
offset = tableStart + segmentCount + payload;
|
|
71
|
+
if (packetsCompleted >= 2 || offset > limit) return offset;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function isMpegFrameSync(bytes, at = 0) {
|
|
75
|
+
if (bytes.length < at + 4) return false;
|
|
76
|
+
if (bytes[at] !== 255 || (bytes[at + 1] & 224) !== 224) return false;
|
|
77
|
+
const version = bytes[at + 1] >> 3 & 3;
|
|
78
|
+
const layer = bytes[at + 1] >> 1 & 3;
|
|
79
|
+
if (version === 1 || layer === 0) return false;
|
|
80
|
+
const bitrateIndex = bytes[at + 2] >> 4 & 15;
|
|
81
|
+
const sampleRateIndex = bytes[at + 2] >> 2 & 3;
|
|
82
|
+
return bitrateIndex !== 0 && bitrateIndex !== 15 && sampleRateIndex !== 3;
|
|
83
|
+
}
|
|
84
|
+
function containerEnd(bytes, at, limit) {
|
|
85
|
+
if (at >= bytes.length) return bytes.length < limit ? null : void 0;
|
|
86
|
+
if (at + 8 > bytes.length) return void 0;
|
|
87
|
+
if (startsWith(bytes, "fLaC", at)) return flacEnd(bytes, limit, at);
|
|
88
|
+
if (startsWith(bytes, "ftyp", at + 4)) return mp4MoovEnd(bytes, limit, at);
|
|
89
|
+
if (startsWith(bytes, "OggS", at)) return oggMetadataEnd(bytes, limit, at);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function metadataFitsInHeader(header, limit) {
|
|
93
|
+
let end;
|
|
94
|
+
if (startsWith(header, "ID3")) {
|
|
95
|
+
const tagEnd = id3v2End(header);
|
|
96
|
+
if (tagEnd === void 0) return false;
|
|
97
|
+
end = tagEnd;
|
|
98
|
+
if (tagEnd <= limit) {
|
|
99
|
+
const behind = containerEnd(header, tagEnd, limit);
|
|
100
|
+
if (behind === void 0) return false;
|
|
101
|
+
if (behind !== null) end = behind;
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
const container = containerEnd(header, 0, limit);
|
|
105
|
+
if (container === null) {
|
|
106
|
+
end = isMpegFrameSync(header) ? 0 : void 0;
|
|
107
|
+
} else {
|
|
108
|
+
end = container;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (end === void 0 || end > header.length) return false;
|
|
112
|
+
return end <= limit;
|
|
113
|
+
}
|
|
114
|
+
function trailerFitsInFooter(tail, footerSize) {
|
|
115
|
+
if (tail.length < Math.min(footerSize, 32)) return false;
|
|
116
|
+
for (const offsetFromEnd of [32, 32 + 128]) {
|
|
117
|
+
const at = tail.length - offsetFromEnd;
|
|
118
|
+
if (at < 0 || !startsWith(tail, "APETAGEX", at)) continue;
|
|
119
|
+
const size = (tail[at + 12] | tail[at + 13] << 8 | tail[at + 14] << 16 | tail[at + 15] << 24) >>> 0;
|
|
120
|
+
const trailing = offsetFromEnd === 32 ? 0 : 128;
|
|
121
|
+
if (size + 32 + trailing > footerSize) return false;
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
metadataFitsInHeader,
|
|
127
|
+
trailerFitsInFooter
|
|
128
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reserved property-map key carrying exact MP4 atom names to the C++ write path.
|
|
3
|
+
*
|
|
4
|
+
* NOT a property and NOT handle state: it is a write-time directive recomputed at
|
|
5
|
+
* each write site from the PROPERTIES table, so it needs no entry in the
|
|
6
|
+
* extra-state reconstruct registry — but every write site that builds a property
|
|
7
|
+
* map must add it, or MP4 freeform atoms get TagLib's upper-cased name
|
|
8
|
+
* (taglib-bnhl). Sites: `BaseAudioFileImpl.setProperties`/`setProperty` and the
|
|
9
|
+
* `saveToFile` reconstruct in `save-reconstruct.ts`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const MP4_ITEM_NAMES_KEY = "_mp4ItemNames";
|
|
12
|
+
//# sourceMappingURL=mp4-item-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mp4-item-names.d.ts","sourceRoot":"","sources":["../../../src/taglib/mp4-item-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The mutable `tag()` facade over a FileHandle.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from audio-file-base.ts, which the additions for taglib-qpl/bnhl/eq3
|
|
5
|
+
* pushed past CLAUDE.md's 250-line limit. This is the whole of the basic-tag
|
|
6
|
+
* surface: five plain string fields plus the two that are backed by a raw
|
|
7
|
+
* string with a numeric mirror (`date`/`year`, `trackNumber`/`track`), whose
|
|
8
|
+
* write rules are the interesting part.
|
|
9
|
+
*/
|
|
10
|
+
import type { FileHandle } from "../wasm.js";
|
|
11
|
+
import type { MutableTag } from "./mutable-tag.js";
|
|
12
|
+
/** Build the mutable tag facade. Reads re-snapshot after every write. */
|
|
13
|
+
export declare function buildMutableTag(handle: FileHandle): MutableTag;
|
|
14
|
+
//# sourceMappingURL=mutable-tag-impl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mutable-tag-impl.d.ts","sourceRoot":"","sources":["../../../src/taglib/mutable-tag-impl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CA2F9D"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { toTagLibKey } from "../constants/properties.js";
|
|
2
|
+
const RAW_TRACK_WIRE_KEY = toTagLibKey("trackNumber");
|
|
3
|
+
function buildMutableTag(handle) {
|
|
4
|
+
let data = handle.getTagData();
|
|
5
|
+
const tag = {
|
|
6
|
+
get title() {
|
|
7
|
+
return data.title;
|
|
8
|
+
},
|
|
9
|
+
get artist() {
|
|
10
|
+
return data.artist;
|
|
11
|
+
},
|
|
12
|
+
get album() {
|
|
13
|
+
return data.album;
|
|
14
|
+
},
|
|
15
|
+
get comment() {
|
|
16
|
+
return data.comment;
|
|
17
|
+
},
|
|
18
|
+
get genre() {
|
|
19
|
+
return data.genre;
|
|
20
|
+
},
|
|
21
|
+
get year() {
|
|
22
|
+
return data.year;
|
|
23
|
+
},
|
|
24
|
+
get track() {
|
|
25
|
+
return data.track;
|
|
26
|
+
},
|
|
27
|
+
get date() {
|
|
28
|
+
return handle.getProperty("DATE") || void 0;
|
|
29
|
+
},
|
|
30
|
+
setTitle: (value) => {
|
|
31
|
+
handle.setTagData({ title: value });
|
|
32
|
+
data = handle.getTagData();
|
|
33
|
+
return tag;
|
|
34
|
+
},
|
|
35
|
+
setArtist: (value) => {
|
|
36
|
+
handle.setTagData({ artist: value });
|
|
37
|
+
data = handle.getTagData();
|
|
38
|
+
return tag;
|
|
39
|
+
},
|
|
40
|
+
setAlbum: (value) => {
|
|
41
|
+
handle.setTagData({ album: value });
|
|
42
|
+
data = handle.getTagData();
|
|
43
|
+
return tag;
|
|
44
|
+
},
|
|
45
|
+
setComment: (value) => {
|
|
46
|
+
handle.setTagData({ comment: value });
|
|
47
|
+
data = handle.getTagData();
|
|
48
|
+
return tag;
|
|
49
|
+
},
|
|
50
|
+
setGenre: (value) => {
|
|
51
|
+
handle.setTagData({ genre: value });
|
|
52
|
+
data = handle.getTagData();
|
|
53
|
+
return tag;
|
|
54
|
+
},
|
|
55
|
+
setYear: (value) => {
|
|
56
|
+
handle.setTagData({ year: value });
|
|
57
|
+
data = handle.getTagData();
|
|
58
|
+
return tag;
|
|
59
|
+
},
|
|
60
|
+
setTrack: (value) => {
|
|
61
|
+
const existing = value > 0 ? handle.getProperty(RAW_TRACK_WIRE_KEY) : "";
|
|
62
|
+
const slash = existing.indexOf("/");
|
|
63
|
+
if (slash !== -1) {
|
|
64
|
+
handle.setProperty(
|
|
65
|
+
RAW_TRACK_WIRE_KEY,
|
|
66
|
+
`${value}${existing.slice(slash)}`
|
|
67
|
+
);
|
|
68
|
+
} else {
|
|
69
|
+
handle.setTagData({ track: value });
|
|
70
|
+
}
|
|
71
|
+
data = handle.getTagData();
|
|
72
|
+
return tag;
|
|
73
|
+
},
|
|
74
|
+
setDate: (value) => {
|
|
75
|
+
if (value === "") {
|
|
76
|
+
handle.setTagData({ year: 0 });
|
|
77
|
+
} else {
|
|
78
|
+
handle.setProperty("DATE", value);
|
|
79
|
+
}
|
|
80
|
+
data = handle.getTagData();
|
|
81
|
+
return tag;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
return tag;
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
buildMutableTag
|
|
88
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"save-reconstruct.d.ts","sourceRoot":"","sources":["../../../src/taglib/save-reconstruct.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"save-reconstruct.d.ts","sourceRoot":"","sources":["../../../src/taglib/save-reconstruct.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA6C3D;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,EACnB,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,EAChD,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,OAAO,EACvB,WAAW,GAAE,WAAW,CAAC,MAAM,CAAa,GAC3C,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
|
|
@@ -3,10 +3,14 @@ import { readFileData } from "../utils/file.js";
|
|
|
3
3
|
import { writeFileData } from "../utils/write.js";
|
|
4
4
|
import { wrapEmbindHandle } from "./embind-adapter.js";
|
|
5
5
|
import { copyExtraState } from "./extra-state-registry.js";
|
|
6
|
+
import { mp4FreeformAtomNames } from "../constants/properties.js";
|
|
7
|
+
import { MP4_ITEM_NAMES_KEY } from "./mp4-item-names.js";
|
|
6
8
|
function copyEditedState(target, source, sourceComplete, deletedKeys) {
|
|
7
9
|
target.setTagData(source.getTagData());
|
|
8
10
|
const merged = sourceComplete ? source.getProperties() : { ...target.getProperties(), ...source.getProperties() };
|
|
9
11
|
for (const key of deletedKeys) delete merged[key];
|
|
12
|
+
const atomNames = mp4FreeformAtomNames(Object.keys(merged));
|
|
13
|
+
if (atomNames.length > 0) merged[MP4_ITEM_NAMES_KEY] = atomNames;
|
|
10
14
|
target.setProperties(merged);
|
|
11
15
|
copyExtraState(target, source, sourceComplete);
|
|
12
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata-mappings.d.ts","sourceRoot":"","sources":["../../../src/types/metadata-mappings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,OAAO,CACL,MAAM,WAAW,EACf,UAAU,GACV,SAAS,GACT,QAAQ,GACR,UAAU,GACV,MAAM,GACN,UAAU,GACV,MAAM,GACN,aAAa,CAChB,EACD,YAAY,
|
|
1
|
+
{"version":3,"file":"metadata-mappings.d.ts","sourceRoot":"","sources":["../../../src/types/metadata-mappings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,YAAY;IAC3B,wBAAwB;IACxB,KAAK,CAAC,EAAE;QACN,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IACF,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CACpC,OAAO,CACL,MAAM,WAAW,EACf,UAAU,GACV,SAAS,GACT,QAAQ,GACR,UAAU,GACV,MAAM,GACN,UAAU,GACV,MAAM,GACN,aAAa,CAChB,EACD,YAAY,CA8Nb,CAAC"}
|
|
@@ -50,6 +50,14 @@ const METADATA_MAPPINGS = {
|
|
|
50
50
|
mp4: "trkn",
|
|
51
51
|
wav: "ITRK"
|
|
52
52
|
},
|
|
53
|
+
// Raw track string — same underlying frames as `track`, but preserves
|
|
54
|
+
// zero-padding and the "/total" suffix instead of collapsing to an integer.
|
|
55
|
+
trackNumber: {
|
|
56
|
+
id3v2: { frame: "TRCK" },
|
|
57
|
+
vorbis: "TRACKNUMBER",
|
|
58
|
+
mp4: "trkn",
|
|
59
|
+
wav: "ITRK"
|
|
60
|
+
},
|
|
53
61
|
// Advanced fields requiring format-specific handling
|
|
54
62
|
acoustidFingerprint: {
|
|
55
63
|
id3v2: { frame: "TXXX", description: "Acoustid Fingerprint" },
|
|
@@ -206,6 +214,12 @@ const METADATA_MAPPINGS = {
|
|
|
206
214
|
vorbis: "ITUNNORM",
|
|
207
215
|
// Some tools store it in Vorbis comments too
|
|
208
216
|
mp4: "----:com.apple.iTunes:iTunNORM"
|
|
217
|
+
},
|
|
218
|
+
// Same underlying frames pattern as appleSoundCheck, for the sibling atom.
|
|
219
|
+
appleGaplessInfo: {
|
|
220
|
+
id3v2: { frame: "TXXX", description: "iTunSMPB" },
|
|
221
|
+
vorbis: "ITUNSMPB",
|
|
222
|
+
mp4: "----:com.apple.iTunes:iTunSMPB"
|
|
209
223
|
}
|
|
210
224
|
};
|
|
211
225
|
export {
|
package/dist/src/types/tags.d.ts
CHANGED
|
@@ -77,7 +77,14 @@ export interface TagInput {
|
|
|
77
77
|
readonly date?: string | string[];
|
|
78
78
|
/** Track number */
|
|
79
79
|
readonly track?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Raw track field (e.g. "03" or "3/12"). Maps to the same TRACKNUMBER tag as
|
|
82
|
+
* {@link track}; when both are provided, `trackNumber` wins (it preserves
|
|
83
|
+
* zero-padding and the "/total" suffix that a number cannot).
|
|
84
|
+
*/
|
|
85
|
+
readonly trackNumber?: string | string[];
|
|
80
86
|
readonly appleSoundCheck?: string | string[];
|
|
87
|
+
readonly appleGaplessInfo?: string | string[];
|
|
81
88
|
readonly albumArtist?: string | string[];
|
|
82
89
|
readonly composer?: string | string[];
|
|
83
90
|
readonly conductor?: string | string[];
|
|
@@ -135,6 +142,13 @@ export interface ExtendedTag extends Tag {
|
|
|
135
142
|
* Preserves day/month precision that the numeric {@link Tag.year} cannot.
|
|
136
143
|
*/
|
|
137
144
|
readonly date?: string | string[];
|
|
145
|
+
/**
|
|
146
|
+
* Raw track field as stored in TRACKNUMBER (e.g. "03" or "3/12"). Preserves
|
|
147
|
+
* the zero-padding and the "/total" suffix that the numeric
|
|
148
|
+
* {@link Tag.track} cannot, so a readTags() -> applyTags() round-trip does
|
|
149
|
+
* not silently drop them (taglib-qpl).
|
|
150
|
+
*/
|
|
151
|
+
readonly trackNumber?: string | string[];
|
|
138
152
|
/** AcoustID fingerprint (Chromaprint) */
|
|
139
153
|
readonly acoustidFingerprint?: string[];
|
|
140
154
|
/** AcoustID UUID */
|
|
@@ -203,6 +217,8 @@ export interface ExtendedTag extends Tag {
|
|
|
203
217
|
readonly replayGainAlbumPeak?: string[];
|
|
204
218
|
/** Apple Sound Check normalization data (iTunNORM) */
|
|
205
219
|
readonly appleSoundCheck?: string[];
|
|
220
|
+
/** Apple gapless playback data: encoder delay and padding (iTunSMPB) */
|
|
221
|
+
readonly appleGaplessInfo?: string[];
|
|
206
222
|
/** Embedded pictures/artwork */
|
|
207
223
|
readonly pictures?: import("./pictures.js").Picture[];
|
|
208
224
|
/** Popularity/rating data. Same shape as {@link AudioFile.getRatings}. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,GAAG;IAClB,kBAAkB;IAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,kBAAkB;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB;IACjB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,YAAY;IACZ,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW;IACX,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,QAAQ;IACvB,kBAAkB;IAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACnC,kBAAkB;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,iBAAiB;IACjB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACnC,cAAc;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACrC,YAAY;IACZ,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACnC,WAAW;IACX,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAClC,mBAAmB;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"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"}
|