@h3l1os/mp4vault 2.0.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/src/jspack.js ADDED
@@ -0,0 +1,319 @@
1
+ /* eslint-disable */
2
+ // https://github.com/AndreasAntener/node-jspack
3
+ /* !
4
+ * Copyright © 2008 Fair Oaks Labs, Inc.
5
+ * All rights reserved.
6
+ */
7
+
8
+ // Utility object: Encode / Decode C-style binary primitives to / from octet arrays
9
+ function JSPack() {
10
+ // Module-level (private) variables
11
+ var el;
12
+ var bBE = false;
13
+ var m = this;
14
+
15
+ // Raw byte arrays
16
+ m._DeArray = function (a, p, l) {
17
+ return [a.slice(p, p + l)];
18
+ };
19
+
20
+ m._EnArray = function (a, p, l, v) {
21
+ for (var i = 0; i < l; a[p + i] = v[i] ? v[i] : 0, i++) { }
22
+ };
23
+
24
+ // ASCII characters
25
+ m._DeChar = function (a, p) {
26
+ return String.fromCharCode(a[p]);
27
+ };
28
+
29
+ m._EnChar = function (a, p, v) {
30
+ a[p] = v.charCodeAt(0);
31
+ };
32
+
33
+ // Little-endian (un)signed N-byte integers
34
+ m._DeInt = function (a, p) {
35
+ var lsb = bBE ? (el.len - 1) : 0;
36
+ var nsb = bBE ? -1 : 1;
37
+ var stop = lsb + nsb * el.len;
38
+ var rv, i, f;
39
+ for (rv = 0, i = lsb, f = 1; i != stop; rv += (a[p + i] * f), i += nsb, f *= 256) { }
40
+ if (el.bSigned && (rv & Math.pow(2, el.len * 8 - 1))) { rv -= Math.pow(2, el.len * 8); }
41
+ return rv;
42
+ };
43
+
44
+ m._EnInt = function (a, p, v) {
45
+ var lsb = bBE ? (el.len - 1) : 0;
46
+ var nsb = bBE ? -1 : 1;
47
+ var stop = lsb + nsb * el.len;
48
+ var i;
49
+ v = (v < el.min) ? el.min : (v > el.max) ? el.max : v;
50
+ for (i = lsb; i != stop; a[p + i] = v & 0xff, i += nsb, v >>= 8) { }
51
+ };
52
+
53
+ // ASCII character strings
54
+ m._DeString = function (a, p, l) {
55
+ for (var rv = new Array(l), i = 0; i < l; rv[i] = String.fromCharCode(a[p + i]), i++) { }
56
+ return rv.join('');
57
+ };
58
+ m._EnString = function (a, p, l, v) {
59
+
60
+ for (var t, i = 0; i < l; a[p + i] = (t = v.charCodeAt(i)) ? t : 0, i++) { }
61
+ };
62
+
63
+ // Little-endian N-bit IEEE 754 floating point
64
+ m._De754 = function (a, p) {
65
+ var s, e, m, i, d, nBits, mLen, eLen, eBias, eMax;
66
+ mLen = el.mLen, eLen = el.len * 8 - el.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;
67
+
68
+ i = bBE ? 0 : (el.len - 1); d = bBE ? 1 : -1; s = a[p + i]; i += d; nBits = -7;
69
+ for (e = s & ((1 << (-nBits)) - 1), s >>= (-nBits), nBits += eLen;
70
+ nBits > 0; e = e * 256 + a[p + i], i += d, nBits -= 8) { }
71
+ for (m = e & ((1 << (-nBits)) - 1), e >>= (-nBits), nBits += mLen; nBits > 0;
72
+ m = m * 256 + a[p + i], i += d, nBits -= 8) { }
73
+
74
+ switch (e) {
75
+
76
+ case 0:
77
+ // Zero, or denormalized number
78
+ e = 1 - eBias;
79
+ break;
80
+ case eMax:
81
+ // NaN, or +/-Infinity
82
+ return m ? NaN : ((s ? -1 : 1) * Infinity);
83
+ default:
84
+ // Normalized number
85
+ m = m + Math.pow(2, mLen);
86
+ e = e - eBias;
87
+ break;
88
+ }
89
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen);
90
+ };
91
+ m._En754 = function (a, p, v) {
92
+
93
+ var s, e, m, i, d, c, mLen, eLen, eBias, eMax;
94
+ mLen = el.mLen, eLen = el.len * 8 - el.mLen - 1, eMax = (1 << eLen) - 1, eBias = eMax >> 1;
95
+
96
+ s = v < 0 ? 1 : 0;
97
+ v = Math.abs(v);
98
+ if (isNaN(v) || (v == Infinity)) {
99
+
100
+ m = isNaN(v) ? 1 : 0;
101
+ e = eMax;
102
+ }
103
+ else {
104
+ e = Math.floor(Math.log(v) / Math.LN2); // Calculate log2 of the value
105
+ if (v * (c = Math.pow(2, -e)) < 1) { e--; c *= 2; } // Math.log() isn't 100% reliable
106
+
107
+ // Round by adding 1 / 2 the significand's LSD
108
+ if (e + eBias >= 1) {
109
+ // Normalized: mLen significand digits
110
+ v += el.rt / c;
111
+ }
112
+ else {
113
+ // Denormalized: <= mLen significand digits
114
+ v += el.rt * Math.pow(2, 1 - eBias);
115
+ }
116
+ if (v * c >= 2) {
117
+ // Rounding can increment the exponent
118
+ e++; c /= 2;
119
+ }
120
+
121
+ if (e + eBias >= eMax) {
122
+ // Overflow
123
+ m = 0;
124
+ e = eMax;
125
+ }
126
+ else if (e + eBias >= 1) {
127
+
128
+ // Normalized - term order matters, as Math.pow(2, 52 - e) and v * Math.pow(2, 52) can overflow
129
+ m = (v * c - 1) * Math.pow(2, mLen);
130
+ e = e + eBias;
131
+ }
132
+ else {
133
+
134
+ // Denormalized - also catches the '0' case, somewhat by chance
135
+ m = v * Math.pow(2, eBias - 1) * Math.pow(2, mLen);
136
+ e = 0;
137
+ }
138
+ }
139
+
140
+ for (i = bBE ? (el.len - 1) : 0, d = bBE ? -1 : 1; mLen >= 8; a[p + i] = m & 0xff, i += d, m /= 256, mLen -= 8) { }
141
+ for (e = (e << mLen) | m, eLen += mLen; eLen > 0; a[p + i] = e & 0xff, i += d, e /= 256, eLen -= 8) { }
142
+ a[p + i - d] |= s * 128;
143
+ };
144
+
145
+ // Convert int64 to array with 3 elements: [lowBits, highBits, unsignedFlag]
146
+ // '>>>' trick to convert signed 32bit int to unsigned int (because << always results in a signed 32bit int)
147
+ m._DeInt64 = function (a, p) {
148
+ var start = bBE ? 0 : 7;
149
+ var nsb = bBE ? 1 : -1;
150
+ var stop = start + nsb * 8;
151
+ var rv = [0, 0, !el.bSigned];
152
+ var i, f, rvi;
153
+ for (i = start, rvi = 1, f = 0;
154
+ i != stop;
155
+ rv[rvi] = (((rv[rvi] << 8) >>> 0) + a[p + i]), i += nsb, f++, rvi = (f < 4 ? 1 : 0)) {}
156
+
157
+ return rv[0] + rv[1]*4294967296;
158
+ // return rv;
159
+ };
160
+
161
+ m._EnInt64 = function (a, p, v) {
162
+ if (!Array.isArray(v)) {
163
+ // it's Number, so lets split it to array
164
+ v = [
165
+ v % 4294967296,
166
+ Math.floor(v / 4294967296),
167
+ 1];
168
+ }
169
+
170
+ var start = bBE ? 0 : 7;
171
+ var nsb = bBE ? 1 : -1;
172
+ var stop = start + nsb * 8;
173
+ var i, f, rvi, s;
174
+ for (i = start, rvi = 1, f = 0, s = 24;
175
+ i != stop;
176
+ a[p + i] = v[rvi] >> s & 0xff, i += nsb, f++, rvi = (f < 4 ? 1 : 0), s = 24 - (8 * (f % 4))) { }
177
+ };
178
+
179
+ // Class data
180
+ m._sPattern = '(\\d+)?([AxcbBhHsfdiIlLqQ])';
181
+ m._lenLut = {'A': 1, 'x': 1, 'c': 1, 'b': 1, 'B': 1, 'h': 2, 'H': 2, 's': 1,
182
+ 'f': 4, 'd': 8, 'i': 4, 'I': 4, 'l': 4, 'L': 4, 'q': 8, 'Q': 8
183
+ };
184
+ m._elLut = {'A': {en: m._EnArray, de: m._DeArray},
185
+ 's': {en: m._EnString, de: m._DeString},
186
+ 'c': {en: m._EnChar, de: m._DeChar},
187
+ 'b': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: true, min: -Math.pow(2, 7), max: Math.pow(2, 7) - 1},
188
+ 'B': {en: m._EnInt, de: m._DeInt, len: 1, bSigned: false, min: 0, max: Math.pow(2, 8) - 1},
189
+ 'h': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: true, min: -Math.pow(2, 15), max: Math.pow(2, 15) - 1},
190
+ 'H': {en: m._EnInt, de: m._DeInt, len: 2, bSigned: false, min: 0, max: Math.pow(2, 16) - 1},
191
+ 'i': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
192
+ 'I': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
193
+ 'l': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: true, min: -Math.pow(2, 31), max: Math.pow(2, 31) - 1},
194
+ 'L': {en: m._EnInt, de: m._DeInt, len: 4, bSigned: false, min: 0, max: Math.pow(2, 32) - 1},
195
+ 'f': {en: m._En754, de: m._De754, len: 4, mLen: 23, rt: Math.pow(2, -24) - Math.pow(2, -77)},
196
+ 'd': {en: m._En754, de: m._De754, len: 8, mLen: 52, rt: 0},
197
+ 'q': {en: m._EnInt64, de: m._DeInt64, bSigned: true},
198
+ 'Q': {en: m._EnInt64, de: m._DeInt64, bSigned: false}};
199
+
200
+ // Unpack a series of n elements of size s from array a at offset p with fxn
201
+ m._UnpackSeries = function (n, s, a, p) {
202
+ for (var fxn = el.de, rv = [], i = 0; i < n; rv.push(fxn(a, p + i * s)), i++) { }
203
+ return rv;
204
+ };
205
+
206
+ // Pack a series of n elements of size s from array v at offset i to array a at offset p with fxn
207
+ m._PackSeries = function (n, s, a, p, v, i) {
208
+ for (var fxn = el.en, o = 0; o < n; fxn(a, p + o * s, v[i + o]), o++) { }
209
+ };
210
+
211
+ // Unpack the octet array a, beginning at offset p, according to the fmt string
212
+ m.UnpackTo = function (fmt, a, p, allowLessData = false) {
213
+ // Set the private bBE flag based on the format string - assume big-endianness
214
+ bBE = (fmt.charAt(0) != '<');
215
+
216
+ p = p ? p : 0;
217
+ var re = new RegExp(this._sPattern, 'g');
218
+ var m, n, s;
219
+ var rv = [];
220
+ while (m = re.exec(fmt)) {
221
+ n = ((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1]);
222
+ s = this._lenLut[m[2]];
223
+ if ((p + n * s) > a.length) {
224
+ if (allowLessData) {
225
+ // We stop here and return what we have already,
226
+ // or an empty array if this happens in the first iteration.
227
+ break;
228
+ } else {
229
+ return undefined;
230
+ }
231
+ }
232
+ switch (m[2]) {
233
+ case 'A': case 's':
234
+ rv.push(this._elLut[m[2]].de(a, p, n));
235
+ break;
236
+ case 'c': case 'b': case 'B': case 'h': case 'H':
237
+ case 'i': case 'I': case 'l': case 'L': case 'f': case 'd': case 'q': case 'Q':
238
+ el = this._elLut[m[2]];
239
+ if (n > 1) {
240
+ // Field is array, unpack into separate array and push as such
241
+ var arr = [];
242
+ arr.push(this._UnpackSeries(n, s, a, p));
243
+ rv.push(arr);
244
+ } else {
245
+ rv.push(this._UnpackSeries(n, s, a, p));
246
+ }
247
+ break;
248
+ }
249
+ p += n * s;
250
+ }
251
+ return Array.prototype.concat.apply([], rv);
252
+ };
253
+
254
+ // Pack the supplied values into the octet array a, beginning at offset p, according to the fmt string
255
+ m.PackTo = function (fmt, a, p, values, allowLessData) {
256
+ // Set the private bBE flag based on the format string - assume big-endianness
257
+ bBE = (fmt.charAt(0) != '<');
258
+
259
+ var re = new RegExp(this._sPattern, 'g');
260
+ var m, n, s, j;
261
+ var i = 0;
262
+ while (m = re.exec(fmt)) {
263
+ n = ((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1]);
264
+ s = this._lenLut[m[2]];
265
+ if ((p + n * s) > a.length) {
266
+ // this should not happen unless CalcLength() is broken
267
+ return false;
268
+ }
269
+ switch (m[2]) {
270
+ case 'A': case 's':
271
+ if ((i + 1) > values.length) { return allowLessData ? a.slice(0, p) : false; }
272
+ this._elLut[m[2]].en(a, p, n, values[i]);
273
+ i += 1;
274
+ break;
275
+ case 'c': case 'b': case 'B': case 'h': case 'H':
276
+ case 'i': case 'I': case 'l': case 'L': case 'f': case 'd': case 'q': case 'Q':
277
+ el = this._elLut[m[2]];
278
+ if (n > 1 && Array.isArray(values[i])) {
279
+ // Value series is array, iterate through that, only increment by 1
280
+ if ((i + 1) > values.length) { return allowLessData ? a.slice(0, p) : false; }
281
+ this._PackSeries(n, s, a, p, values[i], 0);
282
+ i += 1;
283
+ } else {
284
+ if ((i + n) > values.length) { return allowLessData ? a.slice(0, p) : false; }
285
+ this._PackSeries(n, s, a, p, values, i);
286
+ i += n;
287
+ }
288
+ break;
289
+ case 'x':
290
+ for (j = 0; j < n; j++) { a[p + j] = 0; }
291
+ break;
292
+ }
293
+ p += n * s;
294
+ }
295
+ return a;
296
+ };
297
+
298
+ // Pack the supplied values into a new octet array, according to the fmt string
299
+ m.Pack = function (fmt, values, allowLessData = false) {
300
+ return this.PackTo(fmt, new Array(this.CalcLength(fmt)), 0, values, allowLessData);
301
+ };
302
+
303
+ m.Unpack = function (fmt, values, allowLessData = false) {
304
+ return this.UnpackTo(fmt, values, 0, allowLessData);
305
+ };
306
+
307
+ // Determine the number of bytes represented by the format string
308
+ m.CalcLength = function (fmt) {
309
+ var re = new RegExp(this._sPattern, 'g');
310
+ var sum = 0;
311
+ var m;
312
+ while (m = re.exec(fmt)) {
313
+ sum += (((m[1] == undefined) || (m[1] == '')) ? 1 : parseInt(m[1])) * this._lenLut[m[2]];
314
+ }
315
+ return sum;
316
+ };
317
+ }
318
+
319
+ export default new JSPack();
@@ -0,0 +1,61 @@
1
+ import fs from 'fs/promises';
2
+ import type { FileHandle } from 'fs/promises';
3
+ import type { IReadable } from '../types.js';
4
+
5
+ export class Readable implements IReadable {
6
+ private _filename: string | undefined;
7
+ private _prepared = false;
8
+ private _size = 0;
9
+ private _fp: FileHandle | null = null;
10
+
11
+ constructor(params: { filename?: string } = {}) {
12
+ this._filename = params.filename;
13
+ }
14
+
15
+ isPrepared(): boolean {
16
+ return this._prepared;
17
+ }
18
+
19
+ async prepare(): Promise<void> {
20
+ if (this._prepared) {
21
+ return;
22
+ }
23
+
24
+ if (this._filename) {
25
+ this._fp = await fs.open(this._filename, 'r');
26
+ const stats = await this._fp.stat();
27
+ this._size = stats.size;
28
+ }
29
+
30
+ this._prepared = true;
31
+ }
32
+
33
+ async close(): Promise<void> {
34
+ if (this._fp) {
35
+ try {
36
+ await this._fp.close();
37
+ } catch {
38
+ // file may already be closed
39
+ }
40
+ }
41
+ this._fp = null;
42
+ this._prepared = false;
43
+ }
44
+
45
+ async getSlice(offset: number, length: number): Promise<Uint8Array> {
46
+ if (!this._prepared) {
47
+ await this.prepare();
48
+ }
49
+
50
+ const ret = new Uint8Array(length);
51
+ await this._fp!.read(ret, 0, length, offset);
52
+ return ret;
53
+ }
54
+
55
+ async size(): Promise<number> {
56
+ if (!this._prepared) {
57
+ await this.prepare();
58
+ }
59
+ return this._size;
60
+ }
61
+ }
@@ -0,0 +1,85 @@
1
+ import fs from 'fs/promises';
2
+ import { tmpFileSync } from '../utils.js';
3
+ import type { FileHandle } from 'fs/promises';
4
+ import type { IReadable, IWritable } from '../types.js';
5
+ import { Readable } from './Readable.js';
6
+
7
+ export class Writable implements IWritable {
8
+ private _uint8Array: Uint8Array = new Uint8Array([]);
9
+ private _filename: string | undefined;
10
+ private _prepared = false;
11
+ private _bytesWrote = 0;
12
+ private _fp: FileHandle | null = null;
13
+
14
+ constructor(params: { filename?: string } = {}) {
15
+ if (params.filename) {
16
+ this._filename = params.filename;
17
+ }
18
+ }
19
+
20
+ size(): number {
21
+ if (this._filename) {
22
+ return this._bytesWrote;
23
+ }
24
+ return this._uint8Array.length;
25
+ }
26
+
27
+ async prepare(): Promise<void> {
28
+ if (this._prepared) {
29
+ return;
30
+ }
31
+
32
+ if (this._filename) {
33
+ this._fp = await fs.open(this._filename, 'w');
34
+ }
35
+
36
+ this._prepared = true;
37
+ }
38
+
39
+ async close(): Promise<void> {
40
+ if (this._fp) {
41
+ try {
42
+ await this._fp.close();
43
+ } catch {
44
+ // file may already be closed
45
+ }
46
+ this._fp = null;
47
+ this._prepared = false;
48
+ }
49
+ }
50
+
51
+ async saveToFile(filename: string): Promise<void> {
52
+ await fs.writeFile(filename, this._uint8Array);
53
+ }
54
+
55
+ async write(append: Uint8Array | number[]): Promise<void> {
56
+ if (!this._prepared) {
57
+ await this.prepare();
58
+ }
59
+
60
+ const data = append instanceof Uint8Array ? append : Uint8Array.from(append);
61
+
62
+ if (this._fp) {
63
+ await this._fp.write(data, 0, data.length);
64
+ this._bytesWrote += data.length;
65
+ } else {
66
+ const ret = new Uint8Array(this._uint8Array.length + data.length);
67
+ ret.set(this._uint8Array, 0);
68
+ ret.set(data, this._uint8Array.length);
69
+ this._bytesWrote += data.length;
70
+ this._uint8Array = ret;
71
+ }
72
+ }
73
+
74
+ async toReadable(): Promise<IReadable> {
75
+ if (this._filename) {
76
+ await this.close();
77
+ return new Readable({ filename: this._filename });
78
+ } else {
79
+ const tmpName = tmpFileSync();
80
+ await this.saveToFile(tmpName);
81
+ await this.close();
82
+ return new Readable({ filename: tmpName });
83
+ }
84
+ }
85
+ }
package/src/types.ts ADDED
@@ -0,0 +1,44 @@
1
+ export interface IReadable {
2
+ isPrepared(): boolean;
3
+ prepare(): Promise<void>;
4
+ close(): Promise<void>;
5
+ getSlice(offset: number, length: number): Promise<Uint8Array>;
6
+ size(): Promise<number>;
7
+ }
8
+
9
+ export interface IWritable {
10
+ size(): number;
11
+ prepare(): Promise<void>;
12
+ close(): Promise<void>;
13
+ write(data: Uint8Array | number[]): Promise<void>;
14
+ saveToFile(filename: string): Promise<void>;
15
+ toReadable(): Promise<IReadable>;
16
+ }
17
+
18
+ export interface AtomParams {
19
+ readable?: IReadable;
20
+ name: string;
21
+ start: number;
22
+ size: number;
23
+ header_size: number;
24
+ mother?: Atom | null;
25
+ }
26
+
27
+ export interface FileRecord {
28
+ filename: string;
29
+ size: number;
30
+ meta?: Record<string, unknown>;
31
+ isEncrypted?: boolean;
32
+ offset?: number;
33
+ }
34
+
35
+ export interface EmbedFileParams {
36
+ filename?: string;
37
+ file?: unknown;
38
+ meta?: Record<string, unknown>;
39
+ key?: Buffer | null;
40
+ password?: string | null;
41
+ }
42
+
43
+ // Forward declaration to avoid circular imports
44
+ import type { Atom } from './Atom.js';
package/src/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import tmp from 'tmp';
2
+
3
+ export function tmpFileSync(): string {
4
+ const tmpobj = tmp.fileSync();
5
+ return tmpobj.name;
6
+ }