From b900dec7928a593229a3edbcf0aada82a3b7a00c Mon Sep 17 00:00:00 2001 From: Nicholas Frederiksen Date: Wed, 15 Mar 2023 14:11:31 +0100 Subject: [PATCH] To support loading media and audio manifests seperatlely we needed an only master manifest loader --- index.js | 35 ++++++++++++++++++++------ package-lock.json | 14 +++++------ package.json | 4 +-- spec/integration_tests_spec.js | 45 ++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 346dff4..11f913f 100644 --- a/index.js +++ b/index.js @@ -65,6 +65,27 @@ class HLSSpliceVod { this.cmafMapUri = { video: {}, audio: {} }; } + loadMasterManifest(_injectMasterManifest, _injectMediaManifest, _injectAudioManifest) { + return new Promise((resolve, reject) => { + const parser = m3u8.createStream(); + + parser.on("m3u", (m3u) => { + this.m3u = m3u; + return resolve(); + }); + + if (!_injectMasterManifest) { + try { + request({ uri: this.masterManifestUri, gzip: true }).pipe(parser); + } catch (exc) { + reject(exc); + } + } else { + _injectMasterManifest().pipe(parser); + } + }); + } + /** * * @param {ReadStream} _injectMasterManifest @@ -86,7 +107,7 @@ class HLSSpliceVod { const streamItem = m3u.items.StreamItem[i]; const mediaManifestUrl = url.resolve(baseUrl, streamItem.get("uri")); mediaManifestPromises.push( - this._loadMediaManifest(mediaManifestUrl, streamItem.get("bandwidth"), _injectMediaManifest) + this.loadMediaManifest(mediaManifestUrl, streamItem.get("bandwidth"), _injectMediaManifest) ); } let audioItems = m3u.items.MediaItem.filter((item) => { @@ -125,7 +146,7 @@ class HLSSpliceVod { } const audioManifestUrl = url.resolve(baseUrl, audioItemUri); mediaManifestPromises.push( - this._loadAudioManifest(audioManifestUrl, audioItemGroupId, audioItemLanguage, _injectAudioManifest) + this.loadAudioManifest(audioManifestUrl, audioItemGroupId, audioItemLanguage, _injectAudioManifest) ); } Promise.all(mediaManifestPromises).then(resolve).catch(reject); @@ -492,7 +513,7 @@ class HLSSpliceVod { } } - _loadMediaManifest(mediaManifestUri, bandwidth, _injectMediaManifest) { + loadMediaManifest(mediaManifestUri, bandwidth, _injectMediaManifest) { return new Promise((resolve, reject) => { const parser = m3u8.createStream(); @@ -510,8 +531,7 @@ class HLSSpliceVod { } let map_uri = plItem.attributes.attributes["map-uri"]; if (map_uri && !map_uri.includes("http")) { - plItem.attributes.attributes["map-uri"] = - this.baseUrl + map_uri; + plItem.attributes.attributes["map-uri"] = this.baseUrl + map_uri; } } } @@ -542,7 +562,7 @@ class HLSSpliceVod { }); } - _loadAudioManifest(audioManifestUri, group, lang, _injectAudioManifest) { + loadAudioManifest(audioManifestUri, group, lang, _injectAudioManifest) { return new Promise((resolve, reject) => { const parser = m3u8.createStream(); @@ -564,8 +584,7 @@ class HLSSpliceVod { } let map_uri = plItem.attributes.attributes["map-uri"]; if (map_uri && !map_uri.includes("http")) { - plItem.attributes.attributes["map-uri"] = - this.baseUrl + map_uri; + plItem.attributes.attributes["map-uri"] = this.baseUrl + map_uri; } } } diff --git a/package-lock.json b/package-lock.json index 00dd8b5..1f8d329 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.4.3", "license": "MIT", "dependencies": { - "@eyevinn/m3u8": "^0.5.3", + "@eyevinn/m3u8": "^0.5.6", "request": "^2.88.2" }, "devDependencies": { @@ -336,9 +336,9 @@ } }, "node_modules/@eyevinn/m3u8": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@eyevinn/m3u8/-/m3u8-0.5.3.tgz", - "integrity": "sha512-FdkC95+R5X7YvJmGsmxRTOwc9mRy5BRVZOz6vVosSoK2AILWZMnox/AuTEqS3dbt1r5lnrnHbKSrNHSqtAodrg==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@eyevinn/m3u8/-/m3u8-0.5.6.tgz", + "integrity": "sha512-aYWN9Rzofs3MCuoGrJww6vExnKg8bLHN2jAmzjK8t/akwT3bzwR3i2kqH895ZysR87mikgOzF3IaBp4OYYfwWg==", "dependencies": { "chunked-stream": "~0.0.2" } @@ -2493,9 +2493,9 @@ } }, "@eyevinn/m3u8": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@eyevinn/m3u8/-/m3u8-0.5.3.tgz", - "integrity": "sha512-FdkC95+R5X7YvJmGsmxRTOwc9mRy5BRVZOz6vVosSoK2AILWZMnox/AuTEqS3dbt1r5lnrnHbKSrNHSqtAodrg==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/@eyevinn/m3u8/-/m3u8-0.5.6.tgz", + "integrity": "sha512-aYWN9Rzofs3MCuoGrJww6vExnKg8bLHN2jAmzjK8t/akwT3bzwR3i2kqH895ZysR87mikgOzF3IaBp4OYYfwWg==", "requires": { "chunked-stream": "~0.0.2" } diff --git a/package.json b/package.json index 5d258dc..5e13e66 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "repository": "https://github.com/Eyevinn/hls-splice", "main": "index.js", "scripts": { - "test": "$(npm bin)/jasmine", + "test": "jasmine", "postversion": "git push && git push --tags", "coverage": "nyc npm test && nyc report", "coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" @@ -26,7 +26,7 @@ "nyc": "^15.0.1" }, "dependencies": { - "@eyevinn/m3u8": "^0.5.3", + "@eyevinn/m3u8": "^0.5.6", "request": "^2.88.2" } } diff --git a/spec/integration_tests_spec.js b/spec/integration_tests_spec.js index 58039c8..1a274e5 100644 --- a/spec/integration_tests_spec.js +++ b/spec/integration_tests_spec.js @@ -39,4 +39,49 @@ describe("HLSSpliceVod", () => { done(); }) }); + + + it("can insert ads in an HLS VOD with seperate Manifest Loaders (Master + Media)", done => { + const hlsVod = new HLSSpliceVod('https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/.m3u8', + { + absoluteUrls: true + }); + hlsVod.loadMasterManifest().then(() => { + hlsVod.loadMediaManifest("https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-video=300000.m3u8", 455000) + }) + .then(() => { + return hlsVod.insertAdAt(0, 'https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/.m3u8'); + }) + .then(() => { + const mediaManifest = hlsVod.getMediaManifest(455000); + const lines = mediaManifest.split("\n"); + expect(lines[8]).toEqual(`#EXT-X-MAP:URI="https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/hls/output-video=300000.m4s"`); + expect(lines[12]).toEqual(`https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/hls/output-video=300000-1.m4s`); + expect(lines[23]).toEqual(`#EXT-X-MAP:URI="https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/hls/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-video=300000.m4s"`); + expect(lines[28]).toEqual(`https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/hls/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-video=300000-1.m4s`); + done(); + }) + }); + + it("can insert ads in an HLS VOD with seperate Manifest Loaders (Master + Audio)", done => { + const hlsVod = new HLSSpliceVod('https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/.m3u8', + { + absoluteUrls: true + }); + hlsVod.loadMasterManifest().then(() => { + hlsVod.loadAudioManifest("https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-audio=128000.m3u8", "audio-aacl-128", "audio") + }) + .then(() => { + return hlsVod.insertAdAt(0, 'https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/.m3u8'); + }) + .then(() => { + const audioManifest = hlsVod.getAudioManifest("audio-aacl-128", "audio"); + const lines = audioManifest.split("\n"); + expect(lines[8]).toEqual(`#EXT-X-MAP:URI="https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/hls/output-audio=128000.m4s"`); + expect(lines[12]).toEqual(`https://ovpuspvod.a2d-stage.tv/trailers/63ef9c36e3ffa90028603374/output.ism/hls/output-audio=128000-1.m4s`); + expect(lines[23]).toEqual(`#EXT-X-MAP:URI="https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/hls/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-audio=128000.m4s"`); + expect(lines[28]).toEqual(`https://vod.streaming.a2d.tv/948a9dc4-ccb6-4de0-8295-40909bc90e43/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478.ism/hls/c70657d0-5fe3-11ed-9d66-430eb269fe23_20331478-audio=128000-1.m4s`); + done(); + }) + }); });