Zazama /
node-id3
| 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
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
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 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
|
|||
| 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
|
|||
| 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
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 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
|
|||
| 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
|
|||
| 240 | this.read(filebuffer, (err, currentTags) => {
|
||
|
0 ignored issues
–
show
|
|||
| 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
|
|||
| 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
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 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
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 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
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 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
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 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
|
|||
| 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
|
|||
| 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
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 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
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 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
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 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
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 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 |