Passed
Push — master ( ac0387...4f4074 )
by Jan
01:24
created

index.js (20 issues)

1
const fs = require('fs')
2
const ID3Definitions = require("./src/ID3Definitions")
3
const ID3Frames = require('./src/ID3Frames')
4
const ID3Util = require('./src/ID3Util')
5
const zlib = require('zlib')
6
7
/*
8
**  Used specification: http://id3.org/id3v2.3.0
9
*/
10
11
/**
12
 * Write passed tags to a file/buffer
13
 * @param tags - Object containing tags to be written
14
 * @param filebuffer - Can contain a filepath string or buffer
15
 * @param fn - (optional) Function for async version
16
 * @returns {boolean|Buffer|Error}
17
 */
18
module.exports.write = function(tags, filebuffer, fn) {
19
    let completeTag = this.create(tags)
20
    if(filebuffer instanceof Buffer) {
21
        filebuffer = this.removeTagsFromBuffer(filebuffer) || filebuffer
22
        let completeBuffer = Buffer.concat([completeTag, filebuffer])
23
        if(fn && typeof fn === 'function') {
24
            fn(null, completeBuffer)
25
            return
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
26
        } else {
27
            return completeBuffer
28
        }
29
    }
30
31
    if(fn && typeof fn === 'function') {
32
        try {
33
            fs.readFile(filebuffer, function(err, data) {
34
                if(err) {
35
                    fn(err)
36
                    return
37
                }
38
                data = this.removeTagsFromBuffer(data) || data
39
                let rewriteFile = Buffer.concat([completeTag, data])
40
                fs.writeFile(filebuffer, rewriteFile, 'binary', (err) => {
41
                    fn(err)
42
                })
43
            }.bind(this))
44
        } catch(err) {
45
            fn(err)
46
        }
47
    } else {
48
        try {
49
            let data = fs.readFileSync(filebuffer)
50
            data = this.removeTagsFromBuffer(data) || data
51
            let rewriteFile = Buffer.concat([completeTag, data])
52
            fs.writeFileSync(filebuffer, rewriteFile, 'binary')
53
            return true
54
        } catch(err) {
55
            return err
56
        }
57
    }
58
}
59
60
/**
61
 * Creates a buffer containing the ID3 Tag
62
 * @param tags - Object containing tags to be written
63
 * @param fn fn - (optional) Function for async version
64
 * @returns {Buffer}
65
 */
66
module.exports.create = function(tags, fn) {
67
    let frames = []
68
69
    //  Create & push a header for the ID3-Frame
70
    const header = Buffer.alloc(10)
71
    header.fill(0)
72
    header.write("ID3", 0)              //File identifier
73
    header.writeUInt16BE(0x0300, 3)     //Version 2.3.0  --  03 00
74
    header.writeUInt16BE(0x0000, 5)     //Flags 00
75
76
    //Last 4 bytes are used for header size, but have to be inserted later, because at this point, its size is not clear.
77
    frames.push(header)
78
79
    frames = frames.concat(this.createBuffersFromTags(tags))
80
81
    //  Calculate frame size of ID3 body to insert into header
82
83
    let totalSize = 0
84
    frames.forEach((frame) => {
85
        totalSize += frame.length
86
    })
87
88
    //  Don't count ID3 header itself
89
    totalSize -= 10
90
    //  ID3 header size uses only 7 bits of a byte, bit shift is needed
91
    let size = ID3Util.encodeSize(totalSize)
92
93
    //  Write bytes to ID3 frame header, which is the first frame
94
    frames[0].writeUInt8(size[0], 6)
95
    frames[0].writeUInt8(size[1], 7)
96
    frames[0].writeUInt8(size[2], 8)
97
    frames[0].writeUInt8(size[3], 9)
98
99
    if(fn && typeof fn === 'function') {
100
        fn(Buffer.concat(frames))
101
    } else {
102
        return Buffer.concat(frames)
103
    }
104
}
105
106
/**
107
 * Returns array of buffers created by tags specified in the tags argument
108
 * @param tags - Object containing tags to be written
109
 * @returns {Array}
110
 */
111
module.exports.createBuffersFromTags = function(tags) {
112
    let frames = []
113
    if(!tags) return frames
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
114
    const rawObject = Object.keys(tags).reduce((acc, val) => {
115
        if(ID3Definitions.FRAME_IDENTIFIERS.v3[val] !== undefined) {
116
            acc[ID3Definitions.FRAME_IDENTIFIERS.v3[val]] = tags[val]
117
        } else {
118
            acc[val] = tags[val]
119
        }
120
        return acc
121
    }, {})
122
123
    Object.keys(rawObject).forEach((specName, index) => {
0 ignored issues
show
The parameter index is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
124
        let frame
125
        // Check if invalid specName
126
        if(specName.length !== 4) {
127
            return
128
        }
129
        if(ID3Frames[specName] !== undefined) {
130
            frame = ID3Frames[specName].create(rawObject[specName], 3, this)
131
        } else if(specName.startsWith('T')) {
132
            frame = ID3Frames.GENERIC_TEXT.create(specName, rawObject[specName], 3)
133
        } else if(specName.startsWith('W')) {
134
            if(ID3Util.getSpecOptions(specName, 3).multiple && rawObject[specName] instanceof Array && rawObject[specName].length > 0) {
135
                frame = Buffer.alloc(0)
136
                // deduplicate array
137
                for(let url of [...new Set(rawObject[specName])]) {
138
                    frame = Buffer.concat([frame, ID3Frames.GENERIC_URL.create(specName, url, 3)])
139
                }
140
            } else {
141
                frame = ID3Frames.GENERIC_URL.create(specName, rawObject[specName], 3)
142
            }
143
        }
144
145
        if (frame instanceof Buffer) {
0 ignored issues
show
The variable frame does not seem to be initialized in case specName.startsWith("W") on line 133 is false. Are you sure this can never be the case?
Loading history...
146
            frames.push(frame)
147
        }
148
    })
149
150
    return frames
151
}
152
153
/**
154
 * Read ID3-Tags from passed buffer/filepath
155
 * @param filebuffer - Can contain a filepath string or buffer
156
 * @param options - (optional) Object containing options
157
 * @param fn - (optional) Function for async version
158
 * @returns {boolean}
159
 */
160
module.exports.read = function(filebuffer, options, fn) {
161
    if(!options || typeof options === 'function') {
162
        fn = fn || options
163
        options = {}
164
    }
165
    if(!fn || typeof fn !== 'function') {
166
        if(typeof filebuffer === "string" || filebuffer instanceof String) {
167
            filebuffer = fs.readFileSync(filebuffer)
168
        }
169
        return this.getTagsFromBuffer(filebuffer, options)
170
    } else {
171
        if(typeof filebuffer === "string" || filebuffer instanceof String) {
172
            fs.readFile(filebuffer, function(err, data) {
173
                if(err) {
174
                    fn(err, null)
175
                } else {
176
                    fn(null, this.getTagsFromBuffer(data, options))
177
                }
178
            }.bind(this))
179
        } else {
180
            fn(null, this.getTagsFromBuffer(filebuffer, options))
181
        }
182
    }
183
}
184
185
/**
186
 * Update ID3-Tags from passed buffer/filepath
187
 * @param tags - Object containing tags to be written
188
 * @param filebuffer - Can contain a filepath string or buffer
189
 * @param options - (optional) Object containing options
190
 * @param fn - (optional) Function for async version
191
 * @returns {boolean|Buffer|Error}
192
 */
193
module.exports.update = function(tags, filebuffer, options, fn) {
194
    if(!options || typeof options === 'function') {
195
        fn = fn || options
196
        options = {}
197
    }
198
199
    const rawTags = Object.keys(tags).reduce((acc, val) => {
200
        if(ID3Definitions.FRAME_IDENTIFIERS.v3[val] !== undefined) {
201
            acc[ID3Definitions.FRAME_IDENTIFIERS.v3[val]] = tags[val]
202
        } else {
203
            acc[val] = tags[val]
204
        }
205
        return acc
206
    }, {})
207
208
    const updateFn = (currentTags) => {
209
        currentTags = currentTags.raw || {}
210
        Object.keys(rawTags).map((specName) => {
211
            const options = ID3Util.getSpecOptions(specName, 3)
212
            const cCompare = {}
213
            if(options.multiple && currentTags[specName] && rawTags[specName]) {
214
                if(options.updateCompareKey) {
215
                    currentTags[specName].forEach((cTag, index) => {
216
                        cCompare[cTag[options.updateCompareKey]] = index
217
                    })
218
219
                }
220
                if (!(rawTags[specName] instanceof Array)) rawTags[specName] = [rawTags[specName]]
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
221
                rawTags[specName].forEach((rTag, index) => {
0 ignored issues
show
The parameter index is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
222
                    const comparison = cCompare[rTag[options.updateCompareKey]]
223
                    if (comparison !== undefined) {
224
                        currentTags[specName][comparison] = rTag
225
                    } else {
226
                        currentTags[specName].push(rTag)
227
                    }
228
                })
229
            } else {
230
                currentTags[specName] = rawTags[specName]
231
            }
232
        })
233
234
        return currentTags
235
    }
236
237
    if(!fn || typeof fn !== 'function') {
238
        return this.write(updateFn(this.read(filebuffer, options)), filebuffer)
239
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
240
        this.read(filebuffer, (err, currentTags) => {
0 ignored issues
show
The parameter currentTags is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
The parameter err is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
241
            this.write(updateFn(this.read(filebuffer, options)), filebuffer, fn)
242
        })
243
    }
244
}
245
246
module.exports.getTagsFromBuffer = function(filebuffer, options) {
247
    let framePosition = ID3Util.getFramePosition(filebuffer)
248
    if(framePosition === -1) {
249
        return this.getTagsFromFrames([], 3, options)
250
    }
251
    const frameSize = ID3Util.decodeSize(filebuffer.slice(framePosition + 6, framePosition + 10)) + 10
252
    let ID3Frame = Buffer.alloc(frameSize + 1)
253
    filebuffer.copy(ID3Frame, 0, framePosition)
254
    //ID3 version e.g. 3 if ID3v2.3.0
255
    let ID3Version = ID3Frame[3]
256
    const tagFlags = ID3Util.parseTagHeaderFlags(ID3Frame)
257
    let extendedHeaderOffset = 0
258
    if(tagFlags.extendedHeader) {
259
        if(ID3Version === 3) {
260
            extendedHeaderOffset = 4 + filebuffer.readUInt32BE(10)
261
        } else if(ID3Version === 4) {
262
            extendedHeaderOffset = ID3Util.decodeSize(filebuffer.slice(10, 14))
263
        }
264
    }
265
    let ID3FrameBody = Buffer.alloc(frameSize - 10 - extendedHeaderOffset)
266
    filebuffer.copy(ID3FrameBody, 0, framePosition + 10 + extendedHeaderOffset)
267
268
    let frames = this.getFramesFromID3Body(ID3FrameBody, ID3Version, options)
269
270
    return this.getTagsFromFrames(frames, ID3Version, options)
271
}
272
273
module.exports.getFramesFromID3Body = function(ID3FrameBody, ID3Version, options = {}) {
274
    let currentPosition = 0
275
    let frames = []
276
    if(!ID3FrameBody || !(ID3FrameBody instanceof Buffer)) {
277
        return frames
278
    }
279
280
    let identifierSize = 4
281
    let textframeHeaderSize = 10
282
    if(ID3Version === 2) {
283
        identifierSize = 3
284
        textframeHeaderSize = 6
285
    }
286
287
    while(currentPosition < ID3FrameBody.length && ID3FrameBody[currentPosition] !== 0x00) {
288
        let bodyFrameHeader = Buffer.alloc(textframeHeaderSize)
289
        ID3FrameBody.copy(bodyFrameHeader, 0, currentPosition)
290
291
        let decodeSize = false
292
        if(ID3Version === 4) {
293
            decodeSize = true
294
        }
295
        let bodyFrameSize = ID3Util.getFrameSize(bodyFrameHeader, decodeSize, ID3Version)
296
        if(bodyFrameSize + 10 > (ID3FrameBody.length - currentPosition)) {
297
            break
298
        }
299
        const specName = bodyFrameHeader.toString('utf8', 0, identifierSize)
300
        if(options.exclude instanceof Array && options.exclude.includes(specName) || options.include instanceof Array && !options.include.includes(specName)) {
301
            currentPosition += bodyFrameSize + textframeHeaderSize
302
            continue
303
        }
304
        const frameHeaderFlags = ID3Util.parseFrameHeaderFlags(bodyFrameHeader, ID3Version)
305
        let bodyFrameBuffer = Buffer.alloc(bodyFrameSize)
306
        ID3FrameBody.copy(bodyFrameBuffer, 0, currentPosition + textframeHeaderSize + (frameHeaderFlags.dataLengthIndicator ? 4 : 0))
307
        //  Size of sub frame + its header
308
        currentPosition += bodyFrameSize + textframeHeaderSize
309
        frames.push({
310
            name: specName,
311
            flags: frameHeaderFlags,
312
            body: frameHeaderFlags.unsynchronisation ? ID3Util.processUnsynchronisedBuffer(bodyFrameBuffer) : bodyFrameBuffer
313
        })
314
    }
315
316
    return frames
317
}
318
319
module.exports.getTagsFromFrames = function(frames, ID3Version, options = {}) {
320
    let tags = { }
321
    let raw = { }
322
323
    frames.forEach((frame, index) => {
0 ignored issues
show
The parameter index is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
324
        const specName = ID3Version === 2 ? ID3Definitions.FRAME_IDENTIFIERS.v3[ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v2[frame.name]] : frame.name
325
        const identifier = ID3Version === 2 ? ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v2[frame.name] : ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v3[frame.name]
326
327
        if(!specName || !identifier || frame.flags.encryption) {
328
            return
329
        }
330
331
        if(frame.flags.compression) {
332
            if(frame.body.length < 5) {
333
                return
334
            }
335
            const inflatedSize = frame.body.readInt32BE()
336
            /*
337
            * ID3 spec defines that compression is stored in ZLIB format, but doesn't specify if header is present or not.
338
            * ZLIB has a 2-byte header.
339
            * 1. try if header + body decompression
340
            * 2. else try if header is not stored (assume that all content is deflated "body")
341
            * 3. else try if deflation works if the header is omitted (implementation dependent)
342
            * */
343
            try {
344
                frame.body = zlib.inflateSync(frame.body.slice(4))
345
            } catch (e) {
346
                try {
347
                    frame.body = zlib.inflateRawSync(frame.body.slice(4))
348
                } catch (e) {
349
                    try {
350
                        frame.body = zlib.inflateRawSync(frame.body.slice(6))
351
                    } catch (e) {
352
                        return
353
                    }
354
                }
355
            }
356
            if(frame.body.length !== inflatedSize) {
357
                return
358
            }
359
        }
360
361
        let decoded
362
        if(ID3Frames[specName]) {
363
            decoded = ID3Frames[specName].read(frame.body, ID3Version, this)
364
        } else if(specName.startsWith('T')) {
365
            decoded = ID3Frames.GENERIC_TEXT.read(frame.body, ID3Version)
366
        } else if(specName.startsWith('W')) {
367
            decoded = ID3Frames.GENERIC_URL.read(frame.body, ID3Version)
368
        }
369
370
        if(decoded) {
371
            if(ID3Util.getSpecOptions(specName, ID3Version).multiple) {
372
                if(!options.onlyRaw) {
373
                    if(!tags[identifier]) tags[identifier] = []
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
374
                    tags[identifier].push(decoded)
375
                }
376
                if(!options.noRaw) {
377
                    if(!raw[specName]) raw[specName] = []
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
378
                    raw[specName].push(decoded)
379
                }
380
            } else {
381
                if(!options.onlyRaw) {
382
                    tags[identifier] = decoded
383
                }
384
                if(!options.noRaw) {
385
                    raw[specName] = decoded
386
                }
387
            }
388
        }
389
    })
390
391
    if(options.onlyRaw) return raw
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
392
    if(options.noRaw) return tags
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
393
394
    tags.raw = raw
395
    return tags
396
}
397
398
/**
399
 * Checks and removes already written ID3-Frames from a buffer
400
 * @param data - Buffer
401
 * @returns {boolean|Buffer}
402
 */
403
module.exports.removeTagsFromBuffer = function(data) {
404
    let framePosition = ID3Util.getFramePosition(data)
405
406
    if(framePosition === -1) {
407
        return data
408
    }
409
410
    let hSize = Buffer.from([data[framePosition + 6], data[framePosition + 7], data[framePosition + 8], data[framePosition + 9]])
411
412
    if ((hSize[0] | hSize[1] | hSize[2] | hSize[3]) & 0x80) {
413
        //  Invalid tag size (msb not 0)
414
        return false
415
    }
416
417
    if(data.length >= framePosition + 10) {
418
        const size = ID3Util.decodeSize(data.slice(framePosition + 6, framePosition + 10))
419
        return Buffer.concat([data.slice(0, framePosition), data.slice(framePosition + size + 10)])
420
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
421
        return data
422
    }
423
}
424
425
/**
426
 * Checks and removes already written ID3-Frames from a file
427
 * @param filepath - Filepath to file
428
 * @param fn - (optional) Function for async usage
429
 * @returns {boolean|Error}
430
 */
431
module.exports.removeTags = function(filepath, fn) {
432
    if(!fn || typeof fn !== 'function') {
433
        let data
434
        try {
435
            data = fs.readFileSync(filepath)
436
        } catch(e) {
437
            return e
438
        }
439
440
        let newData = this.removeTagsFromBuffer(data)
441
        if(!newData) {
442
            return false
443
        }
444
445
        try {
446
            fs.writeFileSync(filepath, newData, 'binary')
447
        } catch(e) {
448
            return e
449
        }
450
451
        return true
452
    } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
453
        fs.readFile(filepath, function(err, data) {
454
            if(err) {
455
                fn(err)
456
            }
457
458
            let newData = this.removeTagsFromBuffer(data)
459
            if(!newData) {
460
                fn(err)
461
                return
462
            }
463
464
            fs.writeFile(filepath, newData, 'binary', function(err) {
465
                if(err) {
466
                    fn(err)
467
                } else {
468
                    fn(false)
469
                }
470
            })
471
        }.bind(this))
472
    }
473
}
474
475
module.exports.Promise = {
476
    write: (tags, file) => {
477
        return new Promise((resolve, reject) => {
478
            this.write(tags, file, (err, ret) => {
479
                if(err) reject(err)
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
480
                else resolve(ret)
481
            })
482
        })
483
    },
484
    update: (tags, file) => {
485
        return new Promise((resolve, reject) => {
486
            this.update(tags, file, (err, ret) => {
487
                if(err) reject(err)
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
488
                else resolve(ret)
489
            })
490
        })
491
    },
492
    create: (tags) => {
493
        return new Promise((resolve) => {
494
            this.create(tags, (buffer) => {
495
                resolve(buffer)
496
            })
497
        })
498
    },
499
    read: (file, options) => {
500
        return new Promise((resolve, reject) => {
501
            this.read(file, options, (err, ret) => {
502
                if(err) reject(err)
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
503
                else resolve(ret)
504
            })
505
        })
506
    },
507
    removeTags: (filepath) => {
508
        return new Promise((resolve, reject) => {
509
            this.removeTags(filepath, (err) => {
510
                if(err) reject(err)
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
511
                else resolve()
512
            })
513
        })
514
    }
515
}
516