Passed
Push — master ( c9b3ba...d59e3c )
by Jan
01:32
created

index.js (30 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
6
/*
7
**  Used specification: http://id3.org/id3v2.3.0
8
*/
9
10
/**
11
 * Write passed tags to a file/buffer
12
 * @param tags - Object containing tags to be written
13
 * @param filebuffer - Can contain a filepath string or buffer
14
 * @param fn - (optional) Function for async version
15
 * @returns {boolean|Buffer|Error}
16
 */
17
module.exports.write = function(tags, filebuffer, fn) {
18
    let completeTag = this.create(tags)
19
    if(filebuffer instanceof Buffer) {
20
        filebuffer = this.removeTagsFromBuffer(filebuffer) || filebuffer
21
        let completeBuffer = Buffer.concat([completeTag, filebuffer])
22
        if(fn && typeof fn === 'function') {
23
            fn(null, completeBuffer)
24
            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...
25
        } 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...
26
            return completeBuffer
27
        }
28
    }
29
30
    if(fn && typeof fn === 'function') {
31
        try {
32
            fs.readFile(filebuffer, function(err, data) {
33
                if(err) {
34
                    fn(err)
35
                    return
36
                }
37
                data = this.removeTagsFromBuffer(data) || data
38
                let rewriteFile = Buffer.concat([completeTag, data])
39
                fs.writeFile(filebuffer, rewriteFile, 'binary', (err) => {
40
                    fn(err)
41
                })
42
            }.bind(this))
43
        } catch(err) {
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
44
            fn(err)
45
        }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
46
    } else {
47
        try {
48
            let data = fs.readFileSync(filebuffer)
49
            data = this.removeTagsFromBuffer(data) || data
50
            let rewriteFile = Buffer.concat([completeTag, data])
51
            fs.writeFileSync(filebuffer, rewriteFile, 'binary')
52
            return true
53
        } catch(err) {
54
            return err
55
        }
56
    }
57
}
58
59
/**
60
 * Creates a buffer containing the ID3 Tag
61
 * @param tags - Object containing tags to be written
62
 * @param fn fn - (optional) Function for async version
63
 * @returns {Buffer}
64
 */
65
module.exports.create = function(tags, fn) {
66
    let frames = []
67
68
    //  Create & push a header for the ID3-Frame
69
    const header = Buffer.alloc(10)
70
    header.fill(0)
71
    header.write("ID3", 0)              //File identifier
72
    header.writeUInt16BE(0x0300, 3)     //Version 2.3.0  --  03 00
73
    header.writeUInt16BE(0x0000, 5)     //Flags 00
74
75
    //Last 4 bytes are used for header size, but have to be inserted later, because at this point, its size is not clear.
76
    frames.push(header)
77
78
    frames = frames.concat(this.createBuffersFromTags(tags))
79
80
    //  Calculate frame size of ID3 body to insert into header
81
82
    let totalSize = 0
83
    frames.forEach((frame) => {
84
        totalSize += frame.length
85
    })
86
87
    //  Don't count ID3 header itself
88
    totalSize -= 10
89
    //  ID3 header size uses only 7 bits of a byte, bit shift is needed
90
    let size = ID3Util.encodeSize(totalSize)
91
92
    //  Write bytes to ID3 frame header, which is the first frame
93
    frames[0].writeUInt8(size[0], 6)
94
    frames[0].writeUInt8(size[1], 7)
95
    frames[0].writeUInt8(size[2], 8)
96
    frames[0].writeUInt8(size[3], 9)
97
98
    if(fn && typeof fn === 'function') {
99
        fn(Buffer.concat(frames))
100
    } else {
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
101
        return Buffer.concat(frames)
102
    }
103
}
104
105
/**
106
 * Returns array of buffers created by tags specified in the tags argument
107
 * @param tags - Object containing tags to be written
108
 * @returns {Array}
109
 */
110
module.exports.createBuffersFromTags = function(tags) {
111
    let frames = []
112
    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...
113
    const rawObject = Object.keys(tags).reduce((acc, val) => {
114
        if(ID3Definitions.FRAME_IDENTIFIERS.v3[val] !== undefined) {
115
            acc[ID3Definitions.FRAME_IDENTIFIERS.v3[val]] = tags[val]
116
        } else {
117
            acc[val] = tags[val]
118
        }
119
        return acc
120
    }, {})
121
122
    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...
123
        let frame
124
        // Check if invalid specName
125
        if(specName.length !== 4) {
126
            return
127
        }
128
        if(ID3Frames[specName] !== undefined) {
129
            frame = ID3Frames[specName].create(rawObject[specName], 3, this)
130
        } else if(specName.startsWith('T')) {
131
            frame = ID3Frames.GENERIC_TEXT.create(specName, rawObject[specName], 3)
132
        } else if(specName.startsWith('W')) {
133
            if(ID3Util.getSpecOptions(specName, 3).multiple && rawObject[specName] instanceof Array && rawObject[specName].length > 0) {
134
                frame = Buffer.alloc(0)
135
                // deduplicate array
136
                for(let url of [...new Set(rawObject[specName])]) {
137
                    frame = Buffer.concat([frame, ID3Frames.GENERIC_URL.create(specName, url, 3)])
138
                }
139
            } else {
140
                frame = ID3Frames.GENERIC_URL.create(specName, rawObject[specName], 3)
141
            }
142
        }
143
144
        if (frame instanceof Buffer) {
0 ignored issues
show
The variable frame does not seem to be initialized in case specName.startsWith("W") on line 132 is false. Are you sure this can never be the case?
Loading history...
145
            frames.push(frame)
146
        }
147
    })
148
149
    return frames
150
}
151
152
/**
153
 * Read ID3-Tags from passed buffer/filepath
154
 * @param filebuffer - Can contain a filepath string or buffer
155
 * @param options - (optional) Object containing options
156
 * @param fn - (optional) Function for async version
157
 * @returns {boolean}
158
 */
159
module.exports.read = function(filebuffer, options, fn) {
160
    if(!options || typeof options === 'function') {
161
        fn = fn || options
162
        options = {}
163
    }
164
    if(!fn || typeof fn !== 'function') {
165
        if(typeof filebuffer === "string" || filebuffer instanceof String) {
166
            filebuffer = fs.readFileSync(filebuffer)
167
        }
168
        return this.getTagsFromBuffer(filebuffer, options)
169
    } 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...
170
        if(typeof filebuffer === "string" || filebuffer instanceof String) {
171
            fs.readFile(filebuffer, function(err, data) {
172
                if(err) {
173
                    fn(err, null)
174
                } else {
175
                    fn(null, this.getTagsFromBuffer(data, options))
176
                }
177
            }.bind(this))
178
        } else {
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
179
            fn(null, this.getTagsFromBuffer(filebuffer, options))
180
        }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
181
    }
182
}
183
184
/**
185
 * Update ID3-Tags from passed buffer/filepath
186
 * @param tags - Object containing tags to be written
187
 * @param filebuffer - Can contain a filepath string or buffer
188
 * @param options - (optional) Object containing options
189
 * @param fn - (optional) Function for async version
190
 * @returns {boolean|Buffer|Error}
191
 */
192
module.exports.update = function(tags, filebuffer, options, fn) {
193
    if(!options || typeof options === 'function') {
194
        fn = fn || options
195
        options = {}
196
    }
197
198
    const rawTags = Object.keys(tags).reduce((acc, val) => {
199
        if(ID3Definitions.FRAME_IDENTIFIERS.v3[val] !== undefined) {
200
            acc[ID3Definitions.FRAME_IDENTIFIERS.v3[val]] = tags[val]
201
        } else {
202
            acc[val] = tags[val]
203
        }
204
        return acc
205
    }, {})
206
207
    const updateFn = (currentTags) => {
208
        currentTags = currentTags.raw || {}
209
        Object.keys(rawTags).map((specName) => {
210
            const options = ID3Util.getSpecOptions(specName, 3)
211
            const cCompare = {}
212
            if(options.multiple && currentTags[specName] && rawTags[specName]) {
213
                if(options.updateCompareKey) {
214
                    currentTags[specName].forEach((cTag, index) => {
215
                        cCompare[cTag[options.updateCompareKey]] = index
216
                    })
217
218
                }
219
                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...
220
                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...
221
                    const comparison = cCompare[rTag[options.updateCompareKey]]
222
                    if (comparison !== undefined) {
223
                        currentTags[specName][comparison] = rTag
224
                    } else {
225
                        currentTags[specName].push(rTag)
226
                    }
227
                })
228
            } else {
229
                currentTags[specName] = rawTags[specName]
230
            }
231
        })
232
233
        return currentTags
234
    }
235
236
    if(!fn || typeof fn !== 'function') {
237
        return this.write(updateFn(this.read(filebuffer, options)), filebuffer)
238
    } 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...
239
        this.read(filebuffer, (err, currentTags) => {
0 ignored issues
show
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...
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...
240
            this.write(updateFn(this.read(filebuffer, options)), filebuffer, fn)
241
        })
242
    }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
243
}
244
245
module.exports.getTagsFromBuffer = function(filebuffer, options) {
246
    let framePosition = ID3Util.getFramePosition(filebuffer)
247
    if(framePosition === -1) {
248
        return this.getTagsFromFrames([], 3, options)
249
    }
250
    const frameSize = ID3Util.decodeSize(filebuffer.slice(framePosition + 6, framePosition + 10)) + 10
251
    let ID3Frame = Buffer.alloc(frameSize + 1)
252
    filebuffer.copy(ID3Frame, 0, framePosition)
253
    //ID3 version e.g. 3 if ID3v2.3.0
254
    let ID3Version = ID3Frame[3]
255
    const tagFlags = ID3Util.parseTagHeaderFlags(ID3Frame)
256
    let extendedHeaderOffset = 0
257
    if(tagFlags.extendedHeader) {
258
        if(ID3Version === 3) {
259
            extendedHeaderOffset = 4 + filebuffer.readUInt32BE(10)
260
        } else if(ID3Version === 4) {
261
            extendedHeaderOffset = ID3Util.decodeSize(filebuffer.slice(10, 14))
262
        }
263
    }
264
    let ID3FrameBody = Buffer.alloc(frameSize - 10 - extendedHeaderOffset)
265
    filebuffer.copy(ID3FrameBody, 0, framePosition + 10 + extendedHeaderOffset)
266
267
    let frames = this.getFramesFromID3Body(ID3FrameBody, ID3Version, options)
268
269
    return this.getTagsFromFrames(frames, ID3Version, options)
270
}
271
272
module.exports.getFramesFromID3Body = function(ID3FrameBody, ID3Version, options = {}) {
273
    let currentPosition = 0
274
    let frames = []
275
    if(!ID3FrameBody || !(ID3FrameBody instanceof Buffer)) {
276
        return frames
277
    }
278
279
    let identifierSize = 4
280
    let textframeHeaderSize = 10
281
    if(ID3Version === 2) {
282
        identifierSize = 3
283
        textframeHeaderSize = 6
284
    }
285
286
    while(currentPosition < ID3FrameBody.length && ID3FrameBody[currentPosition] !== 0x00) {
287
        let bodyFrameHeader = Buffer.alloc(textframeHeaderSize)
288
        ID3FrameBody.copy(bodyFrameHeader, 0, currentPosition)
289
290
        let decodeSize = false
291
        if(ID3Version === 4) {
292
            decodeSize = true
293
        }
294
        let bodyFrameSize = ID3Util.getFrameSize(bodyFrameHeader, decodeSize, ID3Version)
295
        if(bodyFrameSize + 10 > (ID3FrameBody.length - currentPosition)) {
296
            break
297
        }
298
        const specName = bodyFrameHeader.toString('utf8', 0, identifierSize)
299
        if(options.exclude instanceof Array && options.exclude.includes(specName) || options.include instanceof Array && !options.include.includes(specName)) {
300
            currentPosition += bodyFrameSize + textframeHeaderSize
301
            continue
302
        }
303
        const frameHeaderFlags = ID3Util.parseFrameHeaderFlags(bodyFrameHeader, ID3Version)
304
        let bodyFrameBuffer = Buffer.alloc(bodyFrameSize)
305
        ID3FrameBody.copy(bodyFrameBuffer, 0, currentPosition + textframeHeaderSize + (frameHeaderFlags.dataLengthIndicator ? 4 : 0))
306
        //  Size of sub frame + its header
307
        currentPosition += bodyFrameSize + textframeHeaderSize
308
        frames.push({
309
            name: specName,
310
            flags: frameHeaderFlags,
311
            body: frameHeaderFlags.unsynchronisation ? ID3Util.processUnsynchronisedBuffer(bodyFrameBuffer) : bodyFrameBuffer
312
        })
313
    }
314
315
    return frames
316
}
317
318
module.exports.getTagsFromFrames = function(frames, ID3Version, options = {}) {
319
    let tags = { }
320
    let raw = { }
321
322
    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...
323
        const specName = ID3Version === 2 ? ID3Definitions.FRAME_IDENTIFIERS.v3[ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v2[frame.name]] : frame.name
324
        const identifier = ID3Version === 2 ? ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v2[frame.name] : ID3Definitions.FRAME_INTERNAL_IDENTIFIERS.v3[frame.name]
325
326
        if(!specName || !identifier) {
327
            return
328
        }
329
330
        let decoded
331
        if(ID3Frames[specName]) {
332
            decoded = ID3Frames[specName].read(frame.body, ID3Version, this)
333
        } else if(specName.startsWith('T')) {
334
            decoded = ID3Frames.GENERIC_TEXT.read(frame.body, ID3Version)
335
        } else if(specName.startsWith('W')) {
336
            decoded = ID3Frames.GENERIC_URL.read(frame.body, ID3Version)
337
        }
338
339
        if(decoded) {
340
            if(ID3Util.getSpecOptions(specName, ID3Version).multiple) {
341
                if(!options.onlyRaw) {
342
                    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...
343
                    tags[identifier].push(decoded)
344
                }
345
                if(!options.noRaw) {
346
                    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...
347
                    raw[specName].push(decoded)
348
                }
349
            } else {
350
                if(!options.onlyRaw) {
351
                    tags[identifier] = decoded
352
                }
353
                if(!options.noRaw) {
354
                    raw[specName] = decoded
355
                }
356
            }
357
        }
358
    })
359
360
    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...
361
    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...
362
363
    tags.raw = raw
364
    return tags
365
}
366
367
/**
368
 * Checks and removes already written ID3-Frames from a buffer
369
 * @param data - Buffer
370
 * @returns {boolean|Buffer}
371
 */
372
module.exports.removeTagsFromBuffer = function(data) {
373
    let framePosition = ID3Util.getFramePosition(data)
374
375
    if(framePosition === -1) {
376
        return data
377
    }
378
379
    let hSize = Buffer.from([data[framePosition + 6], data[framePosition + 7], data[framePosition + 8], data[framePosition + 9]])
380
381
    if ((hSize[0] | hSize[1] | hSize[2] | hSize[3]) & 0x80) {
0 ignored issues
show
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
382
        //  Invalid tag size (msb not 0)
383
        return false
384
    }
385
386
    if(data.length >= framePosition + 10) {
387
        const size = ID3Util.decodeSize(data.slice(framePosition + 6, framePosition + 10))
388
        return Buffer.concat([data.slice(0, framePosition), data.slice(framePosition + size + 10)])
389
    } 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...
390
        return data
391
    }
392
}
393
394
/**
395
 * Checks and removes already written ID3-Frames from a file
396
 * @param filepath - Filepath to file
397
 * @param fn - (optional) Function for async usage
398
 * @returns {boolean|Error}
399
 */
400
module.exports.removeTags = function(filepath, fn) {
401
    if(!fn || typeof fn !== 'function') {
402
        let data
403
        try {
404
            data = fs.readFileSync(filepath)
405
        } catch(e) {
406
            return e
407
        }
408
409
        let newData = this.removeTagsFromBuffer(data)
410
        if(!newData) {
411
            return false
412
        }
413
414
        try {
415
            fs.writeFileSync(filepath, newData, 'binary')
416
        } catch(e) {
417
            return e
418
        }
419
420
        return true
421
    } 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...
422
        fs.readFile(filepath, function(err, data) {
423
            if(err) {
424
                fn(err)
425
            }
426
427
            let newData = this.removeTagsFromBuffer(data)
428
            if(!newData) {
429
                fn(err)
430
                return
431
            }
432
433
            fs.writeFile(filepath, newData, 'binary', function(err) {
434
                if(err) {
435
                    fn(err)
436
                } else {
437
                    fn(false)
438
                }
439
            })
440
        }.bind(this))
441
    }
0 ignored issues
show
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
442
}
443
444
module.exports.Promise = {
445
    write: (tags, file) => {
446
        return new Promise((resolve, reject) => {
447
            this.write(tags, file, (err, ret) => {
448
                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...
449
                else resolve(ret)
450
            })
451
        })
452
    },
453
    update: (tags, file) => {
454
        return new Promise((resolve, reject) => {
455
            this.update(tags, file, (err, ret) => {
456
                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...
457
                else resolve(ret)
458
            })
459
        })
460
    },
461
    create: (tags) => {
462
        return new Promise((resolve) => {
463
            this.create(tags, (buffer) => {
464
                resolve(buffer)
465
            })
466
        })
467
    },
468
    read: (file, options) => {
469
        return new Promise((resolve, reject) => {
470
            this.read(file, options, (err, ret) => {
471
                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...
472
                else resolve(ret)
473
            })
474
        })
475
    },
476
    removeTags: (filepath) => {
477
        return new Promise((resolve, reject) => {
478
            this.removeTags(filepath, (err) => {
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()
481
            })
482
        })
483
    }
484
}
485