file-type 7.7.0 → 9.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/index.js +109 -54
- package/package.json +2 -2
- package/readme.md +5 -1
package/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const toBytes = s =>
|
|
2
|
+
const toBytes = s => [...s].map(c => c.charCodeAt(0));
|
|
3
3
|
const xpiZipFilename = toBytes('META-INF/mozilla.rsa');
|
|
4
4
|
const oxmlContentTypes = toBytes('[Content_Types].xml');
|
|
5
5
|
const oxmlRels = toBytes('_rels/.rels');
|
|
6
6
|
|
|
7
7
|
module.exports = input => {
|
|
8
|
-
const buf =
|
|
8
|
+
const buf = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
9
9
|
|
|
10
10
|
if (!(buf && buf.length > 1)) {
|
|
11
11
|
return null;
|
|
@@ -151,41 +151,54 @@ module.exports = input => {
|
|
|
151
151
|
};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
154
|
+
// The docx, xlsx and pptx file types extend the Office Open XML file format:
|
|
155
|
+
// https://en.wikipedia.org/wiki/Office_Open_XML_file_formats
|
|
156
|
+
// We look for:
|
|
157
|
+
// - one entry named '[Content_Types].xml' or '_rels/.rels',
|
|
158
|
+
// - one entry indicating specific type of file.
|
|
159
|
+
// MS Office, OpenOffice and LibreOffice may put the parts in different order, so the check should not rely on it.
|
|
160
|
+
const findNextZipHeaderIndex = (arr, startAt = 0) => arr.findIndex((el, i, arr) => i >= startAt && arr[i] === 0x50 && arr[i + 1] === 0x4B && arr[i + 2] === 0x3 && arr[i + 3] === 0x4);
|
|
161
|
+
|
|
162
|
+
let zipHeaderIndex = 0; // The first zip header was already found at index 0
|
|
163
|
+
let oxmlFound = false;
|
|
164
|
+
let type = null;
|
|
165
|
+
|
|
166
|
+
do {
|
|
167
|
+
const offset = zipHeaderIndex + 30;
|
|
168
|
+
|
|
169
|
+
if (!oxmlFound) {
|
|
170
|
+
oxmlFound = (check(oxmlContentTypes, {offset}) || check(oxmlRels, {offset}));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!type) {
|
|
174
|
+
if (checkString('word/', {offset})) {
|
|
175
|
+
type = {
|
|
176
|
+
ext: 'docx',
|
|
177
|
+
mime: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
178
|
+
};
|
|
179
|
+
} else if (checkString('ppt/', {offset})) {
|
|
180
|
+
type = {
|
|
181
|
+
ext: 'pptx',
|
|
182
|
+
mime: 'application/vnd.openxmlformats-officedocument.presentationml.presentation'
|
|
183
|
+
};
|
|
184
|
+
} else if (checkString('xl/', {offset})) {
|
|
185
|
+
type = {
|
|
186
|
+
ext: 'xlsx',
|
|
187
|
+
mime: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
188
|
+
};
|
|
187
189
|
}
|
|
188
190
|
}
|
|
191
|
+
|
|
192
|
+
if (oxmlFound && type) {
|
|
193
|
+
return type;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
zipHeaderIndex = findNextZipHeaderIndex(buf, offset);
|
|
197
|
+
} while (zipHeaderIndex >= 0);
|
|
198
|
+
|
|
199
|
+
// No more zip parts available in the buffer, but maybe we are almost certain about the type?
|
|
200
|
+
if (type) {
|
|
201
|
+
return type;
|
|
189
202
|
}
|
|
190
203
|
}
|
|
191
204
|
|
|
@@ -278,7 +291,7 @@ module.exports = input => {
|
|
|
278
291
|
|
|
279
292
|
if (idPos !== -1) {
|
|
280
293
|
const docTypePos = idPos + 3;
|
|
281
|
-
const findDocType = type =>
|
|
294
|
+
const findDocType = type => [...type].every((c, i) => sliced[docTypePos + i] === c.charCodeAt(0));
|
|
282
295
|
|
|
283
296
|
if (findDocType('matroska')) {
|
|
284
297
|
return {
|
|
@@ -307,14 +320,27 @@ module.exports = input => {
|
|
|
307
320
|
};
|
|
308
321
|
}
|
|
309
322
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
check([0x41, 0x56, 0x49], {offset: 8})
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
323
|
+
// RIFF file format which might be AVI, WAV, QCP, etc
|
|
324
|
+
if (check([0x52, 0x49, 0x46, 0x46])) {
|
|
325
|
+
if (check([0x41, 0x56, 0x49], {offset: 8})) {
|
|
326
|
+
return {
|
|
327
|
+
ext: 'avi',
|
|
328
|
+
mime: 'video/vnd.avi'
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
if (check([0x57, 0x41, 0x56, 0x45], {offset: 8})) {
|
|
332
|
+
return {
|
|
333
|
+
ext: 'wav',
|
|
334
|
+
mime: 'audio/vnd.wave'
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
// QLCM, QCP file
|
|
338
|
+
if (check([0x51, 0x4C, 0x43, 0x4D], {offset: 8})) {
|
|
339
|
+
return {
|
|
340
|
+
ext: 'qcp',
|
|
341
|
+
mime: 'audio/qcelp'
|
|
342
|
+
};
|
|
343
|
+
}
|
|
318
344
|
}
|
|
319
345
|
|
|
320
346
|
if (check([0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9])) {
|
|
@@ -361,15 +387,33 @@ module.exports = input => {
|
|
|
361
387
|
mime: 'audio/mpeg'
|
|
362
388
|
};
|
|
363
389
|
}
|
|
390
|
+
|
|
391
|
+
if (
|
|
392
|
+
check([0xFF, 0xF8], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 2 layer 0 using ADTS
|
|
393
|
+
) {
|
|
394
|
+
return {
|
|
395
|
+
ext: 'mp2',
|
|
396
|
+
mime: 'audio/mpeg'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (
|
|
401
|
+
check([0xFF, 0xF0], {offset: start, mask: [0xFF, 0xFC]}) // MPEG 4 layer 0 using ADTS
|
|
402
|
+
) {
|
|
403
|
+
return {
|
|
404
|
+
ext: 'mp4',
|
|
405
|
+
mime: 'audio/mpeg'
|
|
406
|
+
};
|
|
407
|
+
}
|
|
364
408
|
}
|
|
365
409
|
|
|
366
410
|
if (
|
|
367
411
|
check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4}) ||
|
|
368
412
|
check([0x4D, 0x34, 0x41, 0x20])
|
|
369
413
|
) {
|
|
370
|
-
return {
|
|
414
|
+
return { // MPEG-4 layer 3 (audio)
|
|
371
415
|
ext: 'm4a',
|
|
372
|
-
mime: 'audio/
|
|
416
|
+
mime: 'audio/mp4' // RFC 4337
|
|
373
417
|
};
|
|
374
418
|
}
|
|
375
419
|
|
|
@@ -437,13 +481,17 @@ module.exports = input => {
|
|
|
437
481
|
};
|
|
438
482
|
}
|
|
439
483
|
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
484
|
+
if (check([0x4D, 0x41, 0x43, 0x20])) { // 'MAC '
|
|
485
|
+
return {
|
|
486
|
+
ext: 'ape',
|
|
487
|
+
mime: 'audio/ape'
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
if (check([0x77, 0x76, 0x70, 0x6B])) { // 'wvpk'
|
|
444
492
|
return {
|
|
445
|
-
ext: '
|
|
446
|
-
mime: 'audio/
|
|
493
|
+
ext: 'wv',
|
|
494
|
+
mime: 'audio/wavpack'
|
|
447
495
|
};
|
|
448
496
|
}
|
|
449
497
|
|
|
@@ -528,7 +576,7 @@ module.exports = input => {
|
|
|
528
576
|
) {
|
|
529
577
|
return {
|
|
530
578
|
ext: 'eot',
|
|
531
|
-
mime: 'application/
|
|
579
|
+
mime: 'application/vnd.ms-fontobject'
|
|
532
580
|
};
|
|
533
581
|
}
|
|
534
582
|
|
|
@@ -556,7 +604,7 @@ module.exports = input => {
|
|
|
556
604
|
if (check([0x00, 0x00, 0x02, 0x00])) {
|
|
557
605
|
return {
|
|
558
606
|
ext: 'cur',
|
|
559
|
-
mime: 'image/x-
|
|
607
|
+
mime: 'image/x-icon'
|
|
560
608
|
};
|
|
561
609
|
}
|
|
562
610
|
|
|
@@ -770,5 +818,12 @@ module.exports = input => {
|
|
|
770
818
|
}
|
|
771
819
|
}
|
|
772
820
|
|
|
821
|
+
if (check([0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
|
822
|
+
return {
|
|
823
|
+
ext: 'ktx',
|
|
824
|
+
mime: 'image/ktx'
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
|
|
773
828
|
return null;
|
|
774
829
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "file-type",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Detect the file type of a Buffer/Uint8Array",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/file-type",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"url": "sindresorhus.com"
|
|
11
11
|
},
|
|
12
12
|
"engines": {
|
|
13
|
-
"node": ">=
|
|
13
|
+
"node": ">=6"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
16
|
"test": "xo && ava"
|
package/readme.md
CHANGED
|
@@ -76,7 +76,7 @@ Or `null` when no match.
|
|
|
76
76
|
|
|
77
77
|
Type: `Buffer` `Uint8Array`
|
|
78
78
|
|
|
79
|
-
It only needs the first 4100 bytes.
|
|
79
|
+
It only needs the first 4100 bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
## Supported file types
|
|
@@ -114,6 +114,7 @@ It only needs the first 4100 bytes.
|
|
|
114
114
|
- [`opus`](https://en.wikipedia.org/wiki/Opus_(audio_format))
|
|
115
115
|
- [`flac`](https://en.wikipedia.org/wiki/FLAC)
|
|
116
116
|
- [`wav`](https://en.wikipedia.org/wiki/WAV)
|
|
117
|
+
- [`qcp`](https://en.wikipedia.org/wiki/QCP)
|
|
117
118
|
- [`amr`](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec)
|
|
118
119
|
- [`pdf`](https://en.wikipedia.org/wiki/Portable_Document_Format)
|
|
119
120
|
- [`epub`](https://en.wikipedia.org/wiki/EPUB)
|
|
@@ -161,6 +162,9 @@ It only needs the first 4100 bytes.
|
|
|
161
162
|
- [`xml`](https://en.wikipedia.org/wiki/XML)
|
|
162
163
|
- [`heic`](http://nokiatech.github.io/heif/technical.html)
|
|
163
164
|
- [`cur`](https://en.wikipedia.org/wiki/ICO_(file_format))
|
|
165
|
+
- [`ktx`](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/)
|
|
166
|
+
- [`ape`](https://en.wikipedia.org/wiki/Monkey%27s_Audio) - Monkey's Audio
|
|
167
|
+
- [`wv`](https://en.wikipedia.org/wiki/WavPack) - WavPack
|
|
164
168
|
|
|
165
169
|
*SVG isn't included as it requires the whole file to be read, but you can get it [here](https://github.com/sindresorhus/is-svg).*
|
|
166
170
|
|