diff --git a/README.md b/README.md index b41a854..821f43f 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ Extension for [music-metadata](https://github.com/Borewit/music-metadata) to retrieve metadata from files stored on [Amazon Web Services (AWS) S3 cloud storage](https://docs.aws.amazon.com/AmazonS3/latest/dev/Welcome.html). The magic of this module is, it is able to extract the metadata from your audio files, without downloading and parsing the entire file. -Using [@tokenizer/range](https://github.com/Borewit/tokenizer-range), it partial downloads the files, just accessing the chunks holding the metadata. + +This module is integrating [@tokenizer/s3](https://github.com/Borewit/tokenizer-s3), for reading from the S3 cloud, and using [music-metadata](https://github.com/Borewit/music-metadata) to parse the audio file. ## Installation ```shell script @@ -21,17 +22,16 @@ npm install @music-metadata/s3 Read metadata from 'My audio files/01 - My audio track.flac' stored in the S3 cloud: ```js -const { MMS3Client } = require('@music-metadata/s3'); +const { parseS3Object } = require('@music-metadata/s3'); const S3 = require('aws-sdk/clients/s3'); (async () => { const s3 = new S3(); - const mmS3client = new MMS3Client(s3); // Pass S3 client to music-metadata-S3-client console.log('Parsing...'); try { - const data = await mmS3client.parseS3Object({ + const data = await parseS3Object(s3, { Bucket: 'your-bucket', Key: 'My audio files/01 - My audio track.flac' } @@ -45,17 +45,16 @@ const S3 = require('aws-sdk/clients/s3'); Using conventional streaming using the `disableChunked` flag: ```js -const { MMS3Client } = require('@music-metadata/s3'); +const { parseS3Object } = require('@music-metadata/s3'); const S3 = require('aws-sdk/clients/s3'); (async () => { const s3 = new S3(); - const mmS3client = new MMS3Client(s3); // Pass S3 client to music-metadata-S3-client - + console.log('Parsing...'); try { - const data = await mmS3client.parseS3Object({ + const data = await parseS3Object(s3, { Bucket: 'your-bucket', Key: 'My audio files/01 - My audio track.flac' }, { diff --git a/lib/index.ts b/lib/index.ts index 174453a..8336751 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,78 +1,20 @@ import * as S3 from 'aws-sdk/clients/s3'; -import { parseFromTokenizer, parseStream } from 'music-metadata/lib/core'; -import { RangeRequestTokenizer, IRangeRequestClient, IRangeRequestResponse, parseContentRange } from '@tokenizer/range'; -import { AWSError, Request } from 'aws-sdk'; import { IOptions, IAudioMetadata } from 'music-metadata/lib/type'; +import { makeTokenizer, IS3Options} from '@tokenizer/s3'; +import * as mm from 'music-metadata/lib/core'; export { IPicture, IAudioMetadata, IOptions, ITag, INativeTagDict } from 'music-metadata/lib/type'; -interface IS3Options extends IOptions { - /** - * Flag to disable chunked transfer, use conventional HTTPS stream instead - */ - disableChunked?: boolean; +interface IMMS3Options extends IOptions, IS3Options { } /** - * Use S3-client to execute actual HTTP-requests. + * Retrieve metadata from Amazon S3 object + * @param objRequest S3 object request + * @param options music-metadata options + * @return Metadata */ -class S3Request implements IRangeRequestClient { - - constructor(private s3client: MMS3Client, private objRequest: S3.Types.GetObjectRequest) { - } - - public async getResponse(method, range: number[]): Promise { - - return this.s3client.getRangedRequest(this.objRequest, range).promise().then(data => { - return { - contentLength: data.ContentLength, - contentType: data.ContentType, - contentRange: parseContentRange(data.ContentRange), - arrayBuffer: async () => { - return data.Body as Buffer; - } - }; - }); - } -} - -export class MMS3Client { - - constructor(private s3: S3) { - } - - /** - * Do a ranged request, this method will be called by streaming-http-token-reader - * @param objRequest - * @param range - */ - public getRangedRequest(objRequest: S3.Types.GetObjectRequest, range: number[]): Request { - const rangedRequest = {...objRequest}; // Copy request - rangedRequest.Range = `bytes=${range[0]}-${range[1]}`; - return this.s3.getObject(rangedRequest); - } - - /** - * Retrieve metadata from Amazon S3 object - * @param objRequest S3 object request - * @param options music-metadata options - */ - public async parseS3Object(objRequest: S3.Types.GetObjectRequest, options?: IS3Options): Promise { - if (options && options.disableChunked) { - - const info = await this.getRangedRequest(objRequest, [0, 0]).promise(); - - const stream = this.s3 - .getObject(objRequest) - .createReadStream(); - return parseStream(stream, info.ContentType, options); - } else { - const s3Request = new S3Request(this, objRequest); - const rangeRequestTokenizer = new RangeRequestTokenizer(s3Request, { - avoidHeadRequests: true - }); - await rangeRequestTokenizer.init(); - return parseFromTokenizer(rangeRequestTokenizer, rangeRequestTokenizer.contentType, options); - } - } +export async function parseS3Object(s3: S3, objRequest: S3.Types.GetObjectRequest, options?: IMMS3Options): Promise { + const s3Tokenizer = await makeTokenizer(s3, objRequest, options); + return mm.parseFromTokenizer(s3Tokenizer, options); } diff --git a/package.json b/package.json index 822d733..9bb8736 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "url": "https://github.com/Borewit/music-metadata-s3/issues" }, "dependencies": { - "@tokenizer/range": "^0.1.0", - "music-metadata": "^5.4.3" + "@tokenizer/s3": "^0.1.1", + "music-metadata": "^6.0.1" }, "devDependencies": { "aws-sdk": "^2.596.0", diff --git a/test/test.js b/test/test.js index 4796b77..a321981 100644 --- a/test/test.js +++ b/test/test.js @@ -1,19 +1,17 @@ -const {MMS3Client} = require('../lib'); +const {parseS3Object} = require('../lib'); const S3 = require('aws-sdk/clients/s3'); const {assert} = require('chai'); -describe('Parse audio files from AWS S3 cloud', function() { +describe('Parse audio files from AWS S3 cloud', function () { this.timeout(20000); + const s3 = new S3(); describe('Transfer mode: chunked', () => { it('explicitly set', async () => { - const s3 = new S3(); - const mmS3client = new MMS3Client(s3); // Pass s3 client to music-metadata-s3-client - - const metadata = await mmS3client.parseS3Object({ + const metadata = await parseS3Object(s3, { Bucket: 'music-metadata', Key: 'Various Artists - 2008 - netBloc Vol 13 (color in a world of monochrome) {BSCOMP0013} [MP3-V0]/01 - Nils Hoffmann - Sweet Man Like Me.mp3' }, { @@ -28,10 +26,7 @@ describe('Parse audio files from AWS S3 cloud', function() { it('default options', async () => { - const s3 = new S3(); - const mmS3client = new MMS3Client(s3); // Pass s3 client to music-metadata-s3-client - - const metadata = await mmS3client.parseS3Object({ + const metadata = await parseS3Object(s3, { Bucket: 'music-metadata', Key: 'Various Artists - 2008 - netBloc Vol 13 (color in a world of monochrome) {BSCOMP0013} [MP3-V0]/01 - Nils Hoffmann - Sweet Man Like Me.mp3' } @@ -46,10 +41,7 @@ describe('Parse audio files from AWS S3 cloud', function() { it('Transfer mode: conventional', async () => { - const s3 = new S3(); - const mmS3client = new MMS3Client(s3); // Pass s3 client to music-metadata-s3-client - - const metadata = await mmS3client.parseS3Object({ + const metadata = await parseS3Object(s3, { Bucket: 'music-metadata', Key: 'Various Artists - 2008 - netBloc Vol 13 (color in a world of monochrome) {BSCOMP0013} [MP3-V0]/01 - Nils Hoffmann - Sweet Man Like Me.mp3' }, { diff --git a/yarn.lock b/yarn.lock index 96c0091..a08c717 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,13 +39,21 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" -"@tokenizer/range@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@tokenizer/range/-/range-0.1.1.tgz#a174031cbdb8eebf3dc0e0a8eee41217e42d8609" - integrity sha512-GpRSPWZt/AtCjwE1N4m+wFI0r1L7VhLpXiRXaohL3D5QSkUXbl0Im0BzEaxhz7UPg5eT/BxlUwUHMGacwQrTeA== +"@tokenizer/range@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@tokenizer/range/-/range-0.2.1.tgz#f50605c2b42936f296d8fd32569fee5c79ee6bbf" + integrity sha512-guIiNyR/VXFtYbpMbfQj8zVdbwu5MoRjh2YE/NwFeJJwkBOPBw5FhoHmJ7IAv4btHlFeBGtRDiLuuZtYkr12AA== dependencies: debug "^4.1.1" - strtok3 "^3.1.4" + strtok3 "^5.0.0" + +"@tokenizer/s3@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@tokenizer/s3/-/s3-0.1.1.tgz#4b55e9f0cf64ae1badfe2d5b6dd7169a60cba4e4" + integrity sha512-4mhhe8/aQBfjeNC3xH3SAx/IY4zrO0S2/HFbsHJWoWQtPZIU8UVgdfl/nm4197EZLMtPwO+i0k1c8cc//V6Fng== + dependencies: + "@tokenizer/range" "^0.2.1" + strtok3 "^5.0.0" "@tokenizer/token@^0.1.0": version "0.1.0" @@ -867,16 +875,16 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -music-metadata@^5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-5.4.3.tgz#b13c22e59312788922b0656e45a181075148cf6b" - integrity sha512-jGEfhqgfs3omD9sqaQg9ZzYXxsYYqv9wxMWz40WTNtm9cY7NE9TX+y+cL/JCs2Bs3+rj5xokOn6ZddpVvGsVFg== +music-metadata@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/music-metadata/-/music-metadata-6.0.1.tgz#968c25f3a1cb682d80fe5406a1bb180349f9b1b0" + integrity sha512-UrscSZs7MBhiGZZQgZHJj9tHdhl2+DLLC0jHhFyoHKUrrTg5SKFlVYmW76NgOk70hSpGiNN4x8s/izVMOs8hyg== dependencies: content-type "^1.0.4" debug "^4.1.0" file-type "^12.4.2" media-typer "^1.1.0" - strtok3 "^4.1.1" + strtok3 "^5.0.0" token-types "^2.0.0" node-environment-flags@1.0.5: @@ -1224,18 +1232,10 @@ strip-json-comments@2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strtok3@^3.1.4: - version "3.1.7" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-3.1.7.tgz#e236e955e7c93c156442a06f7b385a90a7f528d4" - integrity sha512-N0qFNf5vkyXgaPr4AHRmCTqdFwTnB1LxX3xo1N5KHMLhqircDdL9m8qem3Lpb8IIuJaNL1Jddazvb9WRwUdSlw== - dependencies: - debug "^4.1.1" - then-read-stream "^2.0.8" - -strtok3@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-4.1.1.tgz#75043bb6175ebb22f10d48dfe9b06560345dc647" - integrity sha512-7nfDPVwCrx35LVYqEZPfrNJuoqlgOcsW2PIcru4/IbYXjtI17WtdZLtRJtpwR1Mj/alJ01FY57NsX4Gwl/ntTg== +strtok3@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-5.0.0.tgz#cf48cdede0b7641eb97e62918abce18441cff688" + integrity sha512-HpdgEUSkMqlTjO7uWEBvWHEKBYqXCbVeihlE+sa0keGsfVXspVxye1dPa4OYvnzOJsErzn6ohQU1U/ozcVAPKQ== dependencies: "@tokenizer/token" "^0.1.0" debug "^4.1.1" @@ -1255,11 +1255,6 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" -then-read-stream@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/then-read-stream/-/then-read-stream-2.0.8.tgz#1d2c29e46af1327101875abef183e43313876c1b" - integrity sha512-OIQn3/zF2J/gp6mAQTbBb1AR+3yoSRqjaij0gGnEUcTl93T840mWIZ9sJWwubjwP7VUDwJpT+Tdl7T9RrQmMlw== - then-read-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/then-read-stream/-/then-read-stream-3.0.0.tgz#4a4ec37e23f18135b56fbc61670b6e8195e545b2"