From a4c5670409f5d58430e9f2027cbc62b38dd7bf9f Mon Sep 17 00:00:00 2001 From: lyswhut Date: Sun, 22 Mar 2020 18:33:51 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EFLAC=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E9=9F=B3=E4=B9=90=E6=A0=87=E7=AD=BE=E4=BF=A1=E6=81=AF=E5=86=99?= =?UTF-8?q?=E5=85=A5=E4=B8=8E=E5=B0=81=E9=9D=A2=E5=B5=8C=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 29 +- package.json | 1 + publish/changeLog.md | 2 +- src/main/utils/flac-metadata/index.js | 282 +++++++++++++++++- .../lib/{data => }/MetaDataBlock.js | 0 .../lib/{data => }/MetaDataBlockPicture.js | 0 .../lib/{data => }/MetaDataBlockStreamInfo.js | 0 .../{data => }/MetaDataBlockVorbisComment.js | 0 src/main/utils/flac-metadata/lib/Processor.js | 214 ------------- src/main/utils/flacMeta.js | 102 +++++-- src/main/utils/mp3Meta.js | 1 + src/renderer/lang/cns/view/setting.json | 2 +- src/renderer/lang/cnt/view/setting.json | 2 +- src/renderer/lang/en/view/setting.json | 2 +- 14 files changed, 383 insertions(+), 254 deletions(-) rename src/main/utils/flac-metadata/lib/{data => }/MetaDataBlock.js (100%) rename src/main/utils/flac-metadata/lib/{data => }/MetaDataBlockPicture.js (100%) rename src/main/utils/flac-metadata/lib/{data => }/MetaDataBlockStreamInfo.js (100%) rename src/main/utils/flac-metadata/lib/{data => }/MetaDataBlockVorbisComment.js (100%) delete mode 100644 src/main/utils/flac-metadata/lib/Processor.js diff --git a/package-lock.json b/package-lock.json index 2362cd1a..8b49cd51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9846,11 +9846,12 @@ "dev": true }, "image-size": { - "version": "0.5.5", - "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz", - "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", - "dev": true, - "optional": true + "version": "0.8.3", + "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.8.3.tgz", + "integrity": "sha1-8LVohX4DTym6/9NwE1h/LAyti0Y=", + "requires": { + "queue": "6.0.1" + } }, "import-cwd": { "version": "2.1.0", @@ -9940,8 +9941,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz?cache=0&sync_timestamp=1560975547815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finherits%2Fdownload%2Finherits-2.0.4.tgz", - "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", - "dev": true + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=" }, "ini": { "version": "1.3.5", @@ -10753,6 +10753,13 @@ "tslib": "^1.10.0" }, "dependencies": { + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npm.taobao.org/image-size/download/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "dev": true, + "optional": true + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npm.taobao.org/mime/download/mime-1.6.0.tgz", @@ -13415,6 +13422,14 @@ "integrity": "sha1-YOWl/WSn+L+k0qsu1v30yFutFU4=", "dev": true }, + "queue": { + "version": "6.0.1", + "resolved": "https://registry.npm.taobao.org/queue/download/queue-6.0.1.tgz", + "integrity": "sha1-q9WlsDdpEvBwolcp4Lan1WVoN5E=", + "requires": { + "inherits": "~2.0.3" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npm.taobao.org/randombytes/download/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 922e5b38..d1c99c82 100644 --- a/package.json +++ b/package.json @@ -209,6 +209,7 @@ "electron-log": "^4.1.0", "electron-store": "^5.1.1", "electron-updater": "^4.2.5", + "image-size": "^0.8.3", "js-htmlencode": "^0.3.0", "lrc-file-parser": "^1.0.1", "needle": "^2.3.3", diff --git a/publish/changeLog.md b/publish/changeLog.md index 6678503a..2d5e96b4 100644 --- a/publish/changeLog.md +++ b/publish/changeLog.md @@ -1,6 +1,6 @@ ### 新增 -- 新增FLAC格式音乐标签信息写入 +- 新增FLAC格式音乐标签信息写入与封面嵌入 ### 优化 diff --git a/src/main/utils/flac-metadata/index.js b/src/main/utils/flac-metadata/index.js index 34641291..c5b71dc5 100644 --- a/src/main/utils/flac-metadata/index.js +++ b/src/main/utils/flac-metadata/index.js @@ -1,10 +1,280 @@ // https://github.com/claus/flac-metadata -module.exports.Processor = require('./lib/Processor') +const Transform = require('stream').Transform -module.exports.data = { - MetaDataBlock: require('./lib/data/MetaDataBlock'), - MetaDataBlockStreamInfo: require('./lib/data/MetaDataBlockStreamInfo'), - MetaDataBlockVorbisComment: require('./lib/data/MetaDataBlockVorbisComment'), - MetaDataBlockPicture: require('./lib/data/MetaDataBlockPicture'), +const MetaDataBlock = require('./lib/MetaDataBlock') +const MetaDataBlockStreamInfo = require('./lib/MetaDataBlockStreamInfo') +const MetaDataBlockVorbisComment = require('./lib/MetaDataBlockVorbisComment') +const MetaDataBlockPicture = require('./lib/MetaDataBlockPicture') + +const STATE_IDLE = 0 +const STATE_MARKER = 1 +const STATE_MDB_HEADER = 2 +const STATE_MDB = 3 +const STATE_PASS_THROUGH = 4 + +class Processor extends Transform { + constructor(options) { + super(options) + // MDB types + this.MDB_TYPE_STREAMINFO = 0 + this.MDB_TYPE_PADDING = 1 + this.MDB_TYPE_APPLICATION = 2 + this.MDB_TYPE_SEEKTABLE = 3 + this.MDB_TYPE_VORBIS_COMMENT = 4 + this.MDB_TYPE_CUESHEET = 5 + this.MDB_TYPE_PICTURE = 6 + this.MDB_TYPE_INVALID = 127 + + this.state = STATE_IDLE + + this.isFlac = false + + this.buf = null + this.bufPos = 0 + + this.mdb = null + this.mdbLen = 0 + this.mdbLast = false + this.mdbPush = false + this.mdbLastWritten = false + + this.parseMetaDataBlocks = false + + this.waitWriteVorbis = null + this.waitWritePicture = null + this.tasks = 0 + + if (!(this instanceof Processor)) return new Processor(options) + if (options && !!options.parseMetaDataBlocks) { this.parseMetaDataBlocks = true } + } + + writeMeta({ vorbis, picture }) { + if (vorbis != null) { + this.waitWriteVorbis = vorbis + this.tasks++ + } + if (picture != null) { + this.waitWritePicture = picture + this.tasks++ + } + } + + // clearMeta() { + // this.mdbLastWritten = true + // } + + readMeta(callback) { + this.parseMetaDataBlocks = true + this.readCallBack = callback + } + + _transform(chunk, enc, done) { + let chunkPos = 0 + let chunkLen = chunk.length + let isChunkProcessed = false + let _this = this + + function _safePush(minCapacity, persist, validate) { + let slice + let chunkAvailable = chunkLen - chunkPos + let isDone = (chunkAvailable + this.bufPos >= minCapacity) + validate = (typeof validate === 'function') ? validate : function() { return true } + if (isDone) { + // Enough data available + if (persist) { + // Persist the entire block so it can be parsed + if (this.bufPos > 0) { + // Part of this block's data is in backup buffer, copy rest over + chunk.copy(this.buf, this.bufPos, chunkPos, chunkPos + minCapacity - this.bufPos) + slice = this.buf.slice(0, minCapacity) + } else { + // Entire block fits in current chunk + slice = chunk.slice(chunkPos, chunkPos + minCapacity) + } + } else { + slice = chunk.slice(chunkPos, chunkPos + minCapacity - this.bufPos) + } + // Push block after validation + validate(slice, isDone) && _this.push(slice) + chunkPos += minCapacity - this.bufPos + this.bufPos = 0 + this.buf = null + } else { + // Not enough data available + if (persist) { + // Copy/append incomplete block to backup buffer + this.buf = this.buf || Buffer.alloc(minCapacity) + chunk.copy(this.buf, this.bufPos, chunkPos, chunkLen) + } else { + // Push incomplete block after validation + slice = chunk.slice(chunkPos, chunkLen) + validate(slice, isDone) && _this.push(slice) + } + this.bufPos += chunkLen - chunkPos + } + return isDone + }; + let safePush = _safePush.bind(this) + + while (!isChunkProcessed) { + switch (this.state) { + case STATE_IDLE: + this.state = STATE_MARKER + break + case STATE_MARKER: + if (safePush(4, true, this._validateMarker.bind(this))) { + this.state = this.isFlac ? STATE_MDB_HEADER : STATE_PASS_THROUGH + } else { + isChunkProcessed = true + } + break + case STATE_MDB_HEADER: + if (safePush(4, true, this._validateMDBHeader.bind(this))) { + this.state = STATE_MDB + } else { + isChunkProcessed = true + } + break + case STATE_MDB: + if (safePush(this.mdbLen, this.parseMetaDataBlocks, this._validateMDB.bind(this))) { + if (this.mdb.isLast) { + // This MDB has the isLast flag set to true. + // Ignore all following MDBs. + this.mdbLastWritten = true + } + this.readCallBack && this.readCallBack(this.mdb) + if (this.mdbLast) { + this._writeVorbisComment() + this._writePicture() + this.state = STATE_PASS_THROUGH + } else { + this.state = STATE_MDB_HEADER + } + } else { + isChunkProcessed = true + } + break + case STATE_PASS_THROUGH: + safePush(chunkLen - chunkPos, false) + isChunkProcessed = true + break + } + } + + done() + } + + _validateMarker(slice, isDone) { + this.isFlac = (slice.toString('utf8', 0) === 'fLaC') + // TODO: completely bail out if file is not a FLAC? + return true + } + + _validateMDBHeader(slice, isDone) { + // Parse MDB header + let header = slice.readUInt32BE(0) + let type = (header >>> 24) & 0x7f + this.mdbLast = (((header >>> 24) & 0x80) !== 0) + this.mdbLen = header & 0xffffff + // Create appropriate MDB object + // (data is injected later in _validateMDB, if parseMetaDataBlocks option is set to true) + switch (type) { + case this.MDB_TYPE_STREAMINFO: + this.mdb = new MetaDataBlockStreamInfo(this.mdbLast) + break + case this.MDB_TYPE_VORBIS_COMMENT: + if (this.waitWriteVorbis) { + this._writeVorbisComment(slice, header) + this.mdbPush = false + return this.mdbPush + } else { + this.mdb = new MetaDataBlockVorbisComment(this.mdbLast) + this.readCallback && this.readCallback(this.mdb) + } + break + case this.MDB_TYPE_PICTURE: + if (this.waitWritePicture) { + this._writePicture(slice, header) + this.mdbPush = false + return this.mdbPush + } else { + this.mdb = new MetaDataBlockPicture(this.mdbLast) + this.readCallback && this.readCallback(this.mdb) + } + break + case this.MDB_TYPE_PADDING: + case this.MDB_TYPE_APPLICATION: + case this.MDB_TYPE_SEEKTABLE: + case this.MDB_TYPE_CUESHEET: + case this.MDB_TYPE_INVALID: + default: + this.mdb = new MetaDataBlock(this.mdbLast, type) + break + } + + // this.emit('preprocess', this.mdb) + + if (this.mdbLastWritten) { + // A previous MDB had the isLast flag set to true. + // Ignore all following MDBs. + this.mdb.remove() + } else { + if (this.mdbLast && this.tasks > 0) { + header &= 0x7fffffff + slice.writeUInt32BE(header >>> 0, 0) + } + // The consumer may change the MDB's isLast flag in the preprocess handler. + // Here that flag is updated in the MDB header. + } + this.mdbPush = !this.mdb.removed + return this.mdbPush + } + + _validateMDB(slice, isDone) { + // Parse the MDB if parseMetaDataBlocks option is set to true + if (this.parseMetaDataBlocks && isDone) { + this.mdb.parse(slice) + } + return this.mdbPush + } + + _flush(done) { + // All chunks have been processed + // Clean up + this.state = STATE_IDLE + this.mdbLastWritten = false + this.isFlac = false + this.bufPos = 0 + this.buf = null + this.mdb = null + done() + } + + _writeVorbisComment() { + if (this.waitWriteVorbis == null) return + let isLast = this.mdbLast && this.tasks === 1 + this.tasks-- + this.push(MetaDataBlockVorbisComment.create(isLast, this.waitWriteVorbis.vendor, this.waitWriteVorbis.comments).publish()) + this.waitWriteVorbis = null + } + + _writePicture() { + if (this.waitWritePicture == null) return + let isLast = this.mdbLast && this.tasks === 1 + this.tasks-- + this.mdb = + this.push( + MetaDataBlockPicture.create( + isLast, this.waitWritePicture.pictureType, + this.waitWritePicture.mimeType, this.waitWritePicture.description, + this.waitWritePicture.width, this.waitWritePicture.height, + this.waitWritePicture.bitsPerPixel, this.waitWritePicture.colors, + this.waitWritePicture.pictureData, + ).publish(), + ) + this.waitWritePicture = null + } } + +module.exports = Processor diff --git a/src/main/utils/flac-metadata/lib/data/MetaDataBlock.js b/src/main/utils/flac-metadata/lib/MetaDataBlock.js similarity index 100% rename from src/main/utils/flac-metadata/lib/data/MetaDataBlock.js rename to src/main/utils/flac-metadata/lib/MetaDataBlock.js diff --git a/src/main/utils/flac-metadata/lib/data/MetaDataBlockPicture.js b/src/main/utils/flac-metadata/lib/MetaDataBlockPicture.js similarity index 100% rename from src/main/utils/flac-metadata/lib/data/MetaDataBlockPicture.js rename to src/main/utils/flac-metadata/lib/MetaDataBlockPicture.js diff --git a/src/main/utils/flac-metadata/lib/data/MetaDataBlockStreamInfo.js b/src/main/utils/flac-metadata/lib/MetaDataBlockStreamInfo.js similarity index 100% rename from src/main/utils/flac-metadata/lib/data/MetaDataBlockStreamInfo.js rename to src/main/utils/flac-metadata/lib/MetaDataBlockStreamInfo.js diff --git a/src/main/utils/flac-metadata/lib/data/MetaDataBlockVorbisComment.js b/src/main/utils/flac-metadata/lib/MetaDataBlockVorbisComment.js similarity index 100% rename from src/main/utils/flac-metadata/lib/data/MetaDataBlockVorbisComment.js rename to src/main/utils/flac-metadata/lib/MetaDataBlockVorbisComment.js diff --git a/src/main/utils/flac-metadata/lib/Processor.js b/src/main/utils/flac-metadata/lib/Processor.js deleted file mode 100644 index 239c793a..00000000 --- a/src/main/utils/flac-metadata/lib/Processor.js +++ /dev/null @@ -1,214 +0,0 @@ -const Transform = require('stream').Transform - -const MetaDataBlock = require('./data/MetaDataBlock') -const MetaDataBlockStreamInfo = require('./data/MetaDataBlockStreamInfo') -const MetaDataBlockVorbisComment = require('./data/MetaDataBlockVorbisComment') -const MetaDataBlockPicture = require('./data/MetaDataBlockPicture') - -const STATE_IDLE = 0 -const STATE_MARKER = 1 -const STATE_MDB_HEADER = 2 -const STATE_MDB = 3 -const STATE_PASS_THROUGH = 4 - -class Processor extends Transform { - constructor(options) { - super(options) - // MDB types - this.MDB_TYPE_STREAMINFO = 0 - this.MDB_TYPE_PADDING = 1 - this.MDB_TYPE_APPLICATION = 2 - this.MDB_TYPE_SEEKTABLE = 3 - this.MDB_TYPE_VORBIS_COMMENT = 4 - this.MDB_TYPE_CUESHEET = 5 - this.MDB_TYPE_PICTURE = 6 - this.MDB_TYPE_INVALID = 127 - - this.state = STATE_IDLE - - this.isFlac = false - - this.buf = null - this.bufPos = 0 - - this.mdb = null - this.mdbLen = 0 - this.mdbLast = false - this.mdbPush = false - this.mdbLastWritten = false - - this.parseMetaDataBlocks = false - - if (!(this instanceof Processor)) return new Processor(options) - if (options && !!options.parseMetaDataBlocks) { this.parseMetaDataBlocks = true } - } - - _transform(chunk, enc, done) { - let chunkPos = 0 - let chunkLen = chunk.length - let isChunkProcessed = false - let _this = this - - function _safePush(minCapacity, persist, validate) { - let slice - let chunkAvailable = chunkLen - chunkPos - let isDone = (chunkAvailable + this.bufPos >= minCapacity) - validate = (typeof validate === 'function') ? validate : function() { return true } - if (isDone) { - // Enough data available - if (persist) { - // Persist the entire block so it can be parsed - if (this.bufPos > 0) { - // Part of this block's data is in backup buffer, copy rest over - chunk.copy(this.buf, this.bufPos, chunkPos, chunkPos + minCapacity - this.bufPos) - slice = this.buf.slice(0, minCapacity) - } else { - // Entire block fits in current chunk - slice = chunk.slice(chunkPos, chunkPos + minCapacity) - } - } else { - slice = chunk.slice(chunkPos, chunkPos + minCapacity - this.bufPos) - } - // Push block after validation - validate(slice, isDone) && _this.push(slice) - chunkPos += minCapacity - this.bufPos - this.bufPos = 0 - this.buf = null - } else { - // Not enough data available - if (persist) { - // Copy/append incomplete block to backup buffer - this.buf = this.buf || Buffer.alloc(minCapacity) - chunk.copy(this.buf, this.bufPos, chunkPos, chunkLen) - } else { - // Push incomplete block after validation - slice = chunk.slice(chunkPos, chunkLen) - validate(slice, isDone) && _this.push(slice) - } - this.bufPos += chunkLen - chunkPos - } - return isDone - }; - let safePush = _safePush.bind(this) - - while (!isChunkProcessed) { - switch (this.state) { - case STATE_IDLE: - this.state = STATE_MARKER - break - case STATE_MARKER: - if (safePush(4, true, this._validateMarker.bind(this))) { - this.state = this.isFlac ? STATE_MDB_HEADER : STATE_PASS_THROUGH - } else { - isChunkProcessed = true - } - break - case STATE_MDB_HEADER: - if (safePush(4, true, this._validateMDBHeader.bind(this))) { - this.state = STATE_MDB - } else { - isChunkProcessed = true - } - break - case STATE_MDB: - if (safePush(this.mdbLen, this.parseMetaDataBlocks, this._validateMDB.bind(this))) { - if (this.mdb.isLast) { - // This MDB has the isLast flag set to true. - // Ignore all following MDBs. - this.mdbLastWritten = true - } - this.emit('postprocess', this.mdb) - this.state = this.mdbLast ? STATE_PASS_THROUGH : STATE_MDB_HEADER - } else { - isChunkProcessed = true - } - break - case STATE_PASS_THROUGH: - safePush(chunkLen - chunkPos, false) - isChunkProcessed = true - break - } - } - - done() - } - - _validateMarker(slice, isDone) { - this.isFlac = (slice.toString('utf8', 0) === 'fLaC') - // TODO: completely bail out if file is not a FLAC? - return true - } - - _validateMDBHeader(slice, isDone) { - // Parse MDB header - let header = slice.readUInt32BE(0) - let type = (header >>> 24) & 0x7f - this.mdbLast = (((header >>> 24) & 0x80) !== 0) - this.mdbLen = header & 0xffffff - - // Create appropriate MDB object - // (data is injected later in _validateMDB, if parseMetaDataBlocks option is set to true) - switch (type) { - case Processor.MDB_TYPE_STREAMINFO: - this.mdb = new MetaDataBlockStreamInfo(this.mdbLast) - break - case Processor.MDB_TYPE_VORBIS_COMMENT: - this.mdb = new MetaDataBlockVorbisComment(this.mdbLast) - break - case Processor.MDB_TYPE_PICTURE: - this.mdb = new MetaDataBlockPicture(this.mdbLast) - break - case Processor.MDB_TYPE_PADDING: - case Processor.MDB_TYPE_APPLICATION: - case Processor.MDB_TYPE_SEEKTABLE: - case Processor.MDB_TYPE_CUESHEET: - case Processor.MDB_TYPE_INVALID: - default: - this.mdb = new MetaDataBlock(this.mdbLast, type) - break - } - - this.emit('preprocess', this.mdb) - - if (this.mdbLastWritten) { - // A previous MDB had the isLast flag set to true. - // Ignore all following MDBs. - this.mdb.remove() - } else { - // The consumer may change the MDB's isLast flag in the preprocess handler. - // Here that flag is updated in the MDB header. - if (this.mdbLast !== this.mdb.isLast) { - if (this.mdb.isLast) { - header |= 0x80000000 - } else { - header &= 0x7fffffff - } - slice.writeUInt32BE(header >>> 0, 0) - } - } - this.mdbPush = !this.mdb.removed - return this.mdbPush - } - - _validateMDB(slice, isDone) { - // Parse the MDB if parseMetaDataBlocks option is set to true - if (this.parseMetaDataBlocks && isDone) { - this.mdb.parse(slice) - } - return this.mdbPush - } - - _flush(done) { - // All chunks have been processed - // Clean up - this.state = STATE_IDLE - this.mdbLastWritten = false - this.isFlac = false - this.bufPos = 0 - this.buf = null - this.mdb = null - done() - } -} - -module.exports = Processor diff --git a/src/main/utils/flacMeta.js b/src/main/utils/flacMeta.js index 64bfad45..28232c35 100644 --- a/src/main/utils/flacMeta.js +++ b/src/main/utils/flacMeta.js @@ -1,38 +1,94 @@ const fs = require('fs') -const { Processor: FlacProcessor, data: { MetaDataBlockVorbisComment: FlacComment } } = require('./flac-metadata') +const path = require('path') +const getImgSize = require('image-size') +const request = require('request') + +const FlacProcessor = require('./flac-metadata') + +const extReg = /^(\.(?:jpe?g|png)).*$/ const vendor = 'reference libFLAC 1.2.1 20070917' -module.exports = (filenPath, meta) => { - const reader = fs.createReadStream(filenPath) - const tempPath = filenPath + '.lxmtemp' - const writer = fs.createWriteStream(tempPath) - const flacProcessor = new FlacProcessor() - if (meta.APIC) delete meta.APIC - const comments = [] - for (const key in meta) { - comments.push(`${key.toUpperCase()}=${meta[key]}`) +const writeMeta = (filePath, meta, picPath) => { + const comments = Object.keys(meta).map(key => `${key.toUpperCase()}=${meta[key]}`) + const data = { + vorbis: { + vendor, + comments, + }, + } + if (picPath) { + const apicData = Buffer.from(fs.readFileSync(picPath, 'binary'), 'binary') + let imgSize = getImgSize(apicData) + let mime_type + let bitsPerPixel + if (apicData[0] == 0xff && apicData[1] == 0xd8 && apicData[2] == 0xff) { + mime_type = 'image/jpeg' + bitsPerPixel = 24 + } else { + mime_type = 'image/png' + bitsPerPixel = 32 + } + data.picture = { + pictureType: 3, + mimeType: mime_type, + description: '', + width: imgSize.width, + height: imgSize.height, + bitsPerPixel, + colors: 0, + pictureData: apicData, + } } - let isInjected = false - flacProcessor.on('preprocess', function(mdb) { - // Remove existing VORBIS_COMMENT block, if any. - if (mdb.type === flacProcessor.MDB_TYPE_VORBIS_COMMENT) mdb.remove() - // Inject new VORBIS_COMMENT block. - if ((mdb.removed || mdb.isLast) && !isInjected) { - isInjected = true - let mdbVorbis = FlacComment.create(mdb.isLast, vendor, comments) - this.push(mdbVorbis.publish()) - } - }) + const reader = fs.createReadStream(filePath) + const tempPath = filePath + '.lxmtemp' + const writer = fs.createWriteStream(tempPath) + const flacProcessor = new FlacProcessor() + flacProcessor.writeMeta(data) reader.pipe(flacProcessor).pipe(writer).on('finish', () => { - fs.unlink(filenPath, err => { + fs.unlink(filePath, err => { if (err) return console.log(err.message) - fs.rename(tempPath, filenPath, err => { + fs.rename(tempPath, filePath, err => { if (err) console.log(err.message) }) }) }) } +module.exports = (filePath, meta) => { + if (!meta.APIC) return writeMeta(filePath, meta) + const picUrl = meta.APIC + delete meta.APIC + if (!/^http/.test(picUrl)) { + return writeMeta(filePath, meta) + } + let picPath = filePath.replace(/\.flac$/, '') + path.extname(picUrl).replace(extReg, '$1') + + request(picUrl) + .on('response', respones => { + if (respones.statusCode !== 200 && respones.statusCode != 206) return writeMeta(filePath, meta) + respones + .pipe(fs.createWriteStream(picPath)) + .on('finish', () => { + if (respones.complete) { + writeMeta(filePath, meta, picPath) + } else { + writeMeta(filePath, meta) + } + fs.unlink(picPath, err => { + if (err) console.log(err.message) + }) + }) + .on('error', err => { + if (err) console.log(err.message) + writeMeta(filePath, meta) + }) + }) + .on('error', err => { + if (err) console.log(err.message) + writeMeta(filePath, meta) + }) +} + diff --git a/src/main/utils/mp3Meta.js b/src/main/utils/mp3Meta.js index 5bfa0642..bded0892 100644 --- a/src/main/utils/mp3Meta.js +++ b/src/main/utils/mp3Meta.js @@ -26,6 +26,7 @@ module.exports = (filePath, meta) => { NodeID3.write(meta, filePath) } else { delete meta.APIC + NodeID3.write(meta, filePath) } fs.unlink(picPath, err => { if (err) console.log(err.message) diff --git a/src/renderer/lang/cns/view/setting.json b/src/renderer/lang/cns/view/setting.json index 29b99e94..9ae382e0 100644 --- a/src/renderer/lang/cns/view/setting.json +++ b/src/renderer/lang/cns/view/setting.json @@ -58,7 +58,7 @@ "download_name_title": "下载歌曲时的命名方式", "download_name": "文件命名方式", "download_embed_pic_title": "是否将封面嵌入音频文件中", - "download_embed_pic": "封面嵌入(只支持MP3格式)", + "download_embed_pic": "封面嵌入", "download_lyric_title": "是否同时下载歌词文件", "download_lyric": "歌词下载", "download_name1": "歌名 - 歌手", diff --git a/src/renderer/lang/cnt/view/setting.json b/src/renderer/lang/cnt/view/setting.json index 50011d1c..f59fd24a 100644 --- a/src/renderer/lang/cnt/view/setting.json +++ b/src/renderer/lang/cnt/view/setting.json @@ -54,7 +54,7 @@ "download_name_title": "下載歌曲時的命名方式", "download_name": "文件命名方式", "download_embed_pic_title": "是否將封面嵌入音頻文件中", - "download_embed_pic": "封面嵌入(只支持MP3格式)", + "download_embed_pic": "封面嵌入", "download_lyric_title": "是否同時下載歌詞文件", "download_lyric": "歌詞下載", "download_name1": "歌名 - 歌手", diff --git a/src/renderer/lang/en/view/setting.json b/src/renderer/lang/en/view/setting.json index 1ff617cd..f1d4c099 100644 --- a/src/renderer/lang/en/view/setting.json +++ b/src/renderer/lang/en/view/setting.json @@ -58,7 +58,7 @@ "download_name_title": "Naming when downloading songs", "download_name": "File naming", "download_embed_pic_title": "Whether to embed the cover in the audio file", - "download_embed_pic": "Cover embedding (only supports MP3 format)", + "download_embed_pic": "Cover embedding", "download_lyric_title": "Whether to download lyrics files at the same time", "download_lyric": "Lyrics download", "download_name1": "name - singer",