From 78d243f3d5aa506f8d7584e18df05b5fb6f93543 Mon Sep 17 00:00:00 2001 From: Gustav Grusell Date: Fri, 30 Aug 2024 16:53:29 +0200 Subject: [PATCH 1/5] feat: add support for single segment file per stream * Support for packaging with a single segment file per stream and bytrange addressing * Support for packaging only dash or only hls * Support for specifying target segment duration Signed-off-by: Gustav Grusell --- README.md | 29 +++++++--- package.json | 3 +- src/cli.ts | 65 +++++++++++++++++----- src/packager.test.ts | 85 ++++++++++++++++++++++++++--- src/packager.ts | 127 ++++++++++++++++++++++++++++++++++++------- 5 files changed, 256 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index ab52f23..7d52805 100644 --- a/README.md +++ b/README.md @@ -92,18 +92,31 @@ Run script locally ``` % node dist/cli.js -h -Usage: cli [options] +Usage: cli [options] -Run shaka-packager with source on S3 and output to S3 +Run shaka-packager with source on S3 or locally, and output to S3 or local + + Examples: + $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s s3://source-bucket/folder -d s3://output-bucket/folder + $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder + $ shaka-packager-s3 -i a:2=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder --segment-single-file --segment-single-file-name 'Container$KEY$.mp4' --segment-duration 3.84 -Arguments: - source Source bucket URL (supported protocols: s3 - dest Destination bucket URL (supported protocols: s3) + Options: - -i, --input [inputOptions...] Input options on the format: [a|v]:=filename - --staging-dir Staging directory (default: /tmp/data) - -h, --help display help for command + -s, --source-folder [sourceFolder] Source folder URL, ignored if input uses absolute path (supported protocols: s3, local file) + -i, --input [inputOptions...] Input options on the format: [a|v]:=filename + --staging-dir [stagingDir] Staging directory (default: /tmp/data) + --shaka-executable [shakaExecutable] Path to shaka-packager executable, defaults to 'packager'. Can also be set with environment variable SHAKA_PACKAGER_EXECUTABLE. + --no-implicit-audio [noImplicitAudio] Do not include audio unless audio input specified + -d, --destination-folder Destination folder URL (supported protocols: s3, local file). Defaults to CWD. + --dash-only Package only DASH format + --hls-only Package only HLS format + --segment-single-file Use byte range addressing and a single segment file per stream + --segment-single-file-name [segmentSingleFileName] Template for single segment file name, $INDEX$ will be replaced with stream index + --segment-duration [segmentDuration] Segment target duration + -h, --help display help for command + ``` ## Support diff --git a/package.json b/package.json index e612f1f..bda5c7b 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "pretty": "prettier --check --ignore-unknown .", "typecheck": "tsc --noEmit -p tsconfig.json", "test": "jest --passWithNoTests", - "postversion": "git push && git push --tags" + "postversion": "git push && git push --tags", + "start": "ts-node -T src/cli.ts" }, "author": "Eyevinn Technology ", "license": "MIT", diff --git a/src/cli.ts b/src/cli.ts index c795d0a..361f8f0 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -27,6 +27,7 @@ cli Examples: $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s s3://source-bucket/folder -d s3://output-bucket/folder $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder + $ shaka-packager-s3 -i a:2=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder --segment-single-file --segment-single-file-name 'Container$KEY$.mp4' --segment-duration 3.84 ` ) .option( @@ -53,28 +54,62 @@ cli '-d, --destination-folder ', 'Destination folder URL (supported protocols: s3, local file). Defaults to CWD.' ) + .option('--dash-only', 'Package only DASH format') + .option('--hls-only', 'Package only HLS format') + .option( + '--segment-single-file', + 'Use byte range addressing and a single segment file per stream' + ) + .option( + '--segment-single-file-name [segmentSingleFileName]', + 'Template for single segment file name, must contain $KEY$ which will be replaced with key of corresponding input' + ) + .option('--segment-duration [segmentDuration]', 'Segment target duration') .action(async (options) => { try { const inputOptions = parseInputOptions(options.input); - if (inputOptions) { - console.log('inputs', inputOptions); - console.log( - `dest: ${options.destinationFolder}, source: ${options.sourceFolder}` - ); - await doPackage({ - dest: options.destinationFolder || '.', - source: options.sourceFolder, - inputs: inputOptions, - stagingDir: options.stagingDir, - noImplicitAudio: options.noImplicitAudio, - shakaExecutable: - options.shakaExecutable || process.env.SHAKA_PACKAGER_EXECUTABLE - }); - } else { + if (!inputOptions) { console.error('Need at least one input!\n'); cli.help(); process.exit(1); } + if (options.hlsOnly && options.dashOnly) { + console.error('Cannot disable both hls and dash\n'); + cli.help(); + process.exit(1); + } + if ( + options.segmentSingleFileName && + options.segmentSingleFileName.indexOf('$KEY$') === -1 + ) { + console.error( + '--segment-single-file-name argument must contain $KEY$\n' + ); + cli.help(); + process.exit(1); + } + console.log('inputs', inputOptions); + console.log( + `dest: ${options.destinationFolder}, source: ${options.sourceFolder}` + ); + await doPackage({ + dest: options.destinationFolder || '.', + source: options.sourceFolder, + inputs: inputOptions, + stagingDir: options.stagingDir, + noImplicitAudio: options.noImplicitAudio, + packageFormatOptions: { + hlsOnly: options.hlsOnly, + dashOnly: options.dashOnly, + segmentSingleFile: options.segmentSingleFile, + segmentSingleFileTemplate: options.segmentSingleFileName, + segmentDuration: options.segmentDuration + ? parseFloat(options.segmentDuration) + : undefined + }, + shakaExecutable: + options.shakaExecutable || process.env.SHAKA_PACKAGER_EXECUTABLE + }); } catch (err) { console.log((err as Error).message); } diff --git a/src/packager.test.ts b/src/packager.test.ts index 93d66d6..1c64aa6 100644 --- a/src/packager.test.ts +++ b/src/packager.test.ts @@ -1,13 +1,49 @@ -import { createShakaArgs, Input } from './packager'; +import { createShakaArgs, doPackage, Input } from './packager'; + +const singleInputVideo = [ + { + type: 'video', + filename: 'test.mp4', + key: '1' + } as Input +]; + +describe('Test doPackage', () => { + it('Both hlsOnly and dashOnly specified, throws error', async () => { + try { + await doPackage({ + inputs: singleInputVideo, + dest: '.', + packageFormatOptions: { + hlsOnly: true, + dashOnly: true + } + }); + fail('Should throw'); + } catch (err) { + expect((err as Error).message).toBe('Cannot disable both hls and dash'); + } + }); + + it('segmentSingleFileTemplate does not contain $KEY$, throws error', async () => { + try { + await doPackage({ + inputs: singleInputVideo, + dest: '.', + packageFormatOptions: { + segmentSingleFileTemplate: 'Container.mp4' + } + }); + fail('Should throw'); + } catch (err) { + expect((err as Error).message).toBe( + 'segmentSingleFileTemplate must contain $KEY$' + ); + } + }); +}); describe('Test create shaka args', () => { - const singleInputVideo = [ - { - type: 'video', - filename: 'test.mp4', - key: '1' - } as Input - ]; it('Should use first video file as audio source if noImplicitAudio not set', async () => { const args = createShakaArgs(singleInputVideo, false); expect(args).toEqual([ @@ -15,6 +51,7 @@ describe('Test create shaka args', () => { 'in=test.mp4,stream=audio,init_segment=audio/init.mp4,segment_template=audio/$Number$.m4s,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=defaultaudio', '--hls_master_playlist_output', 'index.m3u8', + '--generate_static_live_mpd', '--mpd_output', 'manifest.mpd' ]); @@ -26,6 +63,38 @@ describe('Test create shaka args', () => { 'in=test.mp4,stream=video,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s,playlist_name=video-1.m3u8', '--hls_master_playlist_output', 'index.m3u8', + '--generate_static_live_mpd', + '--mpd_output', + 'manifest.mpd' + ]); + }); + + it('Should set --segement_duration option if segmentDuration is set', async () => { + const args = createShakaArgs(singleInputVideo, true, { + segmentDuration: 3.84 + }); + expect(args).toEqual([ + 'in=test.mp4,stream=video,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s,playlist_name=video-1.m3u8', + '--hls_master_playlist_output', + 'index.m3u8', + '--generate_static_live_mpd', + '--mpd_output', + 'manifest.mpd', + '--segment_duration', + '3.84' + ]); + }); + + it('Should set correct output path for stream if single file segment specified', async () => { + const args = createShakaArgs(singleInputVideo, true, { + segmentSingleFile: true, + segmentSingleFileTemplate: 'Container-$KEY$.mp4' + }); + expect(args).toEqual([ + 'in=test.mp4,stream=video,out=Container-1.mp4,playlist_name=video-1.m3u8', + '--hls_master_playlist_output', + 'index.m3u8', + '--generate_static_live_mpd', '--mpd_output', 'manifest.mpd' ]); diff --git a/src/packager.ts b/src/packager.ts index 871194b..a02b9c0 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -13,17 +13,43 @@ export type Input = { filename: string; }; +export interface PackageFormatOptions { + hlsOnly?: boolean; + dashOnly?: boolean; + segmentSingleFile?: boolean; + segmentSingleFileTemplate?: string; + segmentDuration?: number; +} + export interface PackageOptions { inputs: Input[]; source?: string; dest: string; stagingDir?: string; noImplicitAudio?: boolean; + packageFormatOptions?: PackageFormatOptions; shakaExecutable?: string; serviceAccessToken?: string; } +function validateOptios(opts: PackageOptions) { + if ( + opts?.packageFormatOptions?.hlsOnly && + opts?.packageFormatOptions?.dashOnly + ) { + throw new Error('Cannot disable both hls and dash'); + } + if ( + opts?.packageFormatOptions?.segmentSingleFileTemplate && + opts?.packageFormatOptions?.segmentSingleFileTemplate.indexOf('$KEY$') === + -1 + ) { + throw new Error('segmentSingleFileTemplate must contain $KEY$'); + } +} + export async function doPackage(opts: PackageOptions) { + validateOptios(opts); const stagingDir = await prepare(opts.stagingDir); await createPackage({ ...opts, stagingDir }); await uploadPackage(toUrl(opts.dest), stagingDir); @@ -158,7 +184,11 @@ export async function createPackage(opts: PackageOptions) { }) ); - const args = createShakaArgs(downloadedInputs, noImplicitAudio === true); + const args = createShakaArgs( + downloadedInputs, + noImplicitAudio === true, + opts.packageFormatOptions + ); console.log(args); const shaka = opts.shakaExecutable || 'packager'; const { status, stderr, error } = spawnSync(shaka, args, { @@ -180,39 +210,94 @@ export async function createPackage(opts: PackageOptions) { * * @param inputs List of inputs, filename needs to be a local path * @param noImplicitAudio Should we use first video file as audio source if no audio input is provided + * @param packageFormatOptions Options for package format */ export function createShakaArgs( inputs: Input[], - noImplicitAudio: boolean + noImplicitAudio: boolean, + packageFormatOptions?: PackageFormatOptions ): string[] { const cmdInputs: string[] = []; - let fileForAudio; - for (const input of inputs) { + inputs.forEach((input: Input) => { if (input.type === 'video') { const playlistName = `video-${input.key}`; - const initSegment = join(playlistName, 'init.mp4'); - const segmentTemplate = join(playlistName, '$Number$.m4s'); const playlist = `${playlistName}.m3u8`; - const args = `in=${input.filename},stream=video,init_segment=${initSegment},segment_template=${segmentTemplate},playlist_name=${playlist}`; - cmdInputs.push(args); - if (!fileForAudio && !noImplicitAudio) { - fileForAudio = input.filename; + if (packageFormatOptions?.segmentSingleFile) { + const segmentName = + packageFormatOptions.segmentSingleFileTemplate?.replace( + '$KEY$', + input.key + ) || `${playlistName}.mp4`; + const args = `in=${input.filename},stream=video,out=${segmentName},playlist_name=${playlist}`; + cmdInputs.push(args); + } else { + const initSegment = join(playlistName, 'init.mp4'); + const segmentTemplate = join(playlistName, '$Number$.m4s'); + + const args = `in=${input.filename},stream=video,init_segment=${initSegment},segment_template=${segmentTemplate},playlist_name=${playlist}`; + cmdInputs.push(args); } - } else if (input.type === 'audio') { - fileForAudio = input.filename; } + }); + + const inputForAudio = getInputForAudio(inputs, noImplicitAudio); + + if (inputForAudio) { + const playlistName = `audio`; + const playlist = `${playlistName}.m3u8`; + const fileForAudio = inputForAudio.filename; + if (packageFormatOptions?.segmentSingleFile) { + // Ensure non-duplicate key, to ensure unique segment file name + const key = inputs.find( + (input) => input.type === 'video' && input.key == inputForAudio.key + ) + ? `audio-${inputForAudio.key}` + : inputForAudio?.key; + const segmentName = + packageFormatOptions.segmentSingleFileTemplate?.replace('$KEY$', key) || + `${playlistName}.mp4`; + + cmdInputs.push( + `in=${fileForAudio},stream=audio,out=${segmentName},playlist_name=${playlist},hls_group_id=audio,hls_name=defaultaudio` + ); + } else { + const segmentTemplate = 'audio/' + '$Number$.m4s'; + cmdInputs.push( + `in=${fileForAudio},stream=audio,init_segment=${playlistName}/init.mp4,segment_template=${segmentTemplate},playlist_name=${playlist},hls_group_id=audio,hls_name=defaultaudio` + ); + } + } else { + console.log('No audio input found'); + } + if (packageFormatOptions?.dashOnly !== true) { + cmdInputs.push('--hls_master_playlist_output', 'index.m3u8'); + } + if (packageFormatOptions?.hlsOnly !== true) { + cmdInputs.push( + '--generate_static_live_mpd', + '--mpd_output', + 'manifest.mpd' + ); } - if (fileForAudio) { + if (packageFormatOptions?.segmentDuration) { cmdInputs.push( - `in=${fileForAudio},stream=audio,init_segment=audio/init.mp4,segment_template=audio/$Number$.m4s,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=defaultaudio` + '--segment_duration', + packageFormatOptions.segmentDuration.toString() ); } - return cmdInputs.concat([ - '--hls_master_playlist_output', - 'index.m3u8', - '--generate_static_live_mpd', - '--mpd_output', - 'manifest.mpd' - ]); + return cmdInputs; +} + +function getInputForAudio( + inputs: Input[], + noImplicitAudio: boolean +): Input | undefined { + if (noImplicitAudio) { + return inputs.find((input) => input.type === 'audio'); + } + return ( + inputs.find((input) => input.type === 'audio') || + inputs.find((input) => input.type === 'video') + ); } From 8b232316a557c93dd0c5199e58ae1bc5ef59436a Mon Sep 17 00:00:00 2001 From: Gustav Grusell Date: Fri, 30 Aug 2024 17:17:47 +0200 Subject: [PATCH 2/5] chore: npm audit fix Signed-off-by: Gustav Grusell --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09b34c1..62643fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3538,9 +3538,9 @@ "dev": true }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dev": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -8235,9 +8235,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { "braces": "^3.0.3", From 4b8c115c57c78d5686ef7fbfe7d1d72f51da3363 Mon Sep 17 00:00:00 2001 From: Gustav Grusell Date: Fri, 30 Aug 2024 17:30:46 +0200 Subject: [PATCH 3/5] refactor: make code a little bit nicer Signed-off-by: Gustav Grusell --- src/packager.test.ts | 37 ++++++++++++++++++++++++++++++++----- src/packager.ts | 34 +++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/packager.test.ts b/src/packager.test.ts index 1c64aa6..a32cd59 100644 --- a/src/packager.test.ts +++ b/src/packager.test.ts @@ -47,8 +47,8 @@ describe('Test create shaka args', () => { it('Should use first video file as audio source if noImplicitAudio not set', async () => { const args = createShakaArgs(singleInputVideo, false); expect(args).toEqual([ - 'in=test.mp4,stream=video,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s,playlist_name=video-1.m3u8', - 'in=test.mp4,stream=audio,init_segment=audio/init.mp4,segment_template=audio/$Number$.m4s,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=defaultaudio', + 'in=test.mp4,stream=video,playlist_name=video-1.m3u8,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s', + 'in=test.mp4,stream=audio,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=defaultaudio,init_segment=audio/init.mp4,segment_template=audio/$Number$.m4s', '--hls_master_playlist_output', 'index.m3u8', '--generate_static_live_mpd', @@ -60,7 +60,7 @@ describe('Test create shaka args', () => { it('Should not use first video file as audio source if noImplicitAudio is true', async () => { const args = createShakaArgs(singleInputVideo, true); expect(args).toEqual([ - 'in=test.mp4,stream=video,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s,playlist_name=video-1.m3u8', + 'in=test.mp4,stream=video,playlist_name=video-1.m3u8,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s', '--hls_master_playlist_output', 'index.m3u8', '--generate_static_live_mpd', @@ -74,7 +74,7 @@ describe('Test create shaka args', () => { segmentDuration: 3.84 }); expect(args).toEqual([ - 'in=test.mp4,stream=video,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s,playlist_name=video-1.m3u8', + 'in=test.mp4,stream=video,playlist_name=video-1.m3u8,init_segment=video-1/init.mp4,segment_template=video-1/$Number$.m4s', '--hls_master_playlist_output', 'index.m3u8', '--generate_static_live_mpd', @@ -91,7 +91,34 @@ describe('Test create shaka args', () => { segmentSingleFileTemplate: 'Container-$KEY$.mp4' }); expect(args).toEqual([ - 'in=test.mp4,stream=video,out=Container-1.mp4,playlist_name=video-1.m3u8', + 'in=test.mp4,stream=video,playlist_name=video-1.m3u8,out=Container-1.mp4', + '--hls_master_playlist_output', + 'index.m3u8', + '--generate_static_live_mpd', + '--mpd_output', + 'manifest.mpd' + ]); + }); + + it('Should set correct output path for stream if single file segment specified with audio', async () => { + const args = createShakaArgs( + [ + ...singleInputVideo, + { + type: 'audio', + filename: 'audio.mp4', + key: '2' + } + ], + true, + { + segmentSingleFile: true, + segmentSingleFileTemplate: 'Container-$KEY$.mp4' + } + ); + expect(args).toEqual([ + 'in=test.mp4,stream=video,playlist_name=video-1.m3u8,out=Container-1.mp4', + 'in=audio.mp4,stream=audio,playlist_name=audio.m3u8,hls_group_id=audio,hls_name=defaultaudio,out=Container-2.mp4', '--hls_master_playlist_output', 'index.m3u8', '--generate_static_live_mpd', diff --git a/src/packager.ts b/src/packager.ts index a02b9c0..6c77927 100644 --- a/src/packager.ts +++ b/src/packager.ts @@ -223,21 +223,27 @@ export function createShakaArgs( if (input.type === 'video') { const playlistName = `video-${input.key}`; const playlist = `${playlistName}.m3u8`; + const streamOptions = [ + `in=${input.filename}`, + 'stream=video', + `playlist_name=${playlist}` + ]; if (packageFormatOptions?.segmentSingleFile) { const segmentName = packageFormatOptions.segmentSingleFileTemplate?.replace( '$KEY$', input.key ) || `${playlistName}.mp4`; - const args = `in=${input.filename},stream=video,out=${segmentName},playlist_name=${playlist}`; - cmdInputs.push(args); + streamOptions.push(`out=${segmentName}`); } else { const initSegment = join(playlistName, 'init.mp4'); const segmentTemplate = join(playlistName, '$Number$.m4s'); - - const args = `in=${input.filename},stream=video,init_segment=${initSegment},segment_template=${segmentTemplate},playlist_name=${playlist}`; - cmdInputs.push(args); + streamOptions.push( + `init_segment=${initSegment}`, + `segment_template=${segmentTemplate}` + ); } + cmdInputs.push(streamOptions.join(',')); } }); @@ -247,6 +253,13 @@ export function createShakaArgs( const playlistName = `audio`; const playlist = `${playlistName}.m3u8`; const fileForAudio = inputForAudio.filename; + const streamOptions = [ + `in=${fileForAudio}`, + 'stream=audio', + `playlist_name=${playlist}`, + 'hls_group_id=audio', + 'hls_name=defaultaudio' + ]; if (packageFormatOptions?.segmentSingleFile) { // Ensure non-duplicate key, to ensure unique segment file name const key = inputs.find( @@ -257,16 +270,15 @@ export function createShakaArgs( const segmentName = packageFormatOptions.segmentSingleFileTemplate?.replace('$KEY$', key) || `${playlistName}.mp4`; - - cmdInputs.push( - `in=${fileForAudio},stream=audio,out=${segmentName},playlist_name=${playlist},hls_group_id=audio,hls_name=defaultaudio` - ); + streamOptions.push(`out=${segmentName}`); } else { const segmentTemplate = 'audio/' + '$Number$.m4s'; - cmdInputs.push( - `in=${fileForAudio},stream=audio,init_segment=${playlistName}/init.mp4,segment_template=${segmentTemplate},playlist_name=${playlist},hls_group_id=audio,hls_name=defaultaudio` + streamOptions.push( + `init_segment=${playlistName}/init.mp4`, + `segment_template=${segmentTemplate}` ); } + cmdInputs.push(streamOptions.join(',')); } else { console.log('No audio input found'); } From 06e6c1dafdad7c21d74fe966c9f0d2988d850179 Mon Sep 17 00:00:00 2001 From: Gustav Grusell Date: Mon, 2 Sep 2024 09:25:00 +0200 Subject: [PATCH 4/5] chore: fix prettier warnings in README Signed-off-by: Gustav Grusell --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7d52805..7d0aac3 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,13 @@ Run script locally Usage: cli [options] Run shaka-packager with source on S3 or locally, and output to S3 or local - + Examples: - $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s s3://source-bucket/folder -d s3://output-bucket/folder + $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s s3://source-bucket/folder -d s3://output-bucket/folder $ shaka-packager-s3 -i a:1=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder $ shaka-packager-s3 -i a:2=audio.mp4 -i v:1=video.mp4 -s /path/to/source/folder -d /path/to/output/folder --segment-single-file --segment-single-file-name 'Container$KEY$.mp4' --segment-duration 3.84 - + Options: -s, --source-folder [sourceFolder] Source folder URL, ignored if input uses absolute path (supported protocols: s3, local file) From 77a1ba20b5a1ccdaae9f752e53fe7ab65da552ae Mon Sep 17 00:00:00 2001 From: Gustav Grusell Date: Mon, 2 Sep 2024 09:26:06 +0200 Subject: [PATCH 5/5] docs: fix README Signed-off-by: Gustav Grusell --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d0aac3..428f4cd 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ Options: --dash-only Package only DASH format --hls-only Package only HLS format --segment-single-file Use byte range addressing and a single segment file per stream - --segment-single-file-name [segmentSingleFileName] Template for single segment file name, $INDEX$ will be replaced with stream index + --segment-single-file-name [segmentSingleFileName] Template for single segment file name, $KEY$ will be replaced with stream key --segment-duration [segmentDuration] Segment target duration -h, --help display help for command