file-type 10.9.0 → 11.1.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.d.ts +191 -125
- package/index.js +160 -116
- package/package.json +28 -10
- package/readme.md +24 -7
- package/util.js +58 -0
package/index.d.ts
CHANGED
|
@@ -1,139 +1,205 @@
|
|
|
1
1
|
/// <reference types="node"/>
|
|
2
2
|
import {Readable as ReadableStream} from 'stream';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
4
|
+
declare namespace fileType {
|
|
5
|
+
type FileType =
|
|
6
|
+
| 'jpg'
|
|
7
|
+
| 'png'
|
|
8
|
+
| 'gif'
|
|
9
|
+
| 'webp'
|
|
10
|
+
| 'flif'
|
|
11
|
+
| 'cr2'
|
|
12
|
+
| 'orf'
|
|
13
|
+
| 'arw'
|
|
14
|
+
| 'dng'
|
|
15
|
+
| 'nef'
|
|
16
|
+
| 'tif'
|
|
17
|
+
| 'bmp'
|
|
18
|
+
| 'jxr'
|
|
19
|
+
| 'psd'
|
|
20
|
+
| 'zip'
|
|
21
|
+
| 'tar'
|
|
22
|
+
| 'rar'
|
|
23
|
+
| 'gz'
|
|
24
|
+
| 'bz2'
|
|
25
|
+
| '7z'
|
|
26
|
+
| 'dmg'
|
|
27
|
+
| 'mp4'
|
|
28
|
+
| 'mid'
|
|
29
|
+
| 'mkv'
|
|
30
|
+
| 'webm'
|
|
31
|
+
| 'mov'
|
|
32
|
+
| 'avi'
|
|
33
|
+
| 'wmv'
|
|
34
|
+
| 'mpg'
|
|
35
|
+
| 'mp2'
|
|
36
|
+
| 'mp3'
|
|
37
|
+
| 'm4a'
|
|
38
|
+
| 'ogg'
|
|
39
|
+
| 'opus'
|
|
40
|
+
| 'flac'
|
|
41
|
+
| 'wav'
|
|
42
|
+
| 'qcp'
|
|
43
|
+
| 'amr'
|
|
44
|
+
| 'pdf'
|
|
45
|
+
| 'epub'
|
|
46
|
+
| 'mobi'
|
|
47
|
+
| 'exe'
|
|
48
|
+
| 'swf'
|
|
49
|
+
| 'rtf'
|
|
50
|
+
| 'woff'
|
|
51
|
+
| 'woff2'
|
|
52
|
+
| 'eot'
|
|
53
|
+
| 'ttf'
|
|
54
|
+
| 'otf'
|
|
55
|
+
| 'ico'
|
|
56
|
+
| 'flv'
|
|
57
|
+
| 'ps'
|
|
58
|
+
| 'xz'
|
|
59
|
+
| 'sqlite'
|
|
60
|
+
| 'nes'
|
|
61
|
+
| 'crx'
|
|
62
|
+
| 'xpi'
|
|
63
|
+
| 'cab'
|
|
64
|
+
| 'deb'
|
|
65
|
+
| 'ar'
|
|
66
|
+
| 'rpm'
|
|
67
|
+
| 'Z'
|
|
68
|
+
| 'lz'
|
|
69
|
+
| 'msi'
|
|
70
|
+
| 'mxf'
|
|
71
|
+
| 'mts'
|
|
72
|
+
| 'wasm'
|
|
73
|
+
| 'blend'
|
|
74
|
+
| 'bpg'
|
|
75
|
+
| 'docx'
|
|
76
|
+
| 'pptx'
|
|
77
|
+
| 'xlsx'
|
|
78
|
+
| '3gp'
|
|
79
|
+
| '3g2'
|
|
80
|
+
| 'jp2'
|
|
81
|
+
| 'jpm'
|
|
82
|
+
| 'jpx'
|
|
83
|
+
| 'mj2'
|
|
84
|
+
| 'aif'
|
|
85
|
+
| 'odt'
|
|
86
|
+
| 'ods'
|
|
87
|
+
| 'odp'
|
|
88
|
+
| 'xml'
|
|
89
|
+
| 'heic'
|
|
90
|
+
| 'cur'
|
|
91
|
+
| 'ktx'
|
|
92
|
+
| 'ape'
|
|
93
|
+
| 'wv'
|
|
94
|
+
| 'asf'
|
|
95
|
+
| 'wma'
|
|
96
|
+
| 'wmv'
|
|
97
|
+
| 'dcm'
|
|
98
|
+
| 'mpc'
|
|
99
|
+
| 'ics'
|
|
100
|
+
| 'glb'
|
|
101
|
+
| 'pcap'
|
|
102
|
+
| 'dsf'
|
|
103
|
+
| 'lnk'
|
|
104
|
+
| 'alias'
|
|
105
|
+
| 'voc'
|
|
106
|
+
| 'ac3'
|
|
107
|
+
| 'm4a'
|
|
108
|
+
| 'm4b'
|
|
109
|
+
| 'm4p'
|
|
110
|
+
| 'm4v'
|
|
111
|
+
| 'f4a'
|
|
112
|
+
| 'f4b'
|
|
113
|
+
| 'f4p'
|
|
114
|
+
| 'f4v';
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
116
|
+
interface FileTypeResult {
|
|
117
|
+
/**
|
|
118
|
+
One of the supported [file types](https://github.com/sindresorhus/file-type#supported-file-types).
|
|
119
|
+
*/
|
|
120
|
+
ext: FileType;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
The detected [MIME type](https://en.wikipedia.org/wiki/Internet_media_type).
|
|
124
|
+
*/
|
|
125
|
+
mime: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type ReadableStreamWithFileType = ReadableStream & {
|
|
129
|
+
readonly fileType?: FileTypeResult;
|
|
130
|
+
};
|
|
108
131
|
}
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
declare const fileType: {
|
|
134
|
+
/**
|
|
135
|
+
Detect the file type of a `Buffer`/`Uint8Array`/`ArrayBuffer`. 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.
|
|
136
|
+
|
|
137
|
+
@param buffer - It only needs the first `.minimumBytes` bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
|
|
138
|
+
@returns The detected file type and MIME type or `undefined` when there was no match.
|
|
139
|
+
|
|
140
|
+
@example
|
|
141
|
+
```
|
|
142
|
+
import readChunk = require('read-chunk');
|
|
143
|
+
import fileType = require('file-type');
|
|
144
|
+
|
|
145
|
+
const buffer = readChunk.sync('unicorn.png', 0, fileType.minimumBytes);
|
|
146
|
+
|
|
147
|
+
fileType(buffer);
|
|
148
|
+
//=> {ext: 'png', mime: 'image/png'}
|
|
149
|
+
|
|
113
150
|
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
// Or from a remote location:
|
|
152
|
+
|
|
153
|
+
import * as http from 'http';
|
|
154
|
+
|
|
155
|
+
const url = 'https://assets-cdn.github.com/images/spinners/octocat-spinner-32.gif';
|
|
156
|
+
|
|
157
|
+
http.get(url, response => {
|
|
158
|
+
response.on('readable', () => {
|
|
159
|
+
const chunk = response.read(fileType.minimumBytes);
|
|
160
|
+
response.destroy();
|
|
161
|
+
console.log(fileType(chunk));
|
|
162
|
+
//=> {ext: 'gif', mime: 'image/gif'}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
*/
|
|
167
|
+
(buffer: Buffer | Uint8Array | ArrayBuffer): fileType.FileTypeResult | undefined;
|
|
116
168
|
|
|
117
169
|
/**
|
|
118
|
-
|
|
119
|
-
|
|
170
|
+
The minimum amount of bytes needed to detect a file type. Currently, it's 4100 bytes, but it can change, so don't hard-code it.
|
|
171
|
+
*/
|
|
120
172
|
readonly minimumBytes: number;
|
|
121
173
|
|
|
122
174
|
/**
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
*/
|
|
128
|
-
readonly stream: (readableStream: ReadableStream) => Promise<ReadableStreamWithFileType>;
|
|
129
|
-
}
|
|
175
|
+
Detect the file type of a readable stream.
|
|
176
|
+
|
|
177
|
+
@param readableStream - A readable stream containing a file to examine, see: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream_readable).
|
|
178
|
+
@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 `fileType()`.
|
|
130
179
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
180
|
+
@example
|
|
181
|
+
```
|
|
182
|
+
import * as fs from 'fs';
|
|
183
|
+
import * as crypto from 'crypto';
|
|
184
|
+
import fileType = require('file-type');
|
|
185
|
+
|
|
186
|
+
(async () => {
|
|
187
|
+
const read = fs.createReadStream('encrypted.enc');
|
|
188
|
+
const decipher = crypto.createDecipheriv(alg, key, iv);
|
|
189
|
+
|
|
190
|
+
const stream = await fileType.stream(read.pipe(decipher));
|
|
191
|
+
|
|
192
|
+
console.log(stream.fileType);
|
|
193
|
+
//=> {ext: 'mov', mime: 'video/quicktime'}
|
|
194
|
+
|
|
195
|
+
const write = fs.createWriteStream(`decrypted.${stream.fileType.ext}`);
|
|
196
|
+
stream.pipe(write);
|
|
197
|
+
})();
|
|
198
|
+
```
|
|
199
|
+
*/
|
|
200
|
+
readonly stream: (
|
|
201
|
+
readableStream: ReadableStream
|
|
202
|
+
) => Promise<fileType.ReadableStreamWithFileType>;
|
|
203
|
+
};
|
|
138
204
|
|
|
139
|
-
export
|
|
205
|
+
export = fileType;
|
package/index.js
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const
|
|
3
|
-
const xpiZipFilename = toBytes('META-INF/mozilla.rsa');
|
|
4
|
-
const oxmlContentTypes = toBytes('[Content_Types].xml');
|
|
5
|
-
const oxmlRels = toBytes('_rels/.rels');
|
|
2
|
+
const {stringToBytes, readUInt64LE, tarHeaderChecksumMatches, uint8ArrayUtf8ByteString} = require('./util');
|
|
6
3
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
let i = 0;
|
|
11
|
-
while (++i < 8) {
|
|
12
|
-
mul *= 0x100;
|
|
13
|
-
n += buf[offset + i] * mul;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return n;
|
|
17
|
-
}
|
|
4
|
+
const xpiZipFilename = stringToBytes('META-INF/mozilla.rsa');
|
|
5
|
+
const oxmlContentTypes = stringToBytes('[Content_Types].xml');
|
|
6
|
+
const oxmlRels = stringToBytes('_rels/.rels');
|
|
18
7
|
|
|
19
8
|
const fileType = input => {
|
|
20
|
-
if (!(input instanceof Uint8Array || Buffer.isBuffer(input))) {
|
|
21
|
-
throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\`, got \`${typeof input}\``);
|
|
9
|
+
if (!(input instanceof Uint8Array || input instanceof ArrayBuffer || Buffer.isBuffer(input))) {
|
|
10
|
+
throw new TypeError(`Expected the \`input\` argument to be of type \`Uint8Array\` or \`Buffer\` or \`ArrayBuffer\`, got \`${typeof input}\``);
|
|
22
11
|
}
|
|
23
12
|
|
|
24
|
-
const
|
|
13
|
+
const buffer = input instanceof Uint8Array ? input : new Uint8Array(input);
|
|
25
14
|
|
|
26
|
-
if (!(
|
|
27
|
-
return
|
|
15
|
+
if (!(buffer && buffer.length > 1)) {
|
|
16
|
+
return;
|
|
28
17
|
}
|
|
29
18
|
|
|
30
19
|
const check = (header, options) => {
|
|
@@ -36,10 +25,10 @@ const fileType = input => {
|
|
|
36
25
|
// If a bitmask is set
|
|
37
26
|
if (options.mask) {
|
|
38
27
|
// If header doesn't equal `buf` with bits masked off
|
|
39
|
-
if (header[i] !== (options.mask[i] &
|
|
28
|
+
if (header[i] !== (options.mask[i] & buffer[i + options.offset])) {
|
|
40
29
|
return false;
|
|
41
30
|
}
|
|
42
|
-
} else if (header[i] !==
|
|
31
|
+
} else if (header[i] !== buffer[i + options.offset]) {
|
|
43
32
|
return false;
|
|
44
33
|
}
|
|
45
34
|
}
|
|
@@ -47,7 +36,7 @@ const fileType = input => {
|
|
|
47
36
|
return true;
|
|
48
37
|
};
|
|
49
38
|
|
|
50
|
-
const checkString = (header, options) => check(
|
|
39
|
+
const checkString = (header, options) => check(stringToBytes(header), options);
|
|
51
40
|
|
|
52
41
|
if (check([0xFF, 0xD8, 0xFF])) {
|
|
53
42
|
return {
|
|
@@ -84,7 +73,7 @@ const fileType = input => {
|
|
|
84
73
|
};
|
|
85
74
|
}
|
|
86
75
|
|
|
87
|
-
//
|
|
76
|
+
// `cr2`, `orf`, and `arw` need to be before `tif` check
|
|
88
77
|
if (
|
|
89
78
|
(check([0x49, 0x49, 0x2A, 0x0]) || check([0x4D, 0x4D, 0x0, 0x2A])) &&
|
|
90
79
|
check([0x43, 0x52], {offset: 8})
|
|
@@ -95,6 +84,34 @@ const fileType = input => {
|
|
|
95
84
|
};
|
|
96
85
|
}
|
|
97
86
|
|
|
87
|
+
if (check([0x49, 0x49, 0x52, 0x4F, 0x08, 0x00, 0x00, 0x00, 0x18])) {
|
|
88
|
+
return {
|
|
89
|
+
ext: 'orf',
|
|
90
|
+
mime: 'image/x-olympus-orf'
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (check([0x49, 0x49, 0x2A, 0x00, 0x10, 0xFB, 0x86, 0x01])) {
|
|
95
|
+
return {
|
|
96
|
+
ext: 'arw',
|
|
97
|
+
mime: 'image/x-sony-arw'
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (check([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2D])) {
|
|
102
|
+
return {
|
|
103
|
+
ext: 'dng',
|
|
104
|
+
mime: 'image/x-adobe-dng'
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (check([0x49, 0x49, 0x2A, 0x00, 0x30, 0x3D, 0x72, 0x01, 0x1C])) {
|
|
109
|
+
return {
|
|
110
|
+
ext: 'nef',
|
|
111
|
+
mime: 'image/x-nikon-nef'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
98
115
|
if (
|
|
99
116
|
check([0x49, 0x49, 0x2A, 0x0]) ||
|
|
100
117
|
check([0x4D, 0x4D, 0x0, 0x2A])
|
|
@@ -177,7 +194,7 @@ const fileType = input => {
|
|
|
177
194
|
|
|
178
195
|
let zipHeaderIndex = 0; // The first zip header was already found at index 0
|
|
179
196
|
let oxmlFound = false;
|
|
180
|
-
let type
|
|
197
|
+
let type;
|
|
181
198
|
|
|
182
199
|
do {
|
|
183
200
|
const offset = zipHeaderIndex + 30;
|
|
@@ -209,7 +226,7 @@ const fileType = input => {
|
|
|
209
226
|
return type;
|
|
210
227
|
}
|
|
211
228
|
|
|
212
|
-
zipHeaderIndex = findNextZipHeaderIndex(
|
|
229
|
+
zipHeaderIndex = findNextZipHeaderIndex(buffer, offset);
|
|
213
230
|
} while (zipHeaderIndex >= 0);
|
|
214
231
|
|
|
215
232
|
// No more zip parts available in the buffer, but maybe we are almost certain about the type?
|
|
@@ -220,8 +237,8 @@ const fileType = input => {
|
|
|
220
237
|
|
|
221
238
|
if (
|
|
222
239
|
check([0x50, 0x4B]) &&
|
|
223
|
-
(
|
|
224
|
-
(
|
|
240
|
+
(buffer[2] === 0x3 || buffer[2] === 0x5 || buffer[2] === 0x7) &&
|
|
241
|
+
(buffer[3] === 0x4 || buffer[3] === 0x6 || buffer[3] === 0x8)
|
|
225
242
|
) {
|
|
226
243
|
return {
|
|
227
244
|
ext: 'zip',
|
|
@@ -229,7 +246,10 @@ const fileType = input => {
|
|
|
229
246
|
};
|
|
230
247
|
}
|
|
231
248
|
|
|
232
|
-
if (
|
|
249
|
+
if (
|
|
250
|
+
check([0x30, 0x30, 0x30, 0x30, 0x30, 0x30], {offset: 148, mask: [0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8]}) && // Valid tar checksum
|
|
251
|
+
tarHeaderChecksumMatches(buffer)
|
|
252
|
+
) {
|
|
233
253
|
return {
|
|
234
254
|
ext: 'tar',
|
|
235
255
|
mime: 'application/x-tar'
|
|
@@ -238,7 +258,7 @@ const fileType = input => {
|
|
|
238
258
|
|
|
239
259
|
if (
|
|
240
260
|
check([0x52, 0x61, 0x72, 0x21, 0x1A, 0x7]) &&
|
|
241
|
-
(
|
|
261
|
+
(buffer[6] === 0x0 || buffer[6] === 0x1)
|
|
242
262
|
) {
|
|
243
263
|
return {
|
|
244
264
|
ext: 'rar',
|
|
@@ -274,25 +294,70 @@ const fileType = input => {
|
|
|
274
294
|
};
|
|
275
295
|
}
|
|
276
296
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
check([0x69, 0x73, 0x6F, 0x32], {offset: 8}) || // ISO2
|
|
285
|
-
check([0x6D, 0x6D, 0x70, 0x34], {offset: 8}) || // MMP4
|
|
286
|
-
check([0x4D, 0x34, 0x56], {offset: 8}) || // M4V
|
|
287
|
-
check([0x64, 0x61, 0x73, 0x68], {offset: 8}) // DASH
|
|
288
|
-
)
|
|
289
|
-
)) {
|
|
297
|
+
// `mov` format variants
|
|
298
|
+
if (
|
|
299
|
+
check([0x66, 0x72, 0x65, 0x65], {offset: 4}) || // `free`
|
|
300
|
+
check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // `mdat` MJPEG
|
|
301
|
+
check([0x6D, 0x6F, 0x6F, 0x76], {offset: 4}) || // `moov`
|
|
302
|
+
check([0x77, 0x69, 0x64, 0x65], {offset: 4}) // `wide`
|
|
303
|
+
) {
|
|
290
304
|
return {
|
|
291
|
-
ext: '
|
|
292
|
-
mime: 'video/
|
|
305
|
+
ext: 'mov',
|
|
306
|
+
mime: 'video/quicktime'
|
|
293
307
|
};
|
|
294
308
|
}
|
|
295
309
|
|
|
310
|
+
// File Type Box (https://en.wikipedia.org/wiki/ISO_base_media_file_format)
|
|
311
|
+
// It's not required to be first, but it's recommended to be. Almost all ISO base media files start with `ftyp` box.
|
|
312
|
+
// `ftyp` box must contain a brand major identifier, which must consist of ISO 8859-1 printable characters.
|
|
313
|
+
// Here we check for 8859-1 printable characters (for simplicity, it's a mask which also catches one non-printable character).
|
|
314
|
+
if (
|
|
315
|
+
check([0x66, 0x74, 0x79, 0x70], {offset: 4}) && // `ftyp`
|
|
316
|
+
(buffer[8] & 0x60) !== 0x00 && (buffer[9] & 0x60) !== 0x00 && (buffer[10] & 0x60) !== 0x00 && (buffer[11] & 0x60) !== 0x00 // Brand major
|
|
317
|
+
) {
|
|
318
|
+
// They all can have MIME `video/mp4` except `application/mp4` special-case which is hard to detect.
|
|
319
|
+
// For some cases, we're specific, everything else falls to `video/mp4` with `mp4` extension.
|
|
320
|
+
const brandMajor = uint8ArrayUtf8ByteString(buffer, 8, 12);
|
|
321
|
+
switch (brandMajor) {
|
|
322
|
+
case 'mif1':
|
|
323
|
+
return {ext: 'heic', mime: 'image/heif'};
|
|
324
|
+
case 'msf1':
|
|
325
|
+
return {ext: 'heic', mime: 'image/heif-sequence'};
|
|
326
|
+
case 'heic': case 'heix':
|
|
327
|
+
return {ext: 'heic', mime: 'image/heic'};
|
|
328
|
+
case 'hevc': case 'hevx':
|
|
329
|
+
return {ext: 'heic', mime: 'image/heic-sequence'};
|
|
330
|
+
case 'qt ':
|
|
331
|
+
return {ext: 'mov', mime: 'video/quicktime'};
|
|
332
|
+
case 'M4V ': case 'M4VH': case 'M4VP':
|
|
333
|
+
return {ext: 'm4v', mime: 'video/x-m4v'};
|
|
334
|
+
case 'M4P ':
|
|
335
|
+
return {ext: 'm4p', mime: 'video/mp4'};
|
|
336
|
+
case 'M4B ':
|
|
337
|
+
return {ext: 'm4b', mime: 'audio/mp4'};
|
|
338
|
+
case 'M4A ':
|
|
339
|
+
return {ext: 'm4a', mime: 'audio/x-m4a'};
|
|
340
|
+
case 'F4V ':
|
|
341
|
+
return {ext: 'f4v', mime: 'video/mp4'};
|
|
342
|
+
case 'F4P ':
|
|
343
|
+
return {ext: 'f4p', mime: 'video/mp4'};
|
|
344
|
+
case 'F4A ':
|
|
345
|
+
return {ext: 'f4a', mime: 'audio/mp4'};
|
|
346
|
+
case 'F4B ':
|
|
347
|
+
return {ext: 'f4b', mime: 'audio/mp4'};
|
|
348
|
+
default:
|
|
349
|
+
if (brandMajor.startsWith('3g')) {
|
|
350
|
+
if (brandMajor.startsWith('3g2')) {
|
|
351
|
+
return {ext: '3g2', mime: 'video/3gpp2'};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {ext: '3gp', mime: 'video/3gpp'};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return {ext: 'mp4', mime: 'video/mp4'};
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
296
361
|
if (check([0x4D, 0x54, 0x68, 0x64])) {
|
|
297
362
|
return {
|
|
298
363
|
ext: 'mid',
|
|
@@ -302,7 +367,7 @@ const fileType = input => {
|
|
|
302
367
|
|
|
303
368
|
// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/matroska
|
|
304
369
|
if (check([0x1A, 0x45, 0xDF, 0xA3])) {
|
|
305
|
-
const sliced =
|
|
370
|
+
const sliced = buffer.subarray(4, 4 + 4096);
|
|
306
371
|
const idPos = sliced.findIndex((el, i, arr) => arr[i] === 0x42 && arr[i + 1] === 0x82);
|
|
307
372
|
|
|
308
373
|
if (idPos !== -1) {
|
|
@@ -325,18 +390,6 @@ const fileType = input => {
|
|
|
325
390
|
}
|
|
326
391
|
}
|
|
327
392
|
|
|
328
|
-
if (check([0x0, 0x0, 0x0, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20]) ||
|
|
329
|
-
check([0x66, 0x72, 0x65, 0x65], {offset: 4}) || // Type: `free`
|
|
330
|
-
check([0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20], {offset: 4}) ||
|
|
331
|
-
check([0x6D, 0x64, 0x61, 0x74], {offset: 4}) || // MJPEG
|
|
332
|
-
check([0x6D, 0x6F, 0x6F, 0x76], {offset: 4}) || // Type: `moov`
|
|
333
|
-
check([0x77, 0x69, 0x64, 0x65], {offset: 4})) {
|
|
334
|
-
return {
|
|
335
|
-
ext: 'mov',
|
|
336
|
-
mime: 'video/quicktime'
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
|
|
340
393
|
// RIFF file format which might be AVI, WAV, QCP, etc
|
|
341
394
|
if (check([0x52, 0x49, 0x46, 0x46])) {
|
|
342
395
|
if (check([0x41, 0x56, 0x49], {offset: 8})) {
|
|
@@ -368,7 +421,7 @@ const fileType = input => {
|
|
|
368
421
|
|
|
369
422
|
let offset = 30;
|
|
370
423
|
do {
|
|
371
|
-
const objectSize = readUInt64LE(
|
|
424
|
+
const objectSize = readUInt64LE(buffer, offset + 16);
|
|
372
425
|
if (check([0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65], {offset})) {
|
|
373
426
|
// Sync on Stream-Properties-Object (B7DC0791-A9B7-11CF-8EE6-00C00C205365)
|
|
374
427
|
if (check([0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B], {offset: offset + 24})) {
|
|
@@ -391,7 +444,7 @@ const fileType = input => {
|
|
|
391
444
|
}
|
|
392
445
|
|
|
393
446
|
offset += objectSize;
|
|
394
|
-
} while (offset + 24 <=
|
|
447
|
+
} while (offset + 24 <= buffer.length);
|
|
395
448
|
|
|
396
449
|
// Default to ASF generic extension
|
|
397
450
|
return {
|
|
@@ -410,18 +463,11 @@ const fileType = input => {
|
|
|
410
463
|
};
|
|
411
464
|
}
|
|
412
465
|
|
|
413
|
-
if (check([0x66, 0x74, 0x79, 0x70, 0x33, 0x67], {offset: 4})) {
|
|
414
|
-
return {
|
|
415
|
-
ext: '3gp',
|
|
416
|
-
mime: 'video/3gpp'
|
|
417
|
-
};
|
|
418
|
-
}
|
|
419
|
-
|
|
420
466
|
// Check for MPEG header at different starting offsets
|
|
421
|
-
for (let start = 0; start < 2 && start < (
|
|
467
|
+
for (let start = 0; start < 2 && start < (buffer.length - 16); start++) {
|
|
422
468
|
if (
|
|
423
469
|
check([0x49, 0x44, 0x33], {offset: start}) || // ID3 header
|
|
424
|
-
check([0xFF, 0xE2], {offset: start, mask: [0xFF,
|
|
470
|
+
check([0xFF, 0xE2], {offset: start, mask: [0xFF, 0xE6]}) // MPEG 1 or 2 Layer 3 header
|
|
425
471
|
) {
|
|
426
472
|
return {
|
|
427
473
|
ext: 'mp3',
|
|
@@ -430,7 +476,7 @@ const fileType = input => {
|
|
|
430
476
|
}
|
|
431
477
|
|
|
432
478
|
if (
|
|
433
|
-
check([0xFF, 0xE4], {offset: start, mask: [0xFF,
|
|
479
|
+
check([0xFF, 0xE4], {offset: start, mask: [0xFF, 0xE6]}) // MPEG 1 or 2 Layer 2 header
|
|
434
480
|
) {
|
|
435
481
|
return {
|
|
436
482
|
ext: 'mp2',
|
|
@@ -457,15 +503,6 @@ const fileType = input => {
|
|
|
457
503
|
}
|
|
458
504
|
}
|
|
459
505
|
|
|
460
|
-
if (
|
|
461
|
-
check([0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x41], {offset: 4})
|
|
462
|
-
) {
|
|
463
|
-
return { // MPEG-4 layer 3 (audio)
|
|
464
|
-
ext: 'm4a',
|
|
465
|
-
mime: 'audio/mp4' // RFC 4337
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
|
|
469
506
|
// Needs to be before `ogg` check
|
|
470
507
|
if (check([0x4F, 0x70, 0x75, 0x73, 0x48, 0x65, 0x61, 0x64], {offset: 28})) {
|
|
471
508
|
return {
|
|
@@ -568,7 +605,7 @@ const fileType = input => {
|
|
|
568
605
|
}
|
|
569
606
|
|
|
570
607
|
if (
|
|
571
|
-
(
|
|
608
|
+
(buffer[0] === 0x43 || buffer[0] === 0x46) &&
|
|
572
609
|
check([0x57, 0x53], {offset: 1})
|
|
573
610
|
) {
|
|
574
611
|
return {
|
|
@@ -838,37 +875,6 @@ const fileType = input => {
|
|
|
838
875
|
};
|
|
839
876
|
}
|
|
840
877
|
|
|
841
|
-
// File Type Box (https://en.wikipedia.org/wiki/ISO_base_media_file_format)
|
|
842
|
-
if (check([0x66, 0x74, 0x79, 0x70], {offset: 4})) {
|
|
843
|
-
if (check([0x6D, 0x69, 0x66, 0x31], {offset: 8})) {
|
|
844
|
-
return {
|
|
845
|
-
ext: 'heic',
|
|
846
|
-
mime: 'image/heif'
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (check([0x6D, 0x73, 0x66, 0x31], {offset: 8})) {
|
|
851
|
-
return {
|
|
852
|
-
ext: 'heic',
|
|
853
|
-
mime: 'image/heif-sequence'
|
|
854
|
-
};
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
if (check([0x68, 0x65, 0x69, 0x63], {offset: 8}) || check([0x68, 0x65, 0x69, 0x78], {offset: 8})) {
|
|
858
|
-
return {
|
|
859
|
-
ext: 'heic',
|
|
860
|
-
mime: 'image/heic'
|
|
861
|
-
};
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
if (check([0x68, 0x65, 0x76, 0x63], {offset: 8}) || check([0x68, 0x65, 0x76, 0x78], {offset: 8})) {
|
|
865
|
-
return {
|
|
866
|
-
ext: 'heic',
|
|
867
|
-
mime: 'image/heic-sequence'
|
|
868
|
-
};
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
|
|
872
878
|
if (check([0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A])) {
|
|
873
879
|
return {
|
|
874
880
|
ext: 'ktx',
|
|
@@ -920,26 +926,64 @@ const fileType = input => {
|
|
|
920
926
|
};
|
|
921
927
|
}
|
|
922
928
|
|
|
923
|
-
|
|
929
|
+
// Sony DSD Stream File (DSF)
|
|
930
|
+
if (check([0x44, 0x53, 0x44, 0x20])) {
|
|
931
|
+
return {
|
|
932
|
+
ext: 'dsf',
|
|
933
|
+
mime: 'audio/x-dsf' // Non-standard
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (check([0x4C, 0x00, 0x00, 0x00, 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46])) {
|
|
938
|
+
return {
|
|
939
|
+
ext: 'lnk',
|
|
940
|
+
mime: 'application/x.ms.shortcut' // Invented by us
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (check([0x62, 0x6F, 0x6F, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x72, 0x6B, 0x00, 0x00, 0x00, 0x00])) {
|
|
945
|
+
return {
|
|
946
|
+
ext: 'alias',
|
|
947
|
+
mime: 'application/x.apple.alias' // Invented by us
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
if (checkString('Creative Voice File')) {
|
|
952
|
+
return {
|
|
953
|
+
ext: 'voc',
|
|
954
|
+
mime: 'audio/x-voc'
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
if (check([0x0B, 0x77])) {
|
|
959
|
+
return {
|
|
960
|
+
ext: 'ac3',
|
|
961
|
+
mime: 'audio/vnd.dolby.dd-raw'
|
|
962
|
+
};
|
|
963
|
+
}
|
|
924
964
|
};
|
|
925
965
|
|
|
926
966
|
module.exports = fileType;
|
|
927
|
-
module.exports.default = fileType;
|
|
928
967
|
|
|
929
968
|
Object.defineProperty(fileType, 'minimumBytes', {value: 4100});
|
|
930
969
|
|
|
931
|
-
|
|
970
|
+
fileType.stream = readableStream => new Promise((resolve, reject) => {
|
|
932
971
|
// Using `eval` to work around issues when bundling with Webpack
|
|
933
972
|
const stream = eval('require')('stream'); // eslint-disable-line no-eval
|
|
934
973
|
|
|
935
974
|
readableStream.once('readable', () => {
|
|
936
975
|
const pass = new stream.PassThrough();
|
|
937
976
|
const chunk = readableStream.read(module.exports.minimumBytes) || readableStream.read();
|
|
938
|
-
|
|
977
|
+
try {
|
|
978
|
+
pass.fileType = fileType(chunk);
|
|
979
|
+
} catch (error) {
|
|
980
|
+
reject(error);
|
|
981
|
+
}
|
|
982
|
+
|
|
939
983
|
readableStream.unshift(chunk);
|
|
940
984
|
|
|
941
985
|
if (stream.pipeline) {
|
|
942
|
-
resolve(stream.pipeline(readableStream, pass));
|
|
986
|
+
resolve(stream.pipeline(readableStream, pass, () => {}));
|
|
943
987
|
} else {
|
|
944
988
|
resolve(readableStream.pipe(pass));
|
|
945
989
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "file-type",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Detect the file type of a Buffer/Uint8Array",
|
|
3
|
+
"version": "11.1.0",
|
|
4
|
+
"description": "Detect the file type of a Buffer/Uint8Array/ArrayBuffer",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "sindresorhus/file-type",
|
|
7
7
|
"author": {
|
|
@@ -13,11 +13,12 @@
|
|
|
13
13
|
"node": ">=6"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "xo && ava && tsd
|
|
16
|
+
"test": "xo && ava && tsd"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"index.js",
|
|
20
|
-
"index.d.ts"
|
|
20
|
+
"index.d.ts",
|
|
21
|
+
"util.js"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"mime",
|
|
@@ -45,6 +46,10 @@
|
|
|
45
46
|
"webp",
|
|
46
47
|
"flif",
|
|
47
48
|
"cr2",
|
|
49
|
+
"orf",
|
|
50
|
+
"arw",
|
|
51
|
+
"dng",
|
|
52
|
+
"nef",
|
|
48
53
|
"tif",
|
|
49
54
|
"bmp",
|
|
50
55
|
"jxr",
|
|
@@ -57,7 +62,6 @@
|
|
|
57
62
|
"7z",
|
|
58
63
|
"dmg",
|
|
59
64
|
"mp4",
|
|
60
|
-
"m4v",
|
|
61
65
|
"mid",
|
|
62
66
|
"mkv",
|
|
63
67
|
"webm",
|
|
@@ -118,14 +122,28 @@
|
|
|
118
122
|
"wma",
|
|
119
123
|
"ics",
|
|
120
124
|
"glb",
|
|
121
|
-
"pcap"
|
|
125
|
+
"pcap",
|
|
126
|
+
"dsf",
|
|
127
|
+
"lnk",
|
|
128
|
+
"alias",
|
|
129
|
+
"voc",
|
|
130
|
+
"ac3",
|
|
131
|
+
"3g2",
|
|
132
|
+
"m4a",
|
|
133
|
+
"m4b",
|
|
134
|
+
"m4p",
|
|
135
|
+
"m4v",
|
|
136
|
+
"f4a",
|
|
137
|
+
"f4b",
|
|
138
|
+
"f4p",
|
|
139
|
+
"f4v"
|
|
122
140
|
],
|
|
123
141
|
"devDependencies": {
|
|
124
|
-
"@types/node": "^11.
|
|
125
|
-
"ava": "^1.
|
|
142
|
+
"@types/node": "^11.12.2",
|
|
143
|
+
"ava": "^1.4.1",
|
|
126
144
|
"pify": "^4.0.1",
|
|
127
|
-
"read-chunk": "^3.
|
|
128
|
-
"tsd
|
|
145
|
+
"read-chunk": "^3.2.0",
|
|
146
|
+
"tsd": "^0.7.1",
|
|
129
147
|
"xo": "^0.24.0"
|
|
130
148
|
}
|
|
131
149
|
}
|
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# file-type [](https://travis-ci.org/sindresorhus/file-type)
|
|
2
2
|
|
|
3
|
-
> Detect the file type of a Buffer/Uint8Array
|
|
3
|
+
> Detect the file type of a Buffer/Uint8Array/ArrayBuffer
|
|
4
4
|
|
|
5
5
|
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.
|
|
6
6
|
|
|
@@ -95,11 +95,11 @@ Returns an `Object` with:
|
|
|
95
95
|
- `ext` - One of the [supported file types](#supported-file-types)
|
|
96
96
|
- `mime` - The [MIME type](https://en.wikipedia.org/wiki/Internet_media_type)
|
|
97
97
|
|
|
98
|
-
Or `
|
|
98
|
+
Or `undefined` when there is no match.
|
|
99
99
|
|
|
100
100
|
#### input
|
|
101
101
|
|
|
102
|
-
Type: `Buffer
|
|
102
|
+
Type: `Buffer | Uint8Array | ArrayBuffer`
|
|
103
103
|
|
|
104
104
|
It only needs the first `.minimumBytes` bytes. The exception is detection of `docx`, `pptx`, and `xlsx` which potentially requires reading the whole file.
|
|
105
105
|
|
|
@@ -129,7 +129,11 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
|
|
|
129
129
|
- [`gif`](https://en.wikipedia.org/wiki/GIF)
|
|
130
130
|
- [`webp`](https://en.wikipedia.org/wiki/WebP)
|
|
131
131
|
- [`flif`](https://en.wikipedia.org/wiki/Free_Lossless_Image_Format)
|
|
132
|
-
- [`cr2`](https://fileinfo.com/extension/cr2)
|
|
132
|
+
- [`cr2`](https://fileinfo.com/extension/cr2) - Canon Raw image file (v2)
|
|
133
|
+
- [`orf`](https://en.wikipedia.org/wiki/ORF_format) - Olympus Raw image file
|
|
134
|
+
- [`arw`](https://en.wikipedia.org/wiki/Raw_image_format#ARW) - Sony Alpha Raw image file
|
|
135
|
+
- [`dng`](https://en.wikipedia.org/wiki/Digital_Negative) - Adobe Digital Negative image file
|
|
136
|
+
- [`nef`](https://www.nikonusa.com/en/learn-and-explore/a/products-and-innovation/nikon-electronic-format-nef.html) - Nikon Electronic Format image file
|
|
133
137
|
- [`tif`](https://en.wikipedia.org/wiki/Tagged_Image_File_Format)
|
|
134
138
|
- [`bmp`](https://en.wikipedia.org/wiki/BMP_file_format)
|
|
135
139
|
- [`jxr`](https://en.wikipedia.org/wiki/JPEG_XR)
|
|
@@ -142,7 +146,6 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
|
|
|
142
146
|
- [`7z`](https://en.wikipedia.org/wiki/7z)
|
|
143
147
|
- [`dmg`](https://en.wikipedia.org/wiki/Apple_Disk_Image)
|
|
144
148
|
- [`mp4`](https://en.wikipedia.org/wiki/MPEG-4_Part_14#Filename_extensions)
|
|
145
|
-
- [`m4v`](https://en.wikipedia.org/wiki/M4V)
|
|
146
149
|
- [`mid`](https://en.wikipedia.org/wiki/MIDI)
|
|
147
150
|
- [`mkv`](https://en.wikipedia.org/wiki/Matroska)
|
|
148
151
|
- [`webm`](https://en.wikipedia.org/wiki/WebM)
|
|
@@ -193,7 +196,6 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
|
|
|
193
196
|
- [`docx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
|
194
197
|
- [`pptx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
|
195
198
|
- [`xlsx`](https://en.wikipedia.org/wiki/Office_Open_XML)
|
|
196
|
-
- [`3gp`](https://en.wikipedia.org/wiki/3GP_and_3G2)
|
|
197
199
|
- [`jp2`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
|
198
200
|
- [`jpm`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
|
199
201
|
- [`jpx`](https://en.wikipedia.org/wiki/JPEG_2000) - JPEG 2000
|
|
@@ -216,10 +218,25 @@ Type: [`stream.Readable`](https://nodejs.org/api/stream.html#stream_class_stream
|
|
|
216
218
|
- [`ics`](https://en.wikipedia.org/wiki/ICalendar#Data_format) - iCalendar
|
|
217
219
|
- [`glb`](https://github.com/KhronosGroup/glTF) - GL Transmission Format
|
|
218
220
|
- [`pcap`](https://wiki.wireshark.org/Development/LibpcapFileFormat) - Libpcap File Format
|
|
221
|
+
- [`dsf`](https://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf) - Sony DSD Stream File (DSF)
|
|
222
|
+
- [`lnk`](https://en.wikipedia.org/wiki/Shortcut_%28computing%29#Microsoft_Windows) - Microsoft Windows file shortcut
|
|
223
|
+
- [`alias`](https://en.wikipedia.org/wiki/Alias_%28Mac_OS%29) - macOS Alias file
|
|
224
|
+
- [`voc`](https://wiki.multimedia.cx/index.php/Creative_Voice) - Creative Voice File
|
|
225
|
+
- [`ac3`](https://www.atsc.org/standard/a522012-digital-audio-compression-ac-3-e-ac-3-standard-12172012/) - ATSC A/52 Audio File
|
|
226
|
+
- [`3gp`](https://en.wikipedia.org/wiki/3GP_and_3G2#3GP) - Multimedia container format defined by the Third Generation Partnership Project (3GPP) for 3G UMTS multimedia services
|
|
227
|
+
- [`3g2`](https://en.wikipedia.org/wiki/3GP_and_3G2#3G2) - Multimedia container format defined by the 3GPP2 for 3G CDMA2000 multimedia services
|
|
228
|
+
- [`m4v`](https://en.wikipedia.org/wiki/M4V) - MPEG-4 Visual bitstreams
|
|
229
|
+
- [`m4p`](https://en.wikipedia.org/wiki/MPEG-4_Part_14#Filename_extensions) - MPEG-4 files with audio streams encrypted by FairPlay Digital Rights Management as were sold through the iTunes Store
|
|
230
|
+
- [`m4a`](https://en.wikipedia.org/wiki/M4A) - Audio-only MPEG-4 files
|
|
231
|
+
- [`m4b`](https://en.wikipedia.org/wiki/M4B) - Audiobook and podcast MPEG-4 files, which also contain metadata including chapter markers, images, and hyperlinks
|
|
232
|
+
- [`f4v`](https://en.wikipedia.org/wiki/Flash_Video) - ISO base media file format used by Adobe Flash Player
|
|
233
|
+
- [`f4p`](https://en.wikipedia.org/wiki/Flash_Video) - ISO base media file format protected by Adobe Access DRM used by Adobe Flash Player
|
|
234
|
+
- [`f4a`](https://en.wikipedia.org/wiki/Flash_Video) - Audio-only ISO base media file format used by Adobe Flash Player
|
|
235
|
+
- [`f4b`](https://en.wikipedia.org/wiki/Flash_Video) - Audiobook and podcast ISO base media file format used by Adobe Flash Player
|
|
219
236
|
|
|
220
237
|
*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).*
|
|
221
238
|
|
|
222
|
-
*Pull
|
|
239
|
+
*Pull requests are welcome for additional commonly used file types.*
|
|
223
240
|
|
|
224
241
|
|
|
225
242
|
## Related
|
package/util.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
exports.stringToBytes = string => [...string].map(character => character.charCodeAt(0));
|
|
4
|
+
|
|
5
|
+
const uint8ArrayUtf8ByteString = (array, start, end) => {
|
|
6
|
+
return String.fromCharCode(...array.slice(start, end));
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
exports.readUInt64LE = (buffer, offset = 0) => {
|
|
10
|
+
let n = buffer[offset];
|
|
11
|
+
let mul = 1;
|
|
12
|
+
let i = 0;
|
|
13
|
+
|
|
14
|
+
while (++i < 8) {
|
|
15
|
+
mul *= 0x100;
|
|
16
|
+
n += buffer[offset + i] * mul;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return n;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
exports.tarHeaderChecksumMatches = buffer => { // Does not check if checksum field characters are valid
|
|
23
|
+
if (buffer.length < 512) { // `tar` header size, cannot compute checksum without it
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const MASK_8TH_BIT = 0x80;
|
|
28
|
+
|
|
29
|
+
let sum = 256; // Intitalize sum, with 256 as sum of 8 spaces in checksum field
|
|
30
|
+
let signedBitSum = 0; // Initialize signed bit sum
|
|
31
|
+
|
|
32
|
+
for (let i = 0; i < 148; i++) {
|
|
33
|
+
const byte = buffer[i];
|
|
34
|
+
sum += byte;
|
|
35
|
+
signedBitSum += byte & MASK_8TH_BIT; // Add signed bit to signed bit sum
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Skip checksum field
|
|
39
|
+
|
|
40
|
+
for (let i = 156; i < 512; i++) {
|
|
41
|
+
const byte = buffer[i];
|
|
42
|
+
sum += byte;
|
|
43
|
+
signedBitSum += byte & MASK_8TH_BIT; // Add signed bit to signed bit sum
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const readSum = parseInt(uint8ArrayUtf8ByteString(buffer, 148, 154), 8); // Read sum in header
|
|
47
|
+
|
|
48
|
+
// Some implementations compute checksum incorrectly using signed bytes
|
|
49
|
+
return (
|
|
50
|
+
// Checksum in header equals the sum we calculated
|
|
51
|
+
readSum === sum ||
|
|
52
|
+
|
|
53
|
+
// Checksum in header equals sum we calculated plus signed-to-unsigned delta
|
|
54
|
+
readSum === (sum - (signedBitSum << 1))
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
exports.uint8ArrayUtf8ByteString = uint8ArrayUtf8ByteString;
|