file-type 20.5.0 → 21.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/core.d.ts +20 -5
- package/core.js +100 -95
- package/index.d.ts +9 -8
- package/index.js +4 -4
- package/package.json +7 -8
- package/readme.md +35 -6
- package/supported.js +4 -5
package/core.d.ts
CHANGED
|
@@ -24,16 +24,17 @@ export type FileTypeResult = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
Detect the file type of a `Uint8Array
|
|
27
|
+
Detect the file type of a `Uint8Array` or `ArrayBuffer`.
|
|
28
28
|
|
|
29
29
|
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
|
30
30
|
|
|
31
31
|
If file access is available, it is recommended to use `.fromFile()` instead.
|
|
32
32
|
|
|
33
33
|
@param buffer - An Uint8Array or ArrayBuffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
|
|
34
|
+
@param options - Options to override default behavior.
|
|
34
35
|
@returns The detected file type, or `undefined` when there is no match.
|
|
35
36
|
*/
|
|
36
|
-
export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer): Promise<FileTypeResult | undefined>;
|
|
37
|
+
export function fileTypeFromBuffer(buffer: Uint8Array | ArrayBuffer, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
|
|
@@ -41,9 +42,10 @@ Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/e
|
|
|
41
42
|
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
|
42
43
|
|
|
43
44
|
@param stream - A [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) streaming a file to examine.
|
|
45
|
+
@param options - Options to override default behavior.
|
|
44
46
|
@returns A `Promise` for an object with the detected file type, or `undefined` when there is no match.
|
|
45
47
|
*/
|
|
46
|
-
export function fileTypeFromStream(stream: AnyWebByteStream): Promise<FileTypeResult | undefined>;
|
|
48
|
+
export function fileTypeFromStream(stream: AnyWebByteStream, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
51
|
Detect the file type from an [`ITokenizer`](https://github.com/Borewit/strtok3#tokenizer) source.
|
|
@@ -53,6 +55,7 @@ This method is used internally, but can also be used for a special "tokenizer" r
|
|
|
53
55
|
A tokenizer propagates the internal read functions, allowing alternative transport mechanisms, to access files, to be implemented and used.
|
|
54
56
|
|
|
55
57
|
@param tokenizer - File source implementing the tokenizer interface.
|
|
58
|
+
@param options - Options to override default behavior.
|
|
56
59
|
@returns The detected file type, or `undefined` when there is no match.
|
|
57
60
|
|
|
58
61
|
An example is [`@tokenizer/http`](https://github.com/Borewit/tokenizer-http), which requests data using [HTTP-range-requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Range_requests). A difference with a conventional stream and the [*tokenizer*](https://github.com/Borewit/strtok3#tokenizer), is that it is able to *ignore* (seek, fast-forward) in the stream. For example, you may only need and read the first 6 bytes, and the last 128 bytes, which may be an advantage in case reading the entire file would take longer.
|
|
@@ -71,7 +74,7 @@ console.log(fileType);
|
|
|
71
74
|
//=> {ext: 'mp3', mime: 'audio/mpeg'}
|
|
72
75
|
```
|
|
73
76
|
*/
|
|
74
|
-
export function fileTypeFromTokenizer(tokenizer: ITokenizer): Promise<FileTypeResult | undefined>;
|
|
77
|
+
export function fileTypeFromTokenizer(tokenizer: ITokenizer, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
75
78
|
|
|
76
79
|
/**
|
|
77
80
|
Supported file extensions.
|
|
@@ -96,6 +99,7 @@ export type StreamOptions = {
|
|
|
96
99
|
Detect the file type of a [`Blob`](https://nodejs.org/api/buffer.html#class-blob) or [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File).
|
|
97
100
|
|
|
98
101
|
@param blob - The [`Blob`](https://nodejs.org/api/buffer.html#class-blob) used for file detection.
|
|
102
|
+
@param options - Options to override default behavior.
|
|
99
103
|
@returns The detected file type, or `undefined` when there is no match.
|
|
100
104
|
|
|
101
105
|
@example
|
|
@@ -111,7 +115,7 @@ console.log(await fileTypeFromBlob(blob));
|
|
|
111
115
|
//=> {ext: 'txt', mime: 'text/plain'}
|
|
112
116
|
```
|
|
113
117
|
*/
|
|
114
|
-
export declare function fileTypeFromBlob(blob: Blob): Promise<FileTypeResult | undefined>;
|
|
118
|
+
export declare function fileTypeFromBlob(blob: Blob, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
A custom file type detector.
|
|
@@ -184,6 +188,17 @@ export type Detector = {
|
|
|
184
188
|
|
|
185
189
|
export type FileTypeOptions = {
|
|
186
190
|
customDetectors?: Iterable<Detector>;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
Specifies the byte tolerance for locating the first MPEG audio frame (e.g. `.mp1`, `.mp2`, `.mp3`, `.aac`).
|
|
194
|
+
|
|
195
|
+
Allows detection to handle slight sync offsets between the expected and actual frame start. Common in malformed or incorrectly muxed files, which, while technically invalid, do occur in the wild.
|
|
196
|
+
|
|
197
|
+
A tolerance of 10 bytes covers most cases.
|
|
198
|
+
|
|
199
|
+
@default 0
|
|
200
|
+
*/
|
|
201
|
+
mpegOffsetTolerance?: number;
|
|
187
202
|
};
|
|
188
203
|
|
|
189
204
|
export declare class TokenizerPositionError extends Error {
|
package/core.js
CHANGED
|
@@ -5,7 +5,7 @@ Primary entry point, Node.js specific entry point is index.js
|
|
|
5
5
|
import * as Token from 'token-types';
|
|
6
6
|
import * as strtok3 from 'strtok3/core';
|
|
7
7
|
import {ZipHandler} from '@tokenizer/inflate';
|
|
8
|
-
import {
|
|
8
|
+
import {getUintBE} from 'uint8array-extras';
|
|
9
9
|
import {
|
|
10
10
|
stringToBytes,
|
|
11
11
|
tarHeaderChecksumMatches,
|
|
@@ -15,74 +15,75 @@ import {extensions, mimeTypes} from './supported.js';
|
|
|
15
15
|
|
|
16
16
|
export const reasonableDetectionSizeInBytes = 4100; // A fair amount of file-types are detectable within this range.
|
|
17
17
|
|
|
18
|
-
export async function fileTypeFromStream(stream) {
|
|
19
|
-
return new FileTypeParser().fromStream(stream);
|
|
18
|
+
export async function fileTypeFromStream(stream, options) {
|
|
19
|
+
return new FileTypeParser(options).fromStream(stream);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export async function fileTypeFromBuffer(input) {
|
|
23
|
-
return new FileTypeParser().fromBuffer(input);
|
|
22
|
+
export async function fileTypeFromBuffer(input, options) {
|
|
23
|
+
return new FileTypeParser(options).fromBuffer(input);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export async function fileTypeFromBlob(blob) {
|
|
27
|
-
return new FileTypeParser().fromBlob(blob);
|
|
26
|
+
export async function fileTypeFromBlob(blob, options) {
|
|
27
|
+
return new FileTypeParser(options).fromBlob(blob);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function getFileTypeFromMimeType(mimeType) {
|
|
31
|
-
|
|
31
|
+
mimeType = mimeType.toLowerCase();
|
|
32
|
+
switch (mimeType) {
|
|
32
33
|
case 'application/epub+zip':
|
|
33
34
|
return {
|
|
34
35
|
ext: 'epub',
|
|
35
|
-
mime:
|
|
36
|
+
mime: mimeType,
|
|
36
37
|
};
|
|
37
38
|
case 'application/vnd.oasis.opendocument.text':
|
|
38
39
|
return {
|
|
39
40
|
ext: 'odt',
|
|
40
|
-
mime:
|
|
41
|
+
mime: mimeType,
|
|
41
42
|
};
|
|
42
43
|
case 'application/vnd.oasis.opendocument.text-template':
|
|
43
44
|
return {
|
|
44
45
|
ext: 'ott',
|
|
45
|
-
mime:
|
|
46
|
+
mime: mimeType,
|
|
46
47
|
};
|
|
47
48
|
case 'application/vnd.oasis.opendocument.spreadsheet':
|
|
48
49
|
return {
|
|
49
50
|
ext: 'ods',
|
|
50
|
-
mime:
|
|
51
|
+
mime: mimeType,
|
|
51
52
|
};
|
|
52
53
|
case 'application/vnd.oasis.opendocument.spreadsheet-template':
|
|
53
54
|
return {
|
|
54
55
|
ext: 'ots',
|
|
55
|
-
mime:
|
|
56
|
+
mime: mimeType,
|
|
56
57
|
};
|
|
57
58
|
case 'application/vnd.oasis.opendocument.presentation':
|
|
58
59
|
return {
|
|
59
60
|
ext: 'odp',
|
|
60
|
-
mime:
|
|
61
|
+
mime: mimeType,
|
|
61
62
|
};
|
|
62
63
|
case 'application/vnd.oasis.opendocument.presentation-template':
|
|
63
64
|
return {
|
|
64
65
|
ext: 'otp',
|
|
65
|
-
mime:
|
|
66
|
+
mime: mimeType,
|
|
66
67
|
};
|
|
67
68
|
case 'application/vnd.oasis.opendocument.graphics':
|
|
68
69
|
return {
|
|
69
70
|
ext: 'odg',
|
|
70
|
-
mime:
|
|
71
|
+
mime: mimeType,
|
|
71
72
|
};
|
|
72
73
|
case 'application/vnd.oasis.opendocument.graphics-template':
|
|
73
74
|
return {
|
|
74
75
|
ext: 'otg',
|
|
75
|
-
mime:
|
|
76
|
+
mime: mimeType,
|
|
76
77
|
};
|
|
77
78
|
case 'application/vnd.openxmlformats-officedocument.presentationml.slideshow':
|
|
78
79
|
return {
|
|
79
80
|
ext: 'ppsx',
|
|
80
|
-
mime:
|
|
81
|
+
mime: mimeType,
|
|
81
82
|
};
|
|
82
83
|
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
|
83
84
|
return {
|
|
84
85
|
ext: 'xlsx',
|
|
85
|
-
mime:
|
|
86
|
+
mime: mimeType,
|
|
86
87
|
};
|
|
87
88
|
case 'application/vnd.ms-excel.sheet.macroenabled':
|
|
88
89
|
return {
|
|
@@ -92,7 +93,7 @@ function getFileTypeFromMimeType(mimeType) {
|
|
|
92
93
|
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.template':
|
|
93
94
|
return {
|
|
94
95
|
ext: 'xltx',
|
|
95
|
-
mime:
|
|
96
|
+
mime: mimeType,
|
|
96
97
|
};
|
|
97
98
|
case 'application/vnd.ms-excel.template.macroenabled':
|
|
98
99
|
return {
|
|
@@ -107,7 +108,7 @@ function getFileTypeFromMimeType(mimeType) {
|
|
|
107
108
|
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
|
108
109
|
return {
|
|
109
110
|
ext: 'docx',
|
|
110
|
-
mime:
|
|
111
|
+
mime: mimeType,
|
|
111
112
|
};
|
|
112
113
|
case 'application/vnd.ms-word.document.macroenabled':
|
|
113
114
|
return {
|
|
@@ -117,7 +118,7 @@ function getFileTypeFromMimeType(mimeType) {
|
|
|
117
118
|
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.template':
|
|
118
119
|
return {
|
|
119
120
|
ext: 'dotx',
|
|
120
|
-
mime:
|
|
121
|
+
mime: mimeType,
|
|
121
122
|
};
|
|
122
123
|
case 'application/vnd.ms-word.template.macroenabledtemplate':
|
|
123
124
|
return {
|
|
@@ -127,7 +128,7 @@ function getFileTypeFromMimeType(mimeType) {
|
|
|
127
128
|
case 'application/vnd.openxmlformats-officedocument.presentationml.template':
|
|
128
129
|
return {
|
|
129
130
|
ext: 'potx',
|
|
130
|
-
mime:
|
|
131
|
+
mime: mimeType,
|
|
131
132
|
};
|
|
132
133
|
case 'application/vnd.ms-powerpoint.template.macroenabled':
|
|
133
134
|
return {
|
|
@@ -137,7 +138,7 @@ function getFileTypeFromMimeType(mimeType) {
|
|
|
137
138
|
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
|
138
139
|
return {
|
|
139
140
|
ext: 'pptx',
|
|
140
|
-
mime:
|
|
141
|
+
mime: mimeType,
|
|
141
142
|
};
|
|
142
143
|
case 'application/vnd.ms-powerpoint.presentation.macroenabled':
|
|
143
144
|
return {
|
|
@@ -179,8 +180,8 @@ function _check(buffer, headers, options) {
|
|
|
179
180
|
return true;
|
|
180
181
|
}
|
|
181
182
|
|
|
182
|
-
export async function fileTypeFromTokenizer(tokenizer) {
|
|
183
|
-
return new FileTypeParser().fromTokenizer(tokenizer);
|
|
183
|
+
export async function fileTypeFromTokenizer(tokenizer, options) {
|
|
184
|
+
return new FileTypeParser(options).fromTokenizer(tokenizer);
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
export async function fileTypeStream(webStream, options) {
|
|
@@ -189,6 +190,11 @@ export async function fileTypeStream(webStream, options) {
|
|
|
189
190
|
|
|
190
191
|
export class FileTypeParser {
|
|
191
192
|
constructor(options) {
|
|
193
|
+
this.options = {
|
|
194
|
+
mpegOffsetTolerance: 0,
|
|
195
|
+
...options,
|
|
196
|
+
};
|
|
197
|
+
|
|
192
198
|
this.detectors = [...(options?.customDetectors ?? []),
|
|
193
199
|
{id: 'core', detect: this.detectConfident},
|
|
194
200
|
{id: 'core.imprecise', detect: this.detectImprecise}];
|
|
@@ -253,7 +259,7 @@ export class FileTypeParser {
|
|
|
253
259
|
if (!done && chunk) {
|
|
254
260
|
try {
|
|
255
261
|
// Attempt to detect the file type from the chunk
|
|
256
|
-
detectedFileType = await this.fromBuffer(chunk.
|
|
262
|
+
detectedFileType = await this.fromBuffer(chunk.subarray(0, sampleSize));
|
|
257
263
|
} catch (error) {
|
|
258
264
|
if (!(error instanceof strtok3.EndOfStreamError)) {
|
|
259
265
|
throw error; // Re-throw non-EndOfStreamError
|
|
@@ -709,7 +715,7 @@ export class FileTypeParser {
|
|
|
709
715
|
if (this.checkString('fLaC')) {
|
|
710
716
|
return {
|
|
711
717
|
ext: 'flac',
|
|
712
|
-
mime: 'audio/
|
|
718
|
+
mime: 'audio/flac',
|
|
713
719
|
};
|
|
714
720
|
}
|
|
715
721
|
|
|
@@ -728,28 +734,6 @@ export class FileTypeParser {
|
|
|
728
734
|
}
|
|
729
735
|
|
|
730
736
|
if (this.checkString('%PDF')) {
|
|
731
|
-
try {
|
|
732
|
-
const skipBytes = 1350;
|
|
733
|
-
if (skipBytes === await tokenizer.ignore(skipBytes)) {
|
|
734
|
-
const maxBufferSize = 10 * 1024 * 1024;
|
|
735
|
-
const buffer = new Uint8Array(Math.min(maxBufferSize, tokenizer.fileInfo.size - skipBytes));
|
|
736
|
-
await tokenizer.readBuffer(buffer, {mayBeLess: true});
|
|
737
|
-
|
|
738
|
-
// Check if this is an Adobe Illustrator file
|
|
739
|
-
if (includes(buffer, new TextEncoder().encode('AIPrivateData'))) {
|
|
740
|
-
return {
|
|
741
|
-
ext: 'ai',
|
|
742
|
-
mime: 'application/postscript',
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
} catch (error) {
|
|
747
|
-
// Swallow end of stream error if file is too small for the Adobe AI check
|
|
748
|
-
if (!(error instanceof strtok3.EndOfStreamError)) {
|
|
749
|
-
throw error;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
|
|
753
737
|
// Assume this is just a normal PDF
|
|
754
738
|
return {
|
|
755
739
|
ext: 'pdf',
|
|
@@ -846,7 +830,7 @@ export class FileTypeParser {
|
|
|
846
830
|
case 'matroska':
|
|
847
831
|
return {
|
|
848
832
|
ext: 'mkv',
|
|
849
|
-
mime: 'video/
|
|
833
|
+
mime: 'video/matroska',
|
|
850
834
|
};
|
|
851
835
|
|
|
852
836
|
default:
|
|
@@ -920,10 +904,10 @@ export class FileTypeParser {
|
|
|
920
904
|
};
|
|
921
905
|
}
|
|
922
906
|
|
|
923
|
-
if (this.checkString('PAR1')) {
|
|
907
|
+
if (this.checkString('PAR1') || this.checkString('PARE')) {
|
|
924
908
|
return {
|
|
925
909
|
ext: 'parquet',
|
|
926
|
-
mime: 'application/
|
|
910
|
+
mime: 'application/vnd.apache.parquet',
|
|
927
911
|
};
|
|
928
912
|
}
|
|
929
913
|
|
|
@@ -1199,7 +1183,7 @@ export class FileTypeParser {
|
|
|
1199
1183
|
if (this.check([0x41, 0x52, 0x52, 0x4F, 0x57, 0x31, 0x00, 0x00])) {
|
|
1200
1184
|
return {
|
|
1201
1185
|
ext: 'arrow',
|
|
1202
|
-
mime: 'application/
|
|
1186
|
+
mime: 'application/vnd.apache.arrow.file',
|
|
1203
1187
|
};
|
|
1204
1188
|
}
|
|
1205
1189
|
|
|
@@ -1541,7 +1525,7 @@ export class FileTypeParser {
|
|
|
1541
1525
|
|
|
1542
1526
|
if (jsonSize > 12 && this.buffer.length >= jsonSize + 16) {
|
|
1543
1527
|
try {
|
|
1544
|
-
const header = new TextDecoder().decode(this.buffer.
|
|
1528
|
+
const header = new TextDecoder().decode(this.buffer.subarray(16, jsonSize + 16));
|
|
1545
1529
|
const json = JSON.parse(header);
|
|
1546
1530
|
// Check if Pickle is ASAR
|
|
1547
1531
|
if (json.files) { // Final check, assuring Pickle/ASAR format
|
|
@@ -1644,7 +1628,8 @@ export class FileTypeParser {
|
|
|
1644
1628
|
await tokenizer.peekBuffer(this.buffer, {length: Math.min(512, tokenizer.fileInfo.size), mayBeLess: true});
|
|
1645
1629
|
|
|
1646
1630
|
// Requires a buffer size of 512 bytes
|
|
1647
|
-
if (
|
|
1631
|
+
if ((this.checkString('ustar', {offset: 257}) && (this.checkString('\0', {offset: 262}) || this.checkString(' ', {offset: 262})))
|
|
1632
|
+
|| (this.check([0, 0, 0, 0, 0, 0], {offset: 257}) && tarHeaderChecksumMatches(this.buffer))) {
|
|
1648
1633
|
return {
|
|
1649
1634
|
ext: 'tar',
|
|
1650
1635
|
mime: 'application/x-tar',
|
|
@@ -1714,47 +1699,16 @@ export class FileTypeParser {
|
|
|
1714
1699
|
};
|
|
1715
1700
|
}
|
|
1716
1701
|
|
|
1702
|
+
// Adjust buffer to `mpegOffsetTolerance`
|
|
1703
|
+
await tokenizer.peekBuffer(this.buffer, {length: Math.min(2 + this.options.mpegOffsetTolerance, tokenizer.fileInfo.size), mayBeLess: true});
|
|
1704
|
+
|
|
1717
1705
|
// Check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE)
|
|
1718
|
-
if (this.buffer.length >= 2
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
if (
|
|
1722
|
-
return
|
|
1723
|
-
ext: 'aac',
|
|
1724
|
-
mime: 'audio/aac',
|
|
1725
|
-
};
|
|
1706
|
+
if (this.buffer.length >= (2 + this.options.mpegOffsetTolerance)) {
|
|
1707
|
+
for (let depth = 0; depth <= this.options.mpegOffsetTolerance; ++depth) {
|
|
1708
|
+
const type = this.scanMpeg(depth);
|
|
1709
|
+
if (type) {
|
|
1710
|
+
return type;
|
|
1726
1711
|
}
|
|
1727
|
-
|
|
1728
|
-
// Must be (ADTS) MPEG-4
|
|
1729
|
-
return {
|
|
1730
|
-
ext: 'aac',
|
|
1731
|
-
mime: 'audio/aac',
|
|
1732
|
-
};
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
// MPEG 1 or 2 Layer 3 header
|
|
1736
|
-
// Check for MPEG layer 3
|
|
1737
|
-
if (this.check([0x02], {offset: 1, mask: [0x06]})) {
|
|
1738
|
-
return {
|
|
1739
|
-
ext: 'mp3',
|
|
1740
|
-
mime: 'audio/mpeg',
|
|
1741
|
-
};
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
// Check for MPEG layer 2
|
|
1745
|
-
if (this.check([0x04], {offset: 1, mask: [0x06]})) {
|
|
1746
|
-
return {
|
|
1747
|
-
ext: 'mp2',
|
|
1748
|
-
mime: 'audio/mpeg',
|
|
1749
|
-
};
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
// Check for MPEG layer 1
|
|
1753
|
-
if (this.check([0x06], {offset: 1, mask: [0x06]})) {
|
|
1754
|
-
return {
|
|
1755
|
-
ext: 'mp1',
|
|
1756
|
-
mime: 'audio/mpeg',
|
|
1757
|
-
};
|
|
1758
1712
|
}
|
|
1759
1713
|
}
|
|
1760
1714
|
};
|
|
@@ -1831,6 +1785,57 @@ export class FileTypeParser {
|
|
|
1831
1785
|
};
|
|
1832
1786
|
}
|
|
1833
1787
|
}
|
|
1788
|
+
|
|
1789
|
+
/**
|
|
1790
|
+
Scan check MPEG 1 or 2 Layer 3 header, or 'layer 0' for ADTS (MPEG sync-word 0xFFE).
|
|
1791
|
+
|
|
1792
|
+
@param offset - Offset to scan for sync-preamble.
|
|
1793
|
+
@returns {{ext: string, mime: string}}
|
|
1794
|
+
*/
|
|
1795
|
+
scanMpeg(offset) {
|
|
1796
|
+
if (this.check([0xFF, 0xE0], {offset, mask: [0xFF, 0xE0]})) {
|
|
1797
|
+
if (this.check([0x10], {offset: offset + 1, mask: [0x16]})) {
|
|
1798
|
+
// Check for (ADTS) MPEG-2
|
|
1799
|
+
if (this.check([0x08], {offset: offset + 1, mask: [0x08]})) {
|
|
1800
|
+
return {
|
|
1801
|
+
ext: 'aac',
|
|
1802
|
+
mime: 'audio/aac',
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// Must be (ADTS) MPEG-4
|
|
1807
|
+
return {
|
|
1808
|
+
ext: 'aac',
|
|
1809
|
+
mime: 'audio/aac',
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
// MPEG 1 or 2 Layer 3 header
|
|
1814
|
+
// Check for MPEG layer 3
|
|
1815
|
+
if (this.check([0x02], {offset: offset + 1, mask: [0x06]})) {
|
|
1816
|
+
return {
|
|
1817
|
+
ext: 'mp3',
|
|
1818
|
+
mime: 'audio/mpeg',
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
// Check for MPEG layer 2
|
|
1823
|
+
if (this.check([0x04], {offset: offset + 1, mask: [0x06]})) {
|
|
1824
|
+
return {
|
|
1825
|
+
ext: 'mp2',
|
|
1826
|
+
mime: 'audio/mpeg',
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// Check for MPEG layer 1
|
|
1831
|
+
if (this.check([0x06], {offset: offset + 1, mask: [0x06]})) {
|
|
1832
|
+
return {
|
|
1833
|
+
ext: 'mp1',
|
|
1834
|
+
mime: 'audio/mpeg',
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1834
1839
|
}
|
|
1835
1840
|
|
|
1836
1841
|
export const supportedExtensions = new Set(extensions);
|
package/index.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
type FileTypeResult,
|
|
9
9
|
type StreamOptions,
|
|
10
10
|
type AnyWebReadableStream,
|
|
11
|
-
type Detector,
|
|
12
11
|
type AnyWebReadableByteStreamWithFileType,
|
|
12
|
+
type FileTypeOptions,
|
|
13
13
|
FileTypeParser as DefaultFileTypeParser,
|
|
14
14
|
} from './core.js';
|
|
15
15
|
|
|
@@ -31,8 +31,8 @@ export declare class FileTypeParser extends DefaultFileTypeParser {
|
|
|
31
31
|
/**
|
|
32
32
|
Works the same way as {@link fileTypeStream}, additionally taking into account custom detectors (if any were provided to the constructor).
|
|
33
33
|
*/
|
|
34
|
-
toDetectionStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
|
|
35
|
-
toDetectionStream(webStream: AnyWebReadableStream<Uint8Array>, options?: StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
|
|
34
|
+
toDetectionStream(readableStream: NodeReadableStream, options?: FileTypeOptions & StreamOptions): Promise<ReadableStreamWithFileType>;
|
|
35
|
+
toDetectionStream(webStream: AnyWebReadableStream<Uint8Array>, options?: FileTypeOptions & StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -48,7 +48,7 @@ The file type is detected by checking the [magic number](https://en.wikipedia.or
|
|
|
48
48
|
|
|
49
49
|
@returns The detected file type and MIME type or `undefined` when there is no match.
|
|
50
50
|
*/
|
|
51
|
-
export function fileTypeFromFile(filePath: string, options?:
|
|
51
|
+
export function fileTypeFromFile(filePath: string, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
Detect the file type of a [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
|
|
@@ -60,9 +60,10 @@ Direct support for Node.js streams will be dropped in the future, when Node.js s
|
|
|
60
60
|
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
|
61
61
|
|
|
62
62
|
@param stream - A [web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) or [Node.js `stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable) streaming a file to examine.
|
|
63
|
-
@
|
|
63
|
+
@param options - Options to override default behaviour.
|
|
64
|
+
@returns A `Promise` for an object with the detected file type, or `undefined` when there is no match.
|
|
64
65
|
*/
|
|
65
|
-
export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream): Promise<FileTypeResult | undefined>;
|
|
66
|
+
export function fileTypeFromStream(stream: AnyWebReadableStream<Uint8Array> | NodeReadableStream, options?: FileTypeOptions): Promise<FileTypeResult | undefined>;
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
Returns a `Promise` which resolves to the original readable stream argument, but with an added `fileType` property, which is an object like the one returned from `fileTypeFromFile()`.
|
|
@@ -91,7 +92,7 @@ if (stream2.fileType?.mime === 'image/jpeg') {
|
|
|
91
92
|
}
|
|
92
93
|
```
|
|
93
94
|
*/
|
|
94
|
-
export function fileTypeStream(readableStream: NodeReadableStream, options?: StreamOptions): Promise<ReadableStreamWithFileType>;
|
|
95
|
-
export function fileTypeStream(webStream: AnyWebByteStream, options?: StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
|
|
95
|
+
export function fileTypeStream(readableStream: NodeReadableStream, options?: FileTypeOptions & StreamOptions): Promise<ReadableStreamWithFileType>;
|
|
96
|
+
export function fileTypeStream(webStream: AnyWebByteStream, options?: FileTypeOptions & StreamOptions): Promise<AnyWebReadableByteStreamWithFileType>;
|
|
96
97
|
|
|
97
98
|
export * from './core.js';
|
package/index.js
CHANGED
|
@@ -65,12 +65,12 @@ export class FileTypeParser extends DefaultFileTypeParser {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export async function fileTypeFromFile(path,
|
|
69
|
-
return (new FileTypeParser(
|
|
68
|
+
export async function fileTypeFromFile(path, options) {
|
|
69
|
+
return (new FileTypeParser(options)).fromFile(path, options);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export async function fileTypeFromStream(stream,
|
|
73
|
-
return (new FileTypeParser(
|
|
72
|
+
export async function fileTypeFromStream(stream, options) {
|
|
73
|
+
return (new FileTypeParser(options)).fromStream(stream);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
export async function fileTypeStream(readableStream, options = {}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "file-type",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "21.0.0",
|
|
4
4
|
"description": "Detect the file type of a file, stream, or data",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/file-type",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"sideEffects": false,
|
|
37
37
|
"engines": {
|
|
38
|
-
"node": ">=
|
|
38
|
+
"node": ">=20"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"test": "xo && ava && tsd"
|
|
@@ -198,7 +198,6 @@
|
|
|
198
198
|
"it",
|
|
199
199
|
"s3m",
|
|
200
200
|
"xm",
|
|
201
|
-
"ai",
|
|
202
201
|
"skp",
|
|
203
202
|
"avif",
|
|
204
203
|
"eps",
|
|
@@ -247,19 +246,19 @@
|
|
|
247
246
|
"ppsx"
|
|
248
247
|
],
|
|
249
248
|
"dependencies": {
|
|
250
|
-
"@tokenizer/inflate": "^0.2.
|
|
251
|
-
"strtok3": "^10.2.
|
|
249
|
+
"@tokenizer/inflate": "^0.2.7",
|
|
250
|
+
"strtok3": "^10.2.2",
|
|
252
251
|
"token-types": "^6.0.0",
|
|
253
252
|
"uint8array-extras": "^1.4.0"
|
|
254
253
|
},
|
|
255
254
|
"devDependencies": {
|
|
256
255
|
"@tokenizer/token": "^0.3.0",
|
|
257
|
-
"@types/node": "^22.
|
|
258
|
-
"ava": "^6.0
|
|
256
|
+
"@types/node": "^22.15.21",
|
|
257
|
+
"ava": "^6.3.0",
|
|
259
258
|
"commonmark": "^0.31.2",
|
|
260
259
|
"get-stream": "^9.0.1",
|
|
261
260
|
"noop-stream": "^1.0.0",
|
|
262
|
-
"tsd": "^0.
|
|
261
|
+
"tsd": "^0.32.0",
|
|
263
262
|
"xo": "^0.60.0"
|
|
264
263
|
},
|
|
265
264
|
"xo": {
|
package/readme.md
CHANGED
|
@@ -109,7 +109,7 @@ console.log(fileType);
|
|
|
109
109
|
|
|
110
110
|
## API
|
|
111
111
|
|
|
112
|
-
### fileTypeFromBuffer(buffer)
|
|
112
|
+
### fileTypeFromBuffer(buffer, options)
|
|
113
113
|
|
|
114
114
|
Detect the file type of a `Uint8Array`, or `ArrayBuffer`.
|
|
115
115
|
|
|
@@ -130,13 +130,13 @@ Type: `Uint8Array | ArrayBuffer`
|
|
|
130
130
|
|
|
131
131
|
A buffer representing file data. It works best if the buffer contains the entire file. It may work with a smaller portion as well.
|
|
132
132
|
|
|
133
|
-
### fileTypeFromFile(filePath)
|
|
133
|
+
### fileTypeFromFile(filePath, options)
|
|
134
134
|
|
|
135
135
|
Detect the file type of a file path.
|
|
136
136
|
|
|
137
137
|
This is for Node.js only.
|
|
138
138
|
|
|
139
|
-
To read from a [`File`](https://developer.mozilla.org/docs/Web/API/File), see [`fileTypeFromBlob()`](#filetypefromblobblob).
|
|
139
|
+
To read from a [`File`](https://developer.mozilla.org/docs/Web/API/File), see [`fileTypeFromBlob()`](#filetypefromblobblob-options).
|
|
140
140
|
|
|
141
141
|
The file type is detected by checking the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) of the buffer.
|
|
142
142
|
|
|
@@ -176,7 +176,7 @@ Type: [Web `ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/Re
|
|
|
176
176
|
|
|
177
177
|
A readable stream representing file data.
|
|
178
178
|
|
|
179
|
-
### fileTypeFromBlob(blob)
|
|
179
|
+
### fileTypeFromBlob(blob, options)
|
|
180
180
|
|
|
181
181
|
Detect the file type of a [`Blob`](https://developer.mozilla.org/docs/Web/API/Blob),
|
|
182
182
|
|
|
@@ -225,7 +225,7 @@ async function readFromBlobWithoutStreaming(blob) {
|
|
|
225
225
|
|
|
226
226
|
Type: [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob)
|
|
227
227
|
|
|
228
|
-
### fileTypeFromTokenizer(tokenizer)
|
|
228
|
+
### fileTypeFromTokenizer(tokenizer, options)
|
|
229
229
|
|
|
230
230
|
Detect the file type from an `ITokenizer` source.
|
|
231
231
|
|
|
@@ -304,6 +304,8 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
|
|
|
304
304
|
|
|
305
305
|
Type: `object`
|
|
306
306
|
|
|
307
|
+
Supports the following options in addition to the [general options](#options):
|
|
308
|
+
|
|
307
309
|
##### sampleSize
|
|
308
310
|
|
|
309
311
|
Type: `number`\
|
|
@@ -341,6 +343,32 @@ Returns a `Set<string>` of supported file extensions.
|
|
|
341
343
|
|
|
342
344
|
Returns a `Set<string>` of supported MIME types.
|
|
343
345
|
|
|
346
|
+
### Options
|
|
347
|
+
|
|
348
|
+
#### customDetectors
|
|
349
|
+
|
|
350
|
+
Array of custom file type detectors to run before default detectors.
|
|
351
|
+
|
|
352
|
+
For example:
|
|
353
|
+
|
|
354
|
+
```js
|
|
355
|
+
import {fileTypeFromFile} from 'file-type';
|
|
356
|
+
import {detectXml} from '@file-type/xml';
|
|
357
|
+
|
|
358
|
+
const fileType = await fileTypeFromFile('sample.kml', {customDetectors: [detectXml]});
|
|
359
|
+
console.log(fileType);
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### mpegOffsetTolerance
|
|
363
|
+
|
|
364
|
+
Default: `0`
|
|
365
|
+
|
|
366
|
+
Specifies the byte tolerance for locating the first MPEG audio frame (e.g. `.mp1`, `.mp2`, `.mp3`, `.aac`).
|
|
367
|
+
|
|
368
|
+
Allows detection to handle slight sync offsets between the expected and actual frame start. Common in malformed or incorrectly muxed files, which, while technically invalid, do occur in the wild.
|
|
369
|
+
|
|
370
|
+
A tolerance of 10 bytes covers most cases.
|
|
371
|
+
|
|
344
372
|
## Custom detectors
|
|
345
373
|
|
|
346
374
|
Custom file type detectors are plugins designed to extend the default detection capabilities.
|
|
@@ -353,6 +381,8 @@ Detectors can be added via the constructor options or by directly modifying `Fil
|
|
|
353
381
|
|
|
354
382
|
### Example adding a detector
|
|
355
383
|
|
|
384
|
+
For example:
|
|
385
|
+
|
|
356
386
|
```js
|
|
357
387
|
import {FileTypeParser} from 'file-type';
|
|
358
388
|
import {detectXml} from '@file-type/xml';
|
|
@@ -436,7 +466,6 @@ abortController.abort(); // Abort file-type reading from the Blob stream.
|
|
|
436
466
|
- [`aac`](https://en.wikipedia.org/wiki/Advanced_Audio_Coding) - Advanced Audio Coding
|
|
437
467
|
- [`ac3`](https://www.atsc.org/standard/a522012-digital-audio-compression-ac-3-e-ac-3-standard-12172012/) - ATSC A/52 Audio File
|
|
438
468
|
- [`ace`](https://en.wikipedia.org/wiki/ACE_(compressed_file_format)) - ACE archive
|
|
439
|
-
- [`ai`](https://en.wikipedia.org/wiki/Adobe_Illustrator_Artwork) - Adobe Illustrator Artwork
|
|
440
469
|
- [`aif`](https://en.wikipedia.org/wiki/Audio_Interchange_File_Format) - Audio Interchange file
|
|
441
470
|
- [`alias`](https://en.wikipedia.org/wiki/Alias_%28Mac_OS%29) - macOS Alias file
|
|
442
471
|
- [`amr`](https://en.wikipedia.org/wiki/Adaptive_Multi-Rate_audio_codec) - Adaptive Multi-Rate audio codec
|
package/supported.js
CHANGED
|
@@ -128,7 +128,6 @@ export const extensions = [
|
|
|
128
128
|
'it',
|
|
129
129
|
's3m',
|
|
130
130
|
'xm',
|
|
131
|
-
'ai',
|
|
132
131
|
'skp',
|
|
133
132
|
'avif',
|
|
134
133
|
'eps',
|
|
@@ -208,10 +207,10 @@ export const mimeTypes = [
|
|
|
208
207
|
'application/x-bzip2',
|
|
209
208
|
'application/x-7z-compressed',
|
|
210
209
|
'application/x-apple-diskimage',
|
|
211
|
-
'application/
|
|
210
|
+
'application/vnd.apache.arrow.file',
|
|
212
211
|
'video/mp4',
|
|
213
212
|
'audio/midi',
|
|
214
|
-
'video/
|
|
213
|
+
'video/matroska',
|
|
215
214
|
'video/webm',
|
|
216
215
|
'video/quicktime',
|
|
217
216
|
'video/vnd.avi',
|
|
@@ -228,7 +227,7 @@ export const mimeTypes = [
|
|
|
228
227
|
'audio/ogg',
|
|
229
228
|
'audio/ogg; codecs=opus',
|
|
230
229
|
'application/ogg',
|
|
231
|
-
'audio/
|
|
230
|
+
'audio/flac',
|
|
232
231
|
'audio/ape',
|
|
233
232
|
'audio/wavpack',
|
|
234
233
|
'audio/amr',
|
|
@@ -321,7 +320,7 @@ export const mimeTypes = [
|
|
|
321
320
|
'image/jls',
|
|
322
321
|
'application/vnd.ms-outlook',
|
|
323
322
|
'image/vnd.dwg',
|
|
324
|
-
'application/
|
|
323
|
+
'application/vnd.apache.parquet',
|
|
325
324
|
'application/java-vm',
|
|
326
325
|
'application/x-arj',
|
|
327
326
|
'application/x-cpio',
|