UNPKG

11.4 kBJavaScriptView Raw
1const fs = require('fs')
2const ID3FrameBuilder = require("./ID3FrameBuilder")
3const ID3FrameReader = require("./ID3FrameReader")
4const ID3Definitions = require("./ID3Definitions")
5
6module.exports.GENERIC_TEXT = {
7 create: (specName, data) => {
8 if(!specName || !data) {
9 return null
10 }
11
12 return new ID3FrameBuilder(specName)
13 .appendStaticNumber(0x01, 0x01)
14 .appendStaticValue(data, null, 0x01)
15 .getBuffer()
16 },
17 read: (buffer) => {
18 const reader = new ID3FrameReader(buffer, 0)
19
20 return reader.consumeStaticValue('string')
21 }
22}
23
24module.exports.GENERIC_URL = {
25 create: (specName, data) => {
26 if(!specName || !data) {
27 return null
28 }
29
30 return new ID3FrameBuilder(specName)
31 .appendStaticValue(data)
32 .getBuffer()
33 },
34 read: (buffer) => {
35 const reader = new ID3FrameReader(buffer)
36
37 return reader.consumeStaticValue('string')
38 }
39}
40
41module.exports.APIC = {
42 create: (data) => {
43 try {
44 if (data instanceof Buffer) {
45 data = {
46 imageBuffer: Buffer.from(data)
47 }
48 } else if (typeof data === 'string' || data instanceof String) {
49 data = {
50 imageBuffer: Buffer.from(fs.readFileSync(data, 'binary'), 'binary')
51 }
52 } else if (!data.imageBuffer) {
53 return Buffer.alloc(0)
54 }
55
56 let mime_type = data.mime
57
58 if(!data.mime) {
59 if (data.imageBuffer[0] === 0xff && data.imageBuffer[1] === 0xd8 && data.imageBuffer[2] === 0xff) {
60 mime_type = "image/jpeg"
61 } else {
62 mime_type = "image/png"
63 }
64 }
65
66 return new ID3FrameBuilder("APIC")
67 .appendStaticNumber(0x01, 1)
68 .appendNullTerminatedValue(mime_type)
69 .appendStaticNumber(0x03, 1)
70 .appendNullTerminatedValue(data.description, 0x01)
71 .appendStaticValue(data.imageBuffer)
72 .getBuffer()
73 } catch(e) {
74 return e
75 }
76 },
77 read: (buffer, version) => {
78 const reader = new ID3FrameReader(buffer, 0)
79 let mime
80 if(version === 2) {
81 mime = reader.consumeStaticValue('string', 3, 0x00)
82 } else {
83 mime = reader.consumeNullTerminatedValue('string', 0x00)
84 }
85 if(mime === "image/jpeg") {
86 mime = "jpeg"
87 } else if(mime === "image/png") {
88 mime = "png"
89 }
90
91 const typeId = reader.consumeStaticValue('number', 1)
92 const description = reader.consumeNullTerminatedValue('string')
93 const imageBuffer = reader.consumeStaticValue()
94
95 return {
96 mime: mime,
97 type: {
98 id: typeId,
99 name: ID3Definitions.APIC_TYPES[typeId]
100 },
101 description: description,
102 imageBuffer: imageBuffer
103 }
104 }
105}
106
107module.exports.COMM = {
108 create: (data) => {
109 data = data || {}
110 if(!data.text) {
111 return null
112 }
113
114 return new ID3FrameBuilder("COMM")
115 .appendStaticNumber(0x01, 1)
116 .appendStaticValue(data.language)
117 .appendNullTerminatedValue(data.shortText, 0x01)
118 .appendStaticValue(data.text, null, 0x01)
119 .getBuffer()
120 },
121 read: (buffer) => {
122 const reader = new ID3FrameReader(buffer, 0)
123
124 return {
125 language: reader.consumeStaticValue('string', 3, 0x00),
126 shortText: reader.consumeNullTerminatedValue('string'),
127 text: reader.consumeStaticValue('string', null)
128 }
129 }
130}
131
132module.exports.USLT = {
133 create: (data) => {
134 data = data || {}
135 if(typeof data === 'string' || data instanceof String) {
136 data = {
137 text: data
138 }
139 }
140 if(!data.text) {
141 return null
142 }
143
144 return new ID3FrameBuilder("USLT")
145 .appendStaticNumber(0x01, 1)
146 .appendStaticValue(data.language)
147 .appendNullTerminatedValue(data.shortText, 0x01)
148 .appendStaticValue(data.text, null, 0x01)
149 .getBuffer()
150 },
151 read: (buffer) => {
152 const reader = new ID3FrameReader(buffer, 0)
153
154 return {
155 language: reader.consumeStaticValue('string', 3, 0x00),
156 shortText: reader.consumeNullTerminatedValue('string'),
157 text: reader.consumeStaticValue('string', null)
158 }
159 }
160}
161
162module.exports.TXXX = {
163 create: (data) => {
164 if(!(data instanceof Array)) {
165 data = [data]
166 }
167
168 return Buffer.concat(data.map(udt => new ID3FrameBuilder("TXXX")
169 .appendStaticNumber(0x01, 1)
170 .appendNullTerminatedValue(udt.description, 0x01)
171 .appendStaticValue(udt.value, null, 0x01)
172 .getBuffer()))
173 },
174 read: (buffer) => {
175 const reader = new ID3FrameReader(buffer, 0)
176
177 return {
178 description: reader.consumeNullTerminatedValue('string'),
179 value: reader.consumeStaticValue('string')
180 }
181 }
182}
183
184module.exports.POPM = {
185 create: (data) => {
186 const email = data.email
187 let rating = Math.trunc(data.rating)
188 let counter = Math.trunc(data.counter)
189 if(!email) {
190 return null
191 }
192 if(isNaN(rating) || rating < 0 || rating > 255) {
193 rating = 0
194 }
195 if(isNaN(counter) || counter < 0) {
196 counter = 0
197 }
198
199 return new ID3FrameBuilder("POPM")
200 .appendNullTerminatedValue(email)
201 .appendStaticNumber(rating, 1)
202 .appendStaticNumber(counter, 4)
203 .getBuffer()
204 },
205 read: (buffer) => {
206 const reader = new ID3FrameReader(buffer)
207 return {
208 email: reader.consumeNullTerminatedValue('string'),
209 rating: reader.consumeStaticValue('number', 1),
210 counter: reader.consumeStaticValue('number')
211 }
212 }
213}
214
215module.exports.PRIV = {
216 create: (data) => {
217 if(!(data instanceof Array)) {
218 data = [data]
219 }
220
221 return Buffer.concat(data.map(priv => new ID3FrameBuilder("PRIV")
222 .appendNullTerminatedValue(priv.ownerIdentifier)
223 .appendStaticValue(priv.data instanceof Buffer ? priv.data : Buffer.from(priv.data, "utf8"))
224 .getBuffer()))
225 },
226 read: (buffer) => {
227 const reader = new ID3FrameReader(buffer)
228 return {
229 ownerIdentifier: reader.consumeNullTerminatedValue('string'),
230 data: reader.consumeStaticValue()
231 }
232 }
233}
234
235module.exports.CHAP = {
236 create: (data, version, nodeId3) => {
237 if (!(data instanceof Array)) {
238 data = [data]
239 }
240
241 return Buffer.concat(data.map(chap => {
242 if (!chap || !chap.elementID || typeof chap.startTimeMs === "undefined" || !chap.endTimeMs) {
243 return null
244 }
245 return new ID3FrameBuilder("CHAP")
246 .appendNullTerminatedValue(chap.elementID)
247 .appendStaticNumber(chap.startTimeMs, 4)
248 .appendStaticNumber(chap.endTimeMs, 4)
249 .appendStaticNumber(chap.startOffsetBytes ? chap.startOffsetBytes : 0xFFFFFFFF, 4)
250 .appendStaticNumber(chap.endOffsetBytes ? chap.endOffsetBytes : 0xFFFFFFFF, 4)
251 .appendStaticValue(nodeId3.create(chap.tags).slice(10))
252 .getBuffer()
253 }).filter(chap => chap instanceof Buffer))
254 },
255 read: (buffer, version, nodeId3) => {
256 const reader = new ID3FrameReader(buffer)
257 let chap = {
258 elementID: reader.consumeNullTerminatedValue('string'),
259 startTimeMs: reader.consumeStaticValue('number', 4),
260 endTimeMs: reader.consumeStaticValue('number', 4),
261 startOffsetBytes: reader.consumeStaticValue('number', 4),
262 endOffsetBytes: reader.consumeStaticValue('number', 4),
263 tags: nodeId3.getTagsFromFrames(nodeId3.getFramesFromID3Body(reader.consumeStaticValue(), 3, 4, 10), 3)
264 }
265 if(chap.startOffsetBytes === 0xFFFFFFFF) delete chap.startOffsetBytes
266 if(chap.endOffsetBytes === 0xFFFFFFFF) delete chap.endOffsetBytes
267 return chap
268 }
269}
270
271module.exports.CTOC = {
272 create: (data, version, nodeId3) => {
273 if(!(data instanceof Array)) {
274 data = [data]
275 }
276
277 return Buffer.concat(data.map((toc, index) => {
278 if(!toc || !toc.elementID) {
279 return null
280 }
281 if(!(toc.elements instanceof Array)) {
282 toc.elements = []
283 }
284
285 let ctocFlags = Buffer.alloc(1, 0)
286 if(index === 0) {
287 ctocFlags[0] += 2
288 }
289 if(toc.isOrdered) {
290 ctocFlags[0] += 1
291 }
292
293 const builder = new ID3FrameBuilder("CTOC")
294 .appendNullTerminatedValue(toc.elementID)
295 .appendStaticValue(ctocFlags, 1)
296 .appendStaticNumber(toc.elements.length, 1)
297 toc.elements.forEach((el) => {
298 builder.appendNullTerminatedValue(el)
299 })
300 if(toc.tags) {
301 builder.appendStaticValue(nodeId3.create(toc.tags).slice(10))
302 }
303 return builder.getBuffer()
304 }).filter((toc) => toc instanceof Buffer))
305 },
306 read: (buffer, version, nodeId3) => {
307 const reader = new ID3FrameReader(buffer)
308 const elementID = reader.consumeNullTerminatedValue('string')
309 const flags = reader.consumeStaticValue('number', 1)
310 const entries = reader.consumeStaticValue('number', 1)
311 const elements = []
312 for(let i = 0; i < entries; i++) {
313 elements.push(reader.consumeNullTerminatedValue('string'))
314 }
315 const tags = nodeId3.getTagsFromFrames(nodeId3.getFramesFromID3Body(reader.consumeStaticValue(), 3, 4, 10), 3)
316
317 return {
318 elementID,
319 isOrdered: !!(flags & 0x01 === 0x01),
320 elements,
321 tags
322 }
323 }
324}
325
326module.exports.WXXX = {
327 create: (data) => {
328 if(!(data instanceof Array)) {
329 data = [data]
330 }
331
332 return Buffer.concat(data.map((udu) => {
333 return new ID3FrameBuilder("WXXX")
334 .appendStaticNumber(0x01, 1)
335 .appendNullTerminatedValue(udu.description, 0x01)
336 .appendStaticValue(udu.url, null)
337 .getBuffer()
338 }))
339 },
340 read: (buffer) => {
341 const reader = new ID3FrameReader(buffer, 0)
342
343 return {
344 description: reader.consumeNullTerminatedValue('string'),
345 url: reader.consumeStaticValue('string', null, 0x00)
346 }
347 }
348}