@probityrules/jsmediatags 4.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/CHANGELOG.md +45 -0
- package/LICENSE.md +36 -0
- package/README.md +548 -0
- package/build/ArrayBufferFileReader.d.ts +12 -0
- package/build/ArrayBufferFileReader.js +27 -0
- package/build/ArrayFileReader.d.ts +11 -0
- package/build/ArrayFileReader.js +30 -0
- package/build/BlobFileReader.d.ts +12 -0
- package/build/BlobFileReader.js +47 -0
- package/build/ByteArrayUtils.d.ts +9 -0
- package/build/ByteArrayUtils.js +46 -0
- package/build/ChunkedFileData.d.ts +28 -0
- package/build/ChunkedFileData.js +171 -0
- package/build/DecodedString.d.ts +6 -0
- package/build/DecodedString.js +2 -0
- package/build/FLACTagContents.d.ts +19 -0
- package/build/FLACTagContents.js +54 -0
- package/build/FLACTagReader.d.ts +103 -0
- package/build/FLACTagReader.js +320 -0
- package/build/ID3v1TagReader.d.ts +10 -0
- package/build/ID3v1TagReader.js +176 -0
- package/build/ID3v2FrameReader.d.ts +25 -0
- package/build/ID3v2FrameReader.js +582 -0
- package/build/ID3v2TagContents.d.ts +82 -0
- package/build/ID3v2TagContents.js +318 -0
- package/build/ID3v2TagReader.d.ts +13 -0
- package/build/ID3v2TagReader.js +118 -0
- package/build/MP4TagContents.d.ts +17 -0
- package/build/MP4TagContents.js +52 -0
- package/build/MP4TagReader.d.ts +19 -0
- package/build/MP4TagReader.js +291 -0
- package/build/MediaFileReader.d.ts +46 -0
- package/build/MediaFileReader.js +168 -0
- package/build/MediaTagReader.d.ts +18 -0
- package/build/MediaTagReader.js +76 -0
- package/build/NodeFileReader.d.ts +12 -0
- package/build/NodeFileReader.js +103 -0
- package/build/ReactNativeFileReader.d.ts +12 -0
- package/build/ReactNativeFileReader.js +48 -0
- package/build/StringUtils.d.ts +7 -0
- package/build/StringUtils.js +102 -0
- package/build/XhrFileReader.d.ts +41 -0
- package/build/XhrFileReader.js +238 -0
- package/build/jsmediatags.d.ts +45 -0
- package/build/jsmediatags.js +219 -0
- package/build/registerNodeFileReaders.d.ts +5 -0
- package/build/registerNodeFileReaders.js +19 -0
- package/build/registerNodeFileReaders.noop.d.ts +2 -0
- package/build/registerNodeFileReaders.noop.js +3 -0
- package/build/types.d.ts +77 -0
- package/build/types.js +2 -0
- package/dist/jsmediatags.min.js +2 -0
- package/package.json +110 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [4.0.0] - 2026-06-02
|
|
9
|
+
|
|
10
|
+
Published to npm as **`@probityrules/jsmediatags`** from [probityrules/jsmediatags](https://github.com/probityrules/jsmediatags).
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- TypeScript source with published `.d.ts` declarations (`build/jsmediatags.d.ts`).
|
|
15
|
+
- Promise-based API: `read(location)` without callbacks, `readAsync()`, and matching methods on `Reader` and `MediaTagReader`. Callbacks remain supported.
|
|
16
|
+
- `package.json` `"exports"` map with `types`, `browser`, and `default` conditions.
|
|
17
|
+
- GitHub Actions CI (build, browser bundle, test).
|
|
18
|
+
- `prepublishOnly` script to build Node and browser artifacts before publish.
|
|
19
|
+
- esbuild browser bundles (`dist/jsmediatags.js`, `dist/jsmediatags.min.js`).
|
|
20
|
+
- Jest test suite in root `test/` with TypeScript tests and shared helpers.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Breaking:** `main` entry is now `build/jsmediatags.js` (compile TypeScript with `npm run build` before use, or consume from npm where artifacts are prebuilt).
|
|
25
|
+
- **Breaking:** Node.js `>=18` required.
|
|
26
|
+
- **Breaking:** Browser bundle is an esbuild IIFE exposing `jsmediatags` globally, replacing the previous Browserify/Closure Compiler UMD build.
|
|
27
|
+
- **Breaking:** `browser` field points at `dist/jsmediatags.min.js`.
|
|
28
|
+
- Migrated from Flow to TypeScript; shared types live in `src/types.ts` (formerly `FlowTypes.ts`).
|
|
29
|
+
- Dev tooling: TypeScript (`tsc`), esbuild, Jest 29, ts-jest.
|
|
30
|
+
- `optionalPeerDependencies` replaced with standard `peerDependencies` and `peerDependenciesMeta` for `buffer` and `react-native-fs`.
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
|
|
34
|
+
- Flow, Babel, Browserify, Watchify, and Google Closure Compiler from the build pipeline.
|
|
35
|
+
- Bower (`bower.json`).
|
|
36
|
+
- Unused `react` / `react-native` devDependencies (React Native support remains via optional peers and source).
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- ID3v2 frame flag defaults when partial flags are provided.
|
|
41
|
+
- `XhrFileReader` `Content-Length` parsing return type.
|
|
42
|
+
|
|
43
|
+
## [3.9.7] and earlier
|
|
44
|
+
|
|
45
|
+
See [git history](https://github.com/aadsm/jsmediatags/commits/master) for releases prior to the 4.0 modernization.
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[BSD License](http://opensource.org/licenses/BSD-3-Clause)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2009 Opera Software ASA
|
|
4
|
+
|
|
5
|
+
Copyright (c) 2015 António Afonso
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2008 Jacob Seidelin, http://blog.nihilogic.dk/
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2010 Joshua Kifer
|
|
10
|
+
|
|
11
|
+
All rights reserved.
|
|
12
|
+
|
|
13
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
14
|
+
are permitted provided that the following conditions are met:
|
|
15
|
+
|
|
16
|
+
* Redistributions of source code must retain the above copyright notice, this
|
|
17
|
+
list of conditions and the following disclaimer.
|
|
18
|
+
|
|
19
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
|
20
|
+
this list of conditions and the following disclaimer in the documentation
|
|
21
|
+
and/or other materials provided with the distribution.
|
|
22
|
+
|
|
23
|
+
* Neither the name Facebook nor the names of its contributors may be used to
|
|
24
|
+
endorse or promote products derived from this software without specific
|
|
25
|
+
prior written permission.
|
|
26
|
+
|
|
27
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
28
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
29
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
30
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
31
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
32
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
33
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
34
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
35
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
36
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
# JS MediaTags
|
|
2
|
+
|
|
3
|
+
Read ID3, MP4, and FLAC metadata from audio files in Node.js, browsers, and React Native. Successor to [JavaScript-ID3-Reader](https://github.com/aadsm/JavaScript-ID3-Reader).
|
|
4
|
+
|
|
5
|
+
This repository is a **fork and modernization** of [aadsm/jsmediatags](https://github.com/aadsm/jsmediatags), the original library and its maintenance home. Development here targets **4.0.0** (TypeScript, updated tooling, and packaging). It is not guaranteed to be merged upstream; treat [aadsm/jsmediatags](https://github.com/aadsm/jsmediatags) as the canonical project history and this fork as an upgraded continuation.
|
|
6
|
+
|
|
7
|
+
**npm:** [`@probityrules/jsmediatags`](https://www.npmjs.com/package/@probityrules/jsmediatags) (published from this fork; includes compiled `build/` and browser bundle only, not `src/`).
|
|
8
|
+
|
|
9
|
+
**Requirements:** Node.js **18+**. See [CHANGELOG.md](CHANGELOG.md) for **4.0.0** breaking changes if upgrading from 3.x.
|
|
10
|
+
|
|
11
|
+
## Donations
|
|
12
|
+
|
|
13
|
+
A few people have asked me about donations (or even crowdfunding). I would prefer you to consider making a donation to the ["Girls Who Code" NPO](https://www.classy.org/checkout/donation?eid=77372). If you do please send me a message so I can add you as a contributor.
|
|
14
|
+
|
|
15
|
+
## [Contributors](CONTRIBUTORS.md)
|
|
16
|
+
|
|
17
|
+
## [Contributing](CONTRIBUTING.md)
|
|
18
|
+
|
|
19
|
+
## Current Support
|
|
20
|
+
|
|
21
|
+
* File Readers
|
|
22
|
+
* NodeJS
|
|
23
|
+
* XMLHttpRequest
|
|
24
|
+
* Blob
|
|
25
|
+
* File
|
|
26
|
+
* Buffers/Arrays
|
|
27
|
+
* React Native
|
|
28
|
+
* Tag Readers
|
|
29
|
+
* ID3v1
|
|
30
|
+
* ID3v2 (with unsynchronisation support!)
|
|
31
|
+
* MP4
|
|
32
|
+
* FLAC
|
|
33
|
+
|
|
34
|
+
## How to use
|
|
35
|
+
|
|
36
|
+
### Node.js
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install @probityrules/jsmediatags
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The published package includes compiled JavaScript and TypeScript declarations (`build/jsmediatags.d.ts`). Source (`src/`) is not shipped on npm.
|
|
43
|
+
|
|
44
|
+
#### Callback API
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// Simple API - will fetch all tags
|
|
48
|
+
var jsmediatags = require("@probityrules/jsmediatags");
|
|
49
|
+
|
|
50
|
+
jsmediatags.read("./music-file.mp3", {
|
|
51
|
+
onSuccess: function(tag) {
|
|
52
|
+
console.log(tag);
|
|
53
|
+
},
|
|
54
|
+
onError: function(error) {
|
|
55
|
+
console.log(':(', error.type, error.info);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// Advanced API
|
|
62
|
+
var jsmediatags = require("@probityrules/jsmediatags");
|
|
63
|
+
|
|
64
|
+
new jsmediatags.Reader("http://www.example.com/music-file.mp3")
|
|
65
|
+
.setTagsToRead(["title", "artist"])
|
|
66
|
+
.read({
|
|
67
|
+
onSuccess: function(tag) {
|
|
68
|
+
console.log(tag);
|
|
69
|
+
},
|
|
70
|
+
onError: function(error) {
|
|
71
|
+
console.log(':(', error.type, error.info);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Promise API
|
|
77
|
+
|
|
78
|
+
Omit callbacks to get a `Promise`, or call `readAsync` explicitly. Rejections use the same `{ type, info, ... }` objects as `onError`.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
const jsmediatags = require("@probityrules/jsmediatags");
|
|
82
|
+
|
|
83
|
+
// read(location) without callbacks
|
|
84
|
+
const tag = await jsmediatags.read("./music-file.mp3");
|
|
85
|
+
|
|
86
|
+
// readAsync(location)
|
|
87
|
+
const tag2 = await jsmediatags.readAsync("./music-file.mp3");
|
|
88
|
+
|
|
89
|
+
// Reader instance
|
|
90
|
+
const tag3 = await new jsmediatags.Reader("./music-file.mp3")
|
|
91
|
+
.setTagsToRead(["title", "artist"])
|
|
92
|
+
.read();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```javascript
|
|
96
|
+
try {
|
|
97
|
+
const tag = await jsmediatags.readAsync("./music-file.mp3");
|
|
98
|
+
console.log(tag.tags.title);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
console.log(error.type, error.info);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Browser
|
|
105
|
+
|
|
106
|
+
Copy [`dist/jsmediatags.min.js`](https://github.com/aadsm/jsmediatags/blob/master/dist/jsmediatags.min.js) into your app and load it with a script tag. The bundle is also on [cdnjs](https://cdnjs.com/libraries/jsmediatags).
|
|
107
|
+
|
|
108
|
+
The browser build is an **IIFE** that assigns a global `jsmediatags` object:
|
|
109
|
+
|
|
110
|
+
```html
|
|
111
|
+
<script src="jsmediatags.min.js"></script>
|
|
112
|
+
<script>
|
|
113
|
+
jsmediatags.read(fileInput.files[0]).then(function(tag) {
|
|
114
|
+
console.log(tag);
|
|
115
|
+
});
|
|
116
|
+
</script>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
When installed via npm, bundlers can `require("@probityrules/jsmediatags")` or `import jsmediatags from "@probityrules/jsmediatags"` and resolve the Node build; the `"browser"` export condition points at the minified bundle for browser-targeted builds.
|
|
120
|
+
|
|
121
|
+
It supports loading files from remote hosts, Blob and File objects:
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// Callback API
|
|
125
|
+
jsmediatags.read("http://www.example.com/music-file.mp3", {
|
|
126
|
+
onSuccess: function(tag) {
|
|
127
|
+
console.log(tag);
|
|
128
|
+
},
|
|
129
|
+
onError: function(error) {
|
|
130
|
+
console.log(error);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Promise API
|
|
135
|
+
const tag = await jsmediatags.read("https://www.example.com/music-file.mp3");
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Note that the URI has to include the scheme (e.g.: https://), as relative URIs are not supported.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// From Blob
|
|
142
|
+
jsmediatags.read(blob, ...);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// From File
|
|
147
|
+
inputTypeFile.addEventListener("change", function(event) {
|
|
148
|
+
var file = event.target.files[0];
|
|
149
|
+
jsmediatags.read(file, ...);
|
|
150
|
+
}, false);
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### React Native
|
|
154
|
+
|
|
155
|
+
Install the library, then the optional peer dependencies used by the React Native file reader:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npm install @probityrules/jsmediatags
|
|
159
|
+
npm install buffer react-native-fs
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
(`buffer` and `react-native-fs` are declared as optional `peerDependencies` in `package.json`.)
|
|
163
|
+
|
|
164
|
+
Usage is the same as in Node.js:
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
const jsmediatags = require('@probityrules/jsmediatags');
|
|
168
|
+
|
|
169
|
+
// Callback API
|
|
170
|
+
new jsmediatags.Reader('/path/to/song.mp3')
|
|
171
|
+
.read({
|
|
172
|
+
onSuccess: (tag) => {
|
|
173
|
+
console.log(tag);
|
|
174
|
+
},
|
|
175
|
+
onError: (error) => {
|
|
176
|
+
console.log(error.type, error.info);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Promise API
|
|
181
|
+
const tag = await jsmediatags.readAsync('/path/to/song.mp3');
|
|
182
|
+
// or
|
|
183
|
+
const tag2 = await new jsmediatags.Reader('/path/to/song.mp3').read();
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Articles
|
|
187
|
+
|
|
188
|
+
* [Cordova : lire les metadatas des mp3s avec jsmediatags](http://blog.luce.pro/2016/02/28/Phonegap-lire-les-metadatas-des-mp3s-avec-jsmediatags/)
|
|
189
|
+
|
|
190
|
+
## Documentation
|
|
191
|
+
|
|
192
|
+
### The Output
|
|
193
|
+
|
|
194
|
+
This is an example of the object passed to the `jsmediatags.read`'s `onSuccess` callback.
|
|
195
|
+
|
|
196
|
+
#### ID3v2
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
{
|
|
200
|
+
type: "ID3",
|
|
201
|
+
version: "2.4.0",
|
|
202
|
+
major: 4,
|
|
203
|
+
revision: 0,
|
|
204
|
+
tags: {
|
|
205
|
+
artist: "Sam, The Kid",
|
|
206
|
+
album: "Pratica(mente)",
|
|
207
|
+
track: "12",
|
|
208
|
+
TPE1: {
|
|
209
|
+
id: "TPE1",
|
|
210
|
+
size: 14,
|
|
211
|
+
description: "Lead performer(s)/Soloist(s)",
|
|
212
|
+
data: "Sam, The Kid"
|
|
213
|
+
},
|
|
214
|
+
TALB: {
|
|
215
|
+
id: "TALB",
|
|
216
|
+
size: 16,
|
|
217
|
+
description: "Album/Movie/Show title",
|
|
218
|
+
data: "Pratica(mente)"
|
|
219
|
+
},
|
|
220
|
+
TRCK: {
|
|
221
|
+
id: "TRCK",
|
|
222
|
+
size: 3,
|
|
223
|
+
description: "Track number/Position in set",
|
|
224
|
+
data: "12",
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
size: 34423,
|
|
228
|
+
flags: {
|
|
229
|
+
unsynchronisation: false,
|
|
230
|
+
extended_header: false,
|
|
231
|
+
experimental_indicator: false,
|
|
232
|
+
footer_present: false
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
#### MP4
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
{
|
|
241
|
+
type: "MP4",
|
|
242
|
+
ftyp: "M4A",
|
|
243
|
+
version: 0,
|
|
244
|
+
tags: {
|
|
245
|
+
"©too": {
|
|
246
|
+
id: "©too",
|
|
247
|
+
size: 35,
|
|
248
|
+
description: 'Encoding Tool',
|
|
249
|
+
data: 'Lavf53.24.2'
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
#### FLAC
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
{
|
|
259
|
+
type: "FLAC",
|
|
260
|
+
version: "1",
|
|
261
|
+
tags: {
|
|
262
|
+
title: "16/12/95",
|
|
263
|
+
artist: "Sam, The Kid",
|
|
264
|
+
album: "Pratica(mente)",
|
|
265
|
+
track: "12",
|
|
266
|
+
picture: ...
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
The `tags` property includes all tags that were found or specified to be read.
|
|
272
|
+
Since each tag type (e.g.: ID3, MP4) uses different tag names for the same type of data (e.g.: the artist name) the most common tags are also available under human readable names (aka shortcuts). In this example, `artist` will point to `TPE1.data`, `album` to `TALB.data` and so forth.
|
|
273
|
+
|
|
274
|
+
The expected tag object depends on the type of tag read (ID3, MP4, etc.) but they all share a common structure:
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
{
|
|
278
|
+
type: <the tag type: ID3, MP4, etc.>
|
|
279
|
+
tags: {
|
|
280
|
+
<shortcut name>: <points to a tags data>
|
|
281
|
+
<tag name>: {
|
|
282
|
+
id: <tag name>,
|
|
283
|
+
data: <the actual tag data>
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Shortcuts
|
|
290
|
+
|
|
291
|
+
These are the supported shortcuts.
|
|
292
|
+
|
|
293
|
+
* `title`
|
|
294
|
+
* `artist`
|
|
295
|
+
* `album`
|
|
296
|
+
* `year`
|
|
297
|
+
* `comment`
|
|
298
|
+
* `track`
|
|
299
|
+
* `genre`
|
|
300
|
+
* `picture`
|
|
301
|
+
* `lyrics`
|
|
302
|
+
|
|
303
|
+
### Picture data
|
|
304
|
+
|
|
305
|
+
The `picture` tag contains an array buffer of all the bytes of the album artwork image as well as the content type of the image. The data can be converted and displayed as an image using:
|
|
306
|
+
|
|
307
|
+
```javascript
|
|
308
|
+
const picture = result.tags.picture;
|
|
309
|
+
let base64String = "";
|
|
310
|
+
for (let i = 0; i < picture.data.length; i++) {
|
|
311
|
+
base64String += String.fromCharCode(picture.data[i]);
|
|
312
|
+
}
|
|
313
|
+
img.src = `data:${picture.format};base64,${window.btoa(base64String)}`;
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### HTTP Access Control (CORS)
|
|
317
|
+
|
|
318
|
+
When using HTTP [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) requests you need to make sure that the server is configured to receive `If-Modified-Since` and `Range` headers with the request.
|
|
319
|
+
This can be configured by returning the `Access-Control-Allow-Headers` HTTP header with the OPTIONS request response.
|
|
320
|
+
|
|
321
|
+
Similarly, you should also allow for the browser to read the `Content-Length` and `Content-Range` headers. This can be configured by returning the `Access-Control-Expose-Headers` HTTP header.
|
|
322
|
+
|
|
323
|
+
In short, the following headers are expected:
|
|
324
|
+
|
|
325
|
+
```http
|
|
326
|
+
Access-Control-Allow-Headers: If-Modified-Since, Range
|
|
327
|
+
Access-Control-Expose-Headers: Content-Length, Content-Range
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
This library still works without these options configured on the server. However it will download the entire file instead of only the necessary bytes for reading the tags.
|
|
331
|
+
|
|
332
|
+
### File and Tag Readers
|
|
333
|
+
|
|
334
|
+
This library uses file readers (MediaFileReader API) to read the file itself and media tag readers (MediaTagReader API) to parse the tags in the file.
|
|
335
|
+
|
|
336
|
+
By default the library will automatically pick the most appropriate file reader depending on the file location. In the common case this will be the URL or local path where the file is located.
|
|
337
|
+
|
|
338
|
+
A similar approach is taken for the tag reader. The most appropriate tag reader will be selected depending on the tag signature found in the file.
|
|
339
|
+
|
|
340
|
+
However, you can specify exactly which file reader or tag reader to use using the advanced API.
|
|
341
|
+
|
|
342
|
+
New file and tag readers can be implemented by extending the MediaFileReader and MediaTagReader classes. Check the **Development** section below for more information.
|
|
343
|
+
|
|
344
|
+
### TypeScript
|
|
345
|
+
|
|
346
|
+
Types are published with the package. Import the default export and optionally use tag types from the compiled declarations:
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import jsmediatags from "@probityrules/jsmediatags";
|
|
350
|
+
import type { TagType } from "@probityrules/jsmediatags/types";
|
|
351
|
+
|
|
352
|
+
const tag: TagType = await jsmediatags.readAsync("./music-file.mp3");
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
When developing this repo locally, shared source types live in [`src/types.ts`](src/types.ts).
|
|
356
|
+
|
|
357
|
+
### Reference
|
|
358
|
+
|
|
359
|
+
* `jsmediatags.read(location, callbacks?)` — Read tags from a file path, URL, Blob, or Buffer. Returns `void` with callbacks, or `Promise<Tag>` when callbacks are omitted.
|
|
360
|
+
* `jsmediatags.readAsync(location)` — Same as `read(location)` without callbacks.
|
|
361
|
+
|
|
362
|
+
* `jsmediatags.Reader`
|
|
363
|
+
* `setTagsToRead(tags: Array<string>)` — Specify which tags to read
|
|
364
|
+
* `setFileReader(fileReader: typeof MediaFileReader)` — Use this particular file reader
|
|
365
|
+
* `setTagReader(tagReader: typeof MediaTagReader)` — Use this particular tag reader
|
|
366
|
+
* `read(callbacks?)` — Read the tags. With callbacks returns `void`; without callbacks returns `Promise<Tag>`.
|
|
367
|
+
* `readAsync()` — Same as `read()` without callbacks.
|
|
368
|
+
|
|
369
|
+
* `jsmediatags.Config`
|
|
370
|
+
* `addFileReader(fileReader: typeof MediaFileReader)` - Add a new file reader to the automatic detection system.
|
|
371
|
+
* `addTagReader(tagReader: typeof MediaTagReader)` - Add a new tag reader to the automatic detection system.
|
|
372
|
+
* `setDisallowedXhrHeaders(disallowedXhrHeaders: Array<string>)` - Prevent the library from using specific http headers. This can be useful when dealing with CORS enabled servers you don't control.
|
|
373
|
+
* `setXhrTimeoutInSec(timeoutInSec: number)` - Sets the timeout time for http requests. Set it to 0 for no timeout at all. It defaults to 30s.
|
|
374
|
+
|
|
375
|
+
## Development
|
|
376
|
+
|
|
377
|
+
Source code is written in TypeScript. Run `npm run build` to compile the Node/CommonJS output into the `build` directory.
|
|
378
|
+
|
|
379
|
+
### NodeJS Development
|
|
380
|
+
|
|
381
|
+
Run `npm run build` to generate proper JavaScript code into the `build` directory.
|
|
382
|
+
|
|
383
|
+
```javascript
|
|
384
|
+
var NodeFileReader = require('./build/NodeFileReader');
|
|
385
|
+
var ID3v2TagReader = require('./build/ID3v2TagReader');
|
|
386
|
+
...
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
Run `npm run watch` to automatically recompile the source code whenever a file is changed.
|
|
390
|
+
|
|
391
|
+
### Browser Development
|
|
392
|
+
|
|
393
|
+
Run `npm run dist` to generate browser bundles with esbuild: `dist/jsmediatags.js` (debug) and `dist/jsmediatags.min.js` (production). Both expose a global `jsmediatags` object when loaded via a script tag.
|
|
394
|
+
|
|
395
|
+
Run `npm run dist-watch` to rebuild the browser bundle whenever a source file changes.
|
|
396
|
+
|
|
397
|
+
### New File Readers
|
|
398
|
+
|
|
399
|
+
Extend the `MediaFileReader` class to implement a new file reader. Methods to implement are:
|
|
400
|
+
|
|
401
|
+
* init
|
|
402
|
+
* loadRange
|
|
403
|
+
* getBytesLoaded
|
|
404
|
+
* getByteAt
|
|
405
|
+
|
|
406
|
+
Current Implementations:
|
|
407
|
+
|
|
408
|
+
* [NodeFileReader](https://github.com/aadsm/jsmediatags/blob/master/src/NodeFileReader.ts) (NodeJS)
|
|
409
|
+
* [XhrFileReader](https://github.com/aadsm/jsmediatags/blob/master/src/XhrFileReader.ts) (Browser and NodeJS)
|
|
410
|
+
* [BlobFileReader](https://github.com/aadsm/jsmediatags/blob/master/src/BlobFileReader.ts) (Blob and File)
|
|
411
|
+
|
|
412
|
+
### New Tag Readers
|
|
413
|
+
|
|
414
|
+
Extend the `MediaTagReader` class to implement a new tag reader. Methods to implement are:
|
|
415
|
+
|
|
416
|
+
* getTagIdentifierByteRange
|
|
417
|
+
* canReadTagFormat
|
|
418
|
+
* \_loadData
|
|
419
|
+
* \_parseData
|
|
420
|
+
|
|
421
|
+
Current Implementations:
|
|
422
|
+
|
|
423
|
+
* [ID3v1TagReader](https://github.com/aadsm/jsmediatags/blob/master/src/ID3v1TagReader.ts)
|
|
424
|
+
* [ID3v2TagReader](https://github.com/aadsm/jsmediatags/blob/master/src/ID3v2TagReader.ts)
|
|
425
|
+
* [MP4TagReader](https://github.com/aadsm/jsmediatags/blob/master/src/MP4TagReader.ts)
|
|
426
|
+
* [FLACTagReader](https://github.com/aadsm/jsmediatags/blob/master/src/FLACTagReader.ts)
|
|
427
|
+
|
|
428
|
+
### Publishing
|
|
429
|
+
|
|
430
|
+
The package is published to npm as **`@probityrules/jsmediatags`**. Only distributable artifacts are included (`build/`, `dist/jsmediatags.min.js`, docs); `src/` and `test/` stay in the repository only.
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
npm run build && npm run dist # prepublishOnly runs this automatically
|
|
434
|
+
npm publish --access public
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### Unit Testing
|
|
438
|
+
|
|
439
|
+
Tests live in the [`test/`](test/) directory. Run `npm test` to execute the suite, or `npm run test:watch` during development. CI runs `npm run build`, `npm run dist`, and `npm test` on every push and pull request.
|
|
440
|
+
|
|
441
|
+
## Upgrading from 3.x to 4.0
|
|
442
|
+
|
|
443
|
+
4.0 is a modernization release. The tag-reading API is backward compatible if you keep using callbacks, but packaging and runtime expectations changed:
|
|
444
|
+
|
|
445
|
+
| Topic | 3.x | 4.0 |
|
|
446
|
+
| --- | --- | --- |
|
|
447
|
+
| Node.js | Older versions often worked | **Node 18+** required |
|
|
448
|
+
| npm `main` | Previous layout | `build/jsmediatags.js` (prebuilt on publish) |
|
|
449
|
+
| Browser bundle | Browserify / UMD-style | **esbuild IIFE** (`dist/jsmediatags.min.js`) |
|
|
450
|
+
| Source | Flow / JavaScript | **TypeScript** |
|
|
451
|
+
| Promise API | Not built in | `read()` / `readAsync()` without callbacks |
|
|
452
|
+
| React Native extras | `optionalPeerDependencies` | Standard **optional** `peerDependencies` |
|
|
453
|
+
|
|
454
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full list.
|
|
455
|
+
|
|
456
|
+
## JavaScript-ID3-Reader
|
|
457
|
+
|
|
458
|
+
If you want to migrate your project from [JavaScript-ID3-Reader](https://github.com/aadsm/JavaScript-ID3-Reader) to `jsmediatags` use the following guiding examples:
|
|
459
|
+
|
|
460
|
+
### All tags
|
|
461
|
+
|
|
462
|
+
**JavaScript-ID3-Reader:**
|
|
463
|
+
|
|
464
|
+
```javascript
|
|
465
|
+
ID3.loadTags("filename.mp3", function() {
|
|
466
|
+
var tags = ID3.getAllTags("filename.mp3");
|
|
467
|
+
alert(tags.artist + " - " + tags.title + ", " + tags.album);
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**jsmediatags:**
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
jsmediatags.read("filename.mp3", {
|
|
475
|
+
onSuccess: function(tag) {
|
|
476
|
+
var tags = tag.tags;
|
|
477
|
+
alert(tags.artist + " - " + tags.title + ", " + tags.album);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
### Specific tags
|
|
483
|
+
|
|
484
|
+
**JavaScript-ID3-Reader:**
|
|
485
|
+
|
|
486
|
+
```javascript
|
|
487
|
+
ID3.loadTags("filename.mp3", function() {
|
|
488
|
+
var tags = ID3.getAllTags("filename.mp3");
|
|
489
|
+
alert(tags.COMM.data + " - " + tags.TCON.data + ", " + tags.WXXX.data);
|
|
490
|
+
},
|
|
491
|
+
{tags: ["COMM", "TCON", "WXXX"]});
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
**jsmediatags:**
|
|
495
|
+
|
|
496
|
+
```javascript
|
|
497
|
+
new jsmediatags.Reader("filename.mp3")
|
|
498
|
+
.setTagsToRead(["COMM", "TCON", "WXXX"])
|
|
499
|
+
.read({
|
|
500
|
+
onSuccess: function(tag) {
|
|
501
|
+
var tags = tag.tags;
|
|
502
|
+
alert(tags.COMM.data + " - " + tags.TCON.data + ", " + tags.WXXX.data);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Error handling
|
|
508
|
+
|
|
509
|
+
**JavaScript-ID3-Reader:**
|
|
510
|
+
|
|
511
|
+
```javascript
|
|
512
|
+
ID3.loadTags("http://localhost/filename.mp3", function() {
|
|
513
|
+
var tags = ID3.getAllTags("http://localhost/filename.mp3");
|
|
514
|
+
alert(tags.comment + " - " + tags.track + ", " + tags.lyrics);
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
tags: ["comment", "track", "lyrics"],
|
|
518
|
+
onError: function(reason) {
|
|
519
|
+
if (reason.error === "xhr") {
|
|
520
|
+
console.log("There was a network error: ", reason.xhr);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
**jsmediatags:**
|
|
527
|
+
|
|
528
|
+
```javascript
|
|
529
|
+
new jsmediatags.Reader("filename.mp3")
|
|
530
|
+
.setTagsToRead(["comment", "track", "lyrics"])
|
|
531
|
+
.read({
|
|
532
|
+
onSuccess: function(tag) {
|
|
533
|
+
var tags = tag.tags;
|
|
534
|
+
alert(tags.comment + " - " + tags.track + ", " + tags.lyrics);
|
|
535
|
+
},
|
|
536
|
+
onError: function(error) {
|
|
537
|
+
if (error.type === "xhr") {
|
|
538
|
+
console.log("There was a network error: ", error.xhr);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
## Goals
|
|
545
|
+
|
|
546
|
+
* Improve the API of JavaScript-ID3-Reader
|
|
547
|
+
* Readable TypeScript source with published types and tests
|
|
548
|
+
* Support Node.js, browsers, and React Native
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare const MediaFileReader: any;
|
|
2
|
+
import type { LoadCallbackType } from "./types";
|
|
3
|
+
declare class ArrayBufferFileReader extends MediaFileReader {
|
|
4
|
+
private _buffer;
|
|
5
|
+
private _fileData;
|
|
6
|
+
constructor(buffer: ArrayBuffer);
|
|
7
|
+
static canReadFile(file: unknown): boolean;
|
|
8
|
+
_init(callbacks: LoadCallbackType): void;
|
|
9
|
+
loadRange(range: [number, number], callbacks: LoadCallbackType): void;
|
|
10
|
+
getByteAt(offset: number): number;
|
|
11
|
+
}
|
|
12
|
+
export = ArrayBufferFileReader;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const ChunkedFileData = require("./ChunkedFileData");
|
|
3
|
+
const MediaFileReader = require("./MediaFileReader");
|
|
4
|
+
class ArrayBufferFileReader extends MediaFileReader {
|
|
5
|
+
constructor(buffer) {
|
|
6
|
+
super();
|
|
7
|
+
this._buffer = buffer;
|
|
8
|
+
this._fileData = new ChunkedFileData();
|
|
9
|
+
}
|
|
10
|
+
static canReadFile(file) {
|
|
11
|
+
return typeof ArrayBuffer === "function" && file instanceof ArrayBuffer;
|
|
12
|
+
}
|
|
13
|
+
_init(callbacks) {
|
|
14
|
+
this._size = this._buffer.byteLength;
|
|
15
|
+
setTimeout(callbacks.onSuccess, 1);
|
|
16
|
+
}
|
|
17
|
+
loadRange(range, callbacks) {
|
|
18
|
+
const arrayBuf = this._buffer.slice(range[0], range[1] + 1);
|
|
19
|
+
const viewData = new Uint8Array(arrayBuf);
|
|
20
|
+
this._fileData.addData(range[0], viewData);
|
|
21
|
+
callbacks.onSuccess();
|
|
22
|
+
}
|
|
23
|
+
getByteAt(offset) {
|
|
24
|
+
return this._fileData.getByteAt(offset);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
module.exports = ArrayBufferFileReader;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const MediaFileReader: any;
|
|
2
|
+
import type { Byte, LoadCallbackType } from "./types";
|
|
3
|
+
declare class ArrayFileReader extends MediaFileReader {
|
|
4
|
+
private _array;
|
|
5
|
+
constructor(array: Byte[]);
|
|
6
|
+
static canReadFile(file: unknown): boolean;
|
|
7
|
+
init(callbacks: LoadCallbackType): void;
|
|
8
|
+
loadRange(range: [number, number], callbacks: LoadCallbackType): void;
|
|
9
|
+
getByteAt(offset: number): Byte;
|
|
10
|
+
}
|
|
11
|
+
export = ArrayFileReader;
|