diff --git a/README.md b/README.md index 385c1ca..699996b 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ A writable stream to save HLS playlists/segments as local files ## Features * Being used with other [`hlx`](https://github.com/hlxjs) objects, it provides a functionality to write every HLS related data (playlist and segments) to your local filesystem. * It determines the local path for each files based on the `uri` described in the HLS playlist. -* The hostname contained in the `uri` will be ignored (e.g. "https://foo.bar/abc/def.m3u8" is translated into "{rootPath}/abc/def.m3u8") +* The hostname contained in the `uri` will be ignored (e.g. "https://foo.bar/abc/def.m3u8" is translated into "{outputDir}/abc/def.m3u8") +* If the `uri` is a file url, users can specify a root directory (`inputDir`) from which the file should be read (e.g. "file://path/to/abc/def.m3u8", inputDir="/path/to", is translated into "{outputDir}/abc/def.m3u8") ## Install [![NPM](https://nodei.co/npm/hlx-file-writer.png?mini=true)](https://nodei.co/npm/hlx-file-writer/) @@ -27,7 +28,7 @@ const {createTerminator} = require('hlx-terminator') const src = createReadStream('https://foo.bar/sample.m3u8'); const rewrite = createUrlRewriter(); const save = createWriteStream({ - rootPath: '/var/www/media/', + outputDir: '/var/www/media/', storePlaylist: true }); const dest = createTerminator(); @@ -52,7 +53,8 @@ Creates a new `TransformStream` object. #### options | Name | Type | Default | Description | | ----------- | ------ | ------- | --------------------------------- | -| rootPath | string | process.CWD() | The root directory in which all the files are stored | +| inputDir | string | / | The root directory from which all the files are read (This option is only used in case of file urls) | +| outputDir | string | process.CWD() | The root directory to which all the files are written | | storePlaylist | boolean | false | If true, the playlist files are also stored as local files | #### return value diff --git a/file.js b/file.js index 50cb7b3..62d11b3 100644 --- a/file.js +++ b/file.js @@ -1,28 +1,38 @@ const fs = require('fs'); const path = require('path'); - +const {URL} = require('url'); +const debug = require('debug'); const {tryCatch, mkdirP} = require('hlx-util'); -function storeData({uri, data}, rootPath) { +const print = debug('hlx-file-writer'); + +function storeData({uri, parentUri, data}, {inputDir, outputDir}) { if (!data) { return Promise.reject(new Error('No segment data')); } - if (!path.isAbsolute(rootPath)) { - rootPath = path.join(process.cwd(), rootPath); + if (!path.isAbsolute(outputDir)) { + outputDir = path.join(process.cwd(), outputDir); } let localPath; + print(`storeData: uri=${uri}, parentUri=${parentUri}, inputDir=${inputDir}, outputDir=${outputDir}`); + if (path.isAbsolute(uri)) { - localPath = path.join(rootPath, uri); + localPath = path.join(outputDir, uri); } else { const obj = tryCatch( () => new URL(uri), - () => new URL(uri, rootPath), + () => new URL(uri, parentUri), () => null ); - localPath = path.join(rootPath, obj ? obj.pathname : uri); + if (obj) { + localPath = path.join(outputDir, obj.pathname); + } else { + const pathname = path.relative(inputDir, path.join(parentUri, uri)); + localPath = path.join(outputDir, pathname); + } } // Create directory diff --git a/test/spec/file.spec.js b/test/spec/file.spec.js index 07df3c6..b316313 100644 --- a/test/spec/file.spec.js +++ b/test/spec/file.spec.js @@ -23,7 +23,7 @@ test.cb('file.storeData.Buffer', t => { const {storeData} = proxyquire('../../file', {fs: mockFs, 'hlx-util': mockUtil}); const spyWriteFile = sinon.spy(mockFs, 'writeFile'); - storeData({uri: 'ghi.mp4', data: Buffer.alloc(10)}, '/abc/def/') + storeData({uri: 'ghi.mp4', parentUri: 'http://foo.bar.com/main.m3u8', data: Buffer.alloc(10)}, {inputDir: '/does/not/matter', outputDir: '/abc/def/'}) .then(destPath => { t.is(destPath, '/abc/def/ghi.mp4'); t.is(spyWriteFile.callCount, 1); @@ -51,9 +51,9 @@ test.cb('file.storeData.Buffer.path', t => { const {storeData} = proxyquire('../../file', {fs: mockFs, 'hlx-util': mockUtil}); const spyWriteFile = sinon.spy(mockFs, 'writeFile'); - storeData({uri: '/abc/def/ghi/jkl.mp4', data: Buffer.alloc(10)}, '/path/to/') + storeData({uri: '/abc/def/ghi/jkl.mp4', parentUri: 'file:///path/to/main.m3u8', data: Buffer.alloc(10)}, {inputDir: '/path/to', outputDir: '/root/dir/'}) .then(destPath => { - t.is(destPath, '/path/to/abc/def/ghi/jkl.mp4'); + t.is(destPath, '/root/dir/abc/def/ghi/jkl.mp4'); t.is(spyWriteFile.callCount, 1); t.end(); }); @@ -92,9 +92,9 @@ test.cb('file.storeData.Stream', t => { } }; - storeData({uri: 'ghi.mp4', data}, '/abc/def/') + storeData({uri: 'ghi.mp4', parentUri: 'file:///path/to/main.m3u8', data}, {inputDir: '/', outputDir: '/abc/def/'}) .then(destPath => { - t.is(destPath, '/abc/def/ghi.mp4'); + t.is(destPath, '/abc/def/path/to/ghi.mp4'); t.is(spyCreateWriteStream.callCount, 1); t.end(); }); diff --git a/test/spec/writable.spec.js b/test/spec/writable.spec.js index 99fce66..a4cc8fd 100644 --- a/test/spec/writable.spec.js +++ b/test/spec/writable.spec.js @@ -129,7 +129,7 @@ test.cb('writeStream.onlySegments', t => { const spyWriteFile = sinon.spy(mockFs, 'writeFile'); const src = new MockReadStream(); - const fileOutput = new WriteStream({rootPath: '/var/foo/'}); + const fileOutput = new WriteStream({outputDir: '/var/foo/'}); const dest = new NullWriteStream(); src.pipe(fileOutput).pipe(dest) .on('finish', () => { @@ -160,7 +160,7 @@ test.cb('writeStream.all', t => { const spyWriteFile = sinon.spy(mockFs, 'writeFile'); const src = new MockReadStream(); - const fileOutput = new WriteStream({rootPath: '/var/foo/', storePlaylist: true}); + const fileOutput = new WriteStream({outputDir: '/var/foo/', storePlaylist: true}); const dest = new NullWriteStream(); src.pipe(fileOutput).pipe(dest) .on('finish', () => { diff --git a/writable.js b/writable.js index 759bdb7..c333da2 100644 --- a/writable.js +++ b/writable.js @@ -9,7 +9,8 @@ const print = debug('hlx-file-writer'); class WriteStream extends stream.Transform { constructor(options) { super({objectMode: true}); - this.rootPath = options.rootPath || process.cwd(); + this.outputDir = options.outputDir || process.cwd(); + this.outputDir = options.inputDir || '/'; this.shouldStorePlaylist = Boolean(options.storePlaylist); } @@ -22,10 +23,10 @@ class WriteStream extends stream.Transform { let params = data; if (data.type === 'playlist') { - params = {uri: data.uri, data: HLS.stringify(data)}; + params = {uri: data.uri, parentUri: data.parentUri, data: HLS.stringify(data)}; } - storeData(params, this.rootPath) + storeData(params, this) .then(path => { print(`The data is written to ${path}`); this.push(data);