From b3b5b052799e9b7ed4136fa07418b3be052111dc Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 25 Jun 2018 18:22:47 +0800 Subject: [PATCH 1/4] BREAKING: drop bulebird; make `mp3Duration` async function --- index.js | 126 ++++++++++++++++++++++++++----------------------------- 1 file changed, 59 insertions(+), 67 deletions(-) diff --git a/index.js b/index.js index 5525f2e..99f574c 100644 --- a/index.js +++ b/index.js @@ -1,13 +1,12 @@ 'use strict'; -var fs = require('fs') - , Promise = require('bluebird') - , co = require('bluebird-co').co +const fs = require('fs'); +const { promisify } = require('util'); - , open = Promise.promisify(fs.open) //(filename, flags [, mode]) - , read = Promise.promisify(fs.read).bind(fs) //(fd, buffer, bufferOffset, length, position) - , fstat = Promise.promisify(fs.fstat).bind(fs) //(fs) - , close = Promise.promisify(fs.close).bind(fs); +const open = promisify(fs.open); +const read = promisify(fs.read); +const fstat = promisify(fs.fstat); +const close = promisify(fs.close); function skipId3(buffer) { var id3v2Flags @@ -36,7 +35,7 @@ function skipId3(buffer) { return 0; } -var versions = ['2.5', 'x', '2', '1'] +const versions = ['2.5', 'x', '2', '1'] , layers = ['x', '3', '2', '1'] , bitRates = { 'V1Lx' : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -142,83 +141,76 @@ function round(duration) { return Math.round(duration * 1000) / 1000; //round to nearest ms } -function mp3Duration(filename, cbrEstimate, callback) { - if (typeof cbrEstimate === 'function') { - callback = cbrEstimate; - cbrEstimate = false; +async function mp3Duration(filename, cbrEstimate = false) { + var duration = 0 + , fd + , buffer + , bytesRead + , offset + , stat + , info + , srcBuffer + , isBuffer = false; + + if (typeof filename === 'string') { + fd = await open(filename, 'r'); + } else if (filename instanceof Buffer) { + srcBuffer = filename; + isBuffer = true; } - return co(function* () { - var duration = 0 - , fd - , buffer - , bytesRead - , offset - , stat - , info - , srcBuffer - , isBuffer = false; - - if (typeof filename === 'string') { - fd = yield open(filename, 'r'); - } else if (filename instanceof Buffer) { - srcBuffer = filename; - isBuffer = true; + + try { + if (!isBuffer) { + stat = await fstat(fd); } - try { - if (!isBuffer) { - stat = yield fstat(fd); - } + buffer = Buffer.alloc(100); + + if (!isBuffer) { + bytesRead = await read(fd, buffer, 0, 100, 0); + } else { + bytesRead = srcBuffer.copy(buffer, 0, 0, 100); + } + if (bytesRead < 100) return 0; - buffer = new Buffer(100); + offset = skipId3(buffer); + while (offset < (isBuffer ? srcBuffer.length : stat.size)) { if (!isBuffer) { - bytesRead = yield read(fd, buffer, 0, 100, 0); + bytesRead = await read(fd, buffer, 0, 10, offset); } else { - bytesRead = srcBuffer.copy(buffer, 0, 0, 100); + bytesRead = srcBuffer.copy(buffer, 0, offset, offset + 10); } - if (bytesRead < 100) return 0; - - offset = skipId3(buffer); - while (offset < (isBuffer ? srcBuffer.length : stat.size)) { - if (!isBuffer) { - bytesRead = yield read(fd, buffer, 0, 10, offset); - } else { - bytesRead = srcBuffer.copy(buffer, 0, offset, offset + 10); - } + if (bytesRead < 10) return round(duration); - if (bytesRead < 10) return round(duration); - - //Looking for 1111 1111 111 (frame synchronization bits) - if (buffer[0] === 0xff && (buffer[1] & 0xe0) === 0xe0) { - info = parseFrameHeader(buffer); - if (info.frameSize && info.samples) { - offset += info.frameSize; - duration += ( info.samples / info.sampleRate ); - } else { - offset++; //Corrupt file? - } - } else if (buffer[0] === 0x54 && buffer[1] === 0x41 && buffer[2] === 0x47) {//'TAG' - offset += 128; //Skip over id3v1 tag size + //Looking for 1111 1111 111 (frame synchronization bits) + if (buffer[0] === 0xff && (buffer[1] & 0xe0) === 0xe0) { + info = parseFrameHeader(buffer); + if (info.frameSize && info.samples) { + offset += info.frameSize; + duration += ( info.samples / info.sampleRate ); } else { offset++; //Corrupt file? } - - if (cbrEstimate && info) { - return round(estimateDuration(info.bitRate, offset, stat.size)); - } + } else if (buffer[0] === 0x54 && buffer[1] === 0x41 && buffer[2] === 0x47) {//'TAG' + offset += 128; //Skip over id3v1 tag size + } else { + offset++; //Corrupt file? } - return round(duration); - - } finally { - if (!isBuffer) { - yield close(fd); + if (cbrEstimate && info) { + return round(estimateDuration(info.bitRate, offset, stat.size)); } } - }).asCallback(callback); + return round(duration); + + } finally { + if (!isBuffer) { + await close(fd); + } + } } module.exports = mp3Duration; From ed8194df1d2a20d8e558a8c62cc611e6d149e9e2 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 25 Jun 2018 18:25:07 +0800 Subject: [PATCH 2/4] package: create TypeScript declaration file --- index.d.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 index.d.ts diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..38ca526 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,4 @@ +declare function mp3Duration(filePathOrBuffer: string | Buffer , cbrEstimate?: boolean): Promise; +declare function mp3Duration(filePathOrBuffer: string | Buffer): Promise; + +export = mp3Duration; From 0145193644058a3f9409d5c02197c2f5a1d8a9df Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 25 Jun 2018 18:25:39 +0800 Subject: [PATCH 3/4] package: update dependencies, test, ci config --- .travis.yml | 3 ++- package.json | 7 +++---- tests/test.js | 51 +++++++++++++++++---------------------------------- 3 files changed, 22 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index dd5ab9b..21175be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ language: node_js node_js: - - '0.12' + - '8' + - 'node' diff --git a/package.json b/package.json index 1cafebe..8e5911c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "url": "git://github.com/ddsol/mp3-duration" }, "engines": { - "node": ">=0.11.0" + "node": ">=8.0.0" }, "keywords": [ "mp3", @@ -27,11 +27,10 @@ }, "homepage": "https://github.com/ddsol/mp3-duration", "dependencies": { - "bluebird": "^3.5.1", - "bluebird-co": "^2.2.0" + "@types/node": ">=8.0.0" }, "devDependencies": { - "mocha": "^2.2.4", + "mocha": "^5.2.0", "nyc": "^11.2.1" } } diff --git a/tests/test.js b/tests/test.js index 03789f1..8d2100b 100644 --- a/tests/test.js +++ b/tests/test.js @@ -2,52 +2,35 @@ var assert = require('assert'), mp3Duration = require('../index.js'), fs = require('fs'); -describe('mp3Duration', function() { - - it('returns a correct value for VBR duration', function(done) { - mp3Duration('./tests/demo - vbr.mp3', function(err, length) { - if (err) console.log(err.stack); - assert.equal(err, null, (err || {}).message); - assert.equal(length, 285.727, 'Length not as expected'); - done(); - }); +describe('mp3Duration', function () { + + it('returns a correct value for VBR duration', async () => { + const length = await mp3Duration('./tests/demo - vbr.mp3'); + assert.equal(length, 285.727, 'Length not as expected'); }); - it('returns a correct value for CBR duration with estimate', function(done) { - mp3Duration('./tests/demo - cbr.mp3', true, function(err, length) { - assert.equal(err, null, (err || {}).message); - assert.equal(length, 285.727, 'Length not as expected'); - done(); - }); + it('returns a correct value for CBR duration with estimate', async () => { + const length = await mp3Duration('./tests/demo - cbr.mp3', true); + assert.equal(length, 285.727, 'Length not as expected'); }); - it('returns a correct value for CBR duration without estimate', function(done) { - mp3Duration('./tests/demo - cbr.mp3', function(err, length) { - assert.equal(err, null, (err || {}).message); - assert.equal(length, 285.78, 'Length not as expected'); - done(); - }); + it('returns a correct value for CBR duration without estimate', async () => { + const length = await mp3Duration('./tests/demo - cbr.mp3'); + assert.equal(length, 285.78, 'Length not as expected'); }); - it('returns a correct value for VBR duration when passing a buffer instead of a filename', function(done) { + it('returns a correct value for VBR duration when passing a buffer instead of a filename', async () => { const buffer = fs.readFileSync('./tests/demo - vbr.mp3'); - mp3Duration(buffer, function(err, length) { - if (err) console.log(err.stack); - assert.equal(err, null, (err || {}).message); - assert.equal(length, 285.727, 'Length not as expected'); - done(); - }); + const length = await mp3Duration(buffer); + assert.equal(length, 285.727, 'Length not as expected'); }); - it('return a correct value for CBR duration without estimate when passing a buffer instead of a filename', function(done) { + it('return a correct value for CBR duration without estimate when passing a buffer instead of a filename', async () => { const buffer = fs.readFileSync('./tests/demo - cbr.mp3'); - mp3Duration(buffer, function(err, length) { - assert.equal(err, null, (err || {}).message); - assert.equal(length, 285.78, 'Length not as expected'); - done(); - }); + const length = await mp3Duration(buffer); + assert.equal(length, 285.78, 'Length not as expected'); }); }); From f678afb898c0b67d45f0606e68b5411c0a8f2d15 Mon Sep 17 00:00:00 2001 From: Rocka Date: Mon, 25 Jun 2018 18:25:52 +0800 Subject: [PATCH 4/4] doc: update README.md with new function signature --- README.md | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0345711..35d5b5c 100644 --- a/README.md +++ b/README.md @@ -2,51 +2,59 @@ > Calculate duration of an MP3 - ## Install -``` -$ npm install --save mp3-duration +```sh +npm install mp3-duration ``` ## Usage +1. JavaScript + ```javascript var mp3Duration = require('mp3-duration'); -mp3Duration('file.mp3', function (err, duration) { - if (err) return console.log(err.message); - console.log('Your file is ' + duration + ' seconds long'); +mp3Duration('file.mp3').then(duration => { + console.log(`Your file is ${duration} seconds long`); +}).catch(e => { + console.err(e); +}); +``` + +2. TypeScript + +```typescript +import mp3Duration = require('mp3-duration'); + +mp3Duration('file.mp3').then(duration => { + console.log(`Your file is ${duration} seconds long`); +}).catch(e => { + console.err(e); }); ``` ## API -## mp3Duration(filePathOrBuffer [, cbrEstimate] [, callback]) +## mp3Duration(filePathOrBuffer [, cbrEstimate]) ### filePathOrBuffer -Type: `string` | `Buffer` +Type: `string | Buffer` Path to the file or a buffer with the file's contents ### cbrEstimate +Type: `boolean` + Defaults to `false`. When set to `true`, will estimate the length of a constant-bitrate mp3. This speeds up the calculation a lot but isn't guaranteed to be accurate. -### callback(error, duration) - -Type: `function` - -Callback to be called once duration is calculated. It's also possible to -instead use the returned `Promise`. `duration` is the duration of the -mp3 in `ms`. - ### Return value -`mp3Duration` returns a Promise that resolves to the duration of the mp3 in `ms` or rejects with some error. +`mp3Duration` returns a Promise that resolves to the duration of the mp3 in `second` or rejects with some error. ## License