Zazama /
node-id3
| 1 | const zlib = require('zlib') |
||
| 2 | const ID3FrameHeader = require('./ID3FrameHeader') |
||
| 3 | const ID3Frames = require('./ID3Frames') |
||
| 4 | const ID3Util = require('./ID3Util') |
||
| 5 | |||
| 6 | class ID3Frame { |
||
| 7 | constructor(identifier, value, flags = {}) { |
||
| 8 | this.identifier = identifier |
||
| 9 | this.value = value |
||
| 10 | this.dataLengthIndicator = 0 |
||
| 11 | this.flags = flags |
||
| 12 | } |
||
| 13 | |||
| 14 | static createFromBuffer(frameBuffer, version) { |
||
| 15 | const frameHeaderSize = ID3FrameHeader.getHeaderSize(version) |
||
| 16 | // Specification requirement |
||
| 17 | if(frameBuffer < frameHeaderSize + 1) { |
||
| 18 | return null |
||
| 19 | } |
||
| 20 | const frameHeaderBuffer = frameBuffer.subarray(0, frameHeaderSize) |
||
| 21 | const frameHeader = ID3FrameHeader.createFromBuffer(frameHeaderBuffer, version) |
||
| 22 | if(frameHeader.flags.encryption) { |
||
| 23 | return null |
||
| 24 | } |
||
| 25 | |||
| 26 | const frameBodyOffset = frameHeader.flags.dataLengthIndicator ? 4 : 0 |
||
| 27 | const frameBodyStart = frameHeaderSize + frameBodyOffset |
||
| 28 | let frameBody = frameBuffer.subarray(frameBodyStart, frameBodyStart + frameHeader.bodySize - frameBodyOffset) |
||
| 29 | if(frameHeader.flags.unsynchronisation) { |
||
| 30 | // This method should stay in ID3Util for now because it's also used in the Tag's header which we don't have a class for. |
||
| 31 | frameBody = ID3Util.processUnsynchronisedBuffer(frameBody) |
||
| 32 | } |
||
| 33 | if(frameHeader.flags.dataLengthIndicator) { |
||
| 34 | this.dataLengthIndicator = frameBuffer.readInt32BE(frameHeaderSize) |
||
| 35 | } |
||
| 36 | if(frameHeader.flags.compression) { |
||
| 37 | const uncompressedFrameBody = decompressBodyBuffer(frameBody, this.dataLengthIndicator) |
||
| 38 | if(!uncompressedFrameBody) { |
||
| 39 | return null |
||
| 40 | } |
||
| 41 | frameBody = uncompressedFrameBody |
||
| 42 | } |
||
| 43 | |||
| 44 | let value = null |
||
|
0 ignored issues
–
show
Unused Code
introduced
by
Loading history...
|
|||
| 45 | if(ID3Frames[frameHeader.identifier]) { |
||
| 46 | value = ID3Frames[frameHeader.identifier].read(frameBody, version) |
||
| 47 | } else if(frameHeader.identifier.startsWith('T')) { |
||
| 48 | value = ID3Frames.GENERIC_TEXT.read(frameBody, version) |
||
| 49 | } else if(frameHeader.identifier.startsWith('W')) { |
||
| 50 | value = ID3Frames.GENERIC_URL.read(frameBody, version) |
||
| 51 | } else { |
||
| 52 | // Unknown frames are not supported currently. |
||
| 53 | return null |
||
| 54 | } |
||
| 55 | |||
| 56 | return new ID3Frame(frameHeader.identifier, value, frameHeader.flags) |
||
| 57 | } |
||
| 58 | |||
| 59 | getBuffer() { |
||
| 60 | if(ID3Frames[this.identifier]) { |
||
| 61 | return ID3Frames[this.identifier].create(this.value) |
||
| 62 | } |
||
| 63 | if(this.identifier.startsWith('T')) { |
||
| 64 | return ID3Frames.GENERIC_TEXT.create(this.value) |
||
| 65 | } |
||
| 66 | if(this.identifier.startsWith('W')) { |
||
| 67 | return ID3Frames.GENERIC_URL.create(this.value) |
||
| 68 | } |
||
| 69 | |||
| 70 | return null |
||
| 71 | } |
||
| 72 | |||
| 73 | getValue() { |
||
| 74 | return this.value |
||
| 75 | } |
||
| 76 | } |
||
| 77 | |||
| 78 | function decompressBodyBuffer(bodyBuffer, dataLengthIndicator) { |
||
| 79 | if(bodyBuffer.length < 5 || dataLengthIndicator === undefined) { |
||
| 80 | return null |
||
| 81 | } |
||
| 82 | |||
| 83 | /* |
||
| 84 | * ID3 spec defines that compression is stored in ZLIB format, but doesn't specify if header is present or not. |
||
| 85 | * ZLIB has a 2-byte header. |
||
| 86 | * 1. try if header + body decompression |
||
| 87 | * 2. else try if header is not stored (assume that all content is deflated "body") |
||
| 88 | * 3. else try if inflation works if the header is omitted (implementation dependent) |
||
| 89 | * */ |
||
| 90 | let decompressedBody |
||
| 91 | try { |
||
| 92 | decompressedBody = zlib.inflateSync(bodyBuffer) |
||
| 93 | } catch (e) { |
||
| 94 | try { |
||
| 95 | decompressedBody = zlib.inflateRawSync(bodyBuffer) |
||
| 96 | } catch (e) { |
||
| 97 | try { |
||
| 98 | decompressedBody = zlib.inflateRawSync(bodyBuffer.subarray(2)) |
||
| 99 | } catch (e) { |
||
| 100 | return null |
||
| 101 | } |
||
| 102 | } |
||
| 103 | } |
||
| 104 | if(decompressedBody.length !== dataLengthIndicator) { |
||
| 105 | return null |
||
| 106 | } |
||
| 107 | return decompressedBody |
||
| 108 | } |
||
| 109 | |||
| 110 | module.exports = ID3Frame |
||
| 111 |