diff --git a/lib/config/common-media-library.api.md b/lib/config/common-media-library.api.md index 8a1c507e..fb9642da 100644 --- a/lib/config/common-media-library.api.md +++ b/lib/config/common-media-library.api.md @@ -382,6 +382,21 @@ export type CueHandler = { dispatchCue?(): void; }; +// @alpha +export type DashManifest = { + MPD: { + $?: { + maxSegmentDuration?: string; + mediaPresentationDuration?: string; + minBufferTime?: string; + profiles?: string; + type?: string; + xmlns?: string; + }; + Period: Period[]; + }; +}; + // @alpha export function dashToHam(manifest: string): Presentation[]; @@ -483,6 +498,14 @@ export function hamToDash(presentation: Presentation[]): Manifest; // @alpha export function hamToHls(presentation: Presentation[]): Manifest; +// @alpha +export type HlsManifest = { + playlists: PlayList[]; + mediaGroups: MediaGroups; + segments: SegmentHls[]; + targetDuration?: number; +}; + // @alpha export function hlsToHam(manifest: string, ancillaryManifests: string[]): Presentation[]; @@ -639,6 +662,24 @@ export type SelectionSet = Ham & { alignedSwitchingSets?: AlignedSwitchingSet[]; }; +// Warning: (ae-forgotten-export) The symbol "DashParser" needs to be exported by the entry point index.d.ts +// Warning: (ae-internal-missing-underscore) The name "setDashParser" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function setDashParser(parser: DashParser): void; + +// Warning: (ae-forgotten-export) The symbol "DashSerializer" needs to be exported by the entry point index.d.ts +// Warning: (ae-internal-missing-underscore) The name "setDashSerializer" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function setDashSerializer(serializer: DashSerializer): void; + +// Warning: (ae-forgotten-export) The symbol "HlsParser" needs to be exported by the entry point index.d.ts +// Warning: (ae-internal-missing-underscore) The name "setHlsParser" should be prefixed with an underscore because the declaration is marked as @internal +// +// @internal (undocumented) +export function setHlsParser(parser: HlsParser): void; + // @beta export type SfBareItem = string | Uint8Array | boolean | number | symbol | Date | SfToken; @@ -805,4 +846,11 @@ export type VideoTrack = Track & { scanType: string; }; +// Warnings were encountered during analysis: +// +// src/cmaf/ham/types/mapper/dash/DashManifest.ts:19:3 - (ae-forgotten-export) The symbol "Period" needs to be exported by the entry point index.d.ts +// src/cmaf/ham/types/mapper/hls/HlsManifest.ts:12:2 - (ae-forgotten-export) The symbol "PlayList" needs to be exported by the entry point index.d.ts +// src/cmaf/ham/types/mapper/hls/HlsManifest.ts:13:2 - (ae-forgotten-export) The symbol "MediaGroups" needs to be exported by the entry point index.d.ts +// src/cmaf/ham/types/mapper/hls/HlsManifest.ts:14:2 - (ae-forgotten-export) The symbol "SegmentHls" needs to be exported by the entry point index.d.ts + ``` diff --git a/lib/package.json b/lib/package.json index 6091a157..4db02d15 100644 --- a/lib/package.json +++ b/lib/package.json @@ -71,10 +71,5 @@ "build:cjs": "tsc --module commonjs --outDir dist/cjs", "test": "tsx --test ./test/**/*.test.ts", "prepublishOnly": "npm run build" - }, - "dependencies": { - "@types/xml2js": "0.4.14", - "m3u8-parser": "7.1.0", - "xml2js": "0.6.2" } } diff --git a/lib/src/cmaf-ham.ts b/lib/src/cmaf-ham.ts index 6be3867d..f3dd0772 100644 --- a/lib/src/cmaf-ham.ts +++ b/lib/src/cmaf-ham.ts @@ -18,9 +18,15 @@ export type { Track } from './cmaf/ham/types/model/Track.js'; export type { TrackType } from './cmaf/ham/types/model/TrackType.js'; export type { VideoTrack } from './cmaf/ham/types/model/VideoTrack.js'; -export type { Validation } from './cmaf/ham/types/Validation.js'; export type { Manifest } from './cmaf/ham/types/manifest/Manifest.js'; export type { ManifestFormat } from './cmaf/ham/types/manifest/ManifestFormat.js'; +export type { DashManifest } from './cmaf/ham/types/mapper/dash/DashManifest.js'; +export type { HlsManifest } from './cmaf/ham/types/mapper/hls/HlsManifest.js'; +export type { Validation } from './cmaf/ham/types/Validation.js'; + +export { setDashParser } from './cmaf/ham/utils/dash/parseDashManifest.js'; +export { setDashSerializer } from './cmaf/ham/utils/dash/serializeDashManifest.js'; +export { setHlsParser } from './cmaf/ham/utils/hls/parseHlsManifest.js'; export { dashToHam } from './cmaf/ham/services/converters/dashToHam.js'; export { hamToDash } from './cmaf/ham/services/converters/hamToDash.js'; diff --git a/lib/src/cmaf/ham/mapper/dash/DashMapper.ts b/lib/src/cmaf/ham/mapper/dash/DashMapper.ts index b54bf1d6..512556e1 100644 --- a/lib/src/cmaf/ham/mapper/dash/DashMapper.ts +++ b/lib/src/cmaf/ham/mapper/dash/DashMapper.ts @@ -5,7 +5,7 @@ import type { Presentation } from '../../types/model/Presentation.js'; import { mapDashToHam } from './mapDashToHam/mapDashToHam.js'; import { mapHamToDash } from './mapHamToDash/mapHamToDash.js'; -import { xmlToJson } from '../../utils/dash/xmlToJson.js'; +import { parseDashManifest } from '../../utils/dash/parseDashManifest.js'; import { addMetadataToDash } from '../../utils/manifest/addMetadataToDash.js'; import { getMetadata } from '../../utils/manifest/getMetadata.js'; @@ -19,7 +19,7 @@ export class DashMapper implements Mapper { } toHam(manifest: Manifest): Presentation[] { - const dashManifest: DashManifest | undefined = xmlToJson( + const dashManifest: DashManifest | undefined = parseDashManifest( manifest.manifest, ); diff --git a/lib/src/cmaf/ham/mapper/dash/mapHamToDash/mapHamToDash.ts b/lib/src/cmaf/ham/mapper/dash/mapHamToDash/mapHamToDash.ts index 0009c6c3..b60c7ff3 100644 --- a/lib/src/cmaf/ham/mapper/dash/mapHamToDash/mapHamToDash.ts +++ b/lib/src/cmaf/ham/mapper/dash/mapHamToDash/mapHamToDash.ts @@ -5,7 +5,7 @@ import type { Period } from '../../../types/mapper/dash/Period.js'; import { presentationsToPeriods } from './presentationsToPeriods.js'; -import { jsonToXml } from '../../../utils/dash/jsonToXml.js'; +import { serializeDashManifest } from '../../../utils/dash/serializeDashManifest.js'; export function mapHamToDash(hamManifests: Presentation[]): string { const periods: Period[] = presentationsToPeriods(hamManifests); @@ -20,5 +20,5 @@ export function mapHamToDash(hamManifests: Presentation[]): string { }, }; - return jsonToXml(manifest); + return serializeDashManifest(manifest); } diff --git a/lib/src/cmaf/ham/utils/dash/jsonToXml.ts b/lib/src/cmaf/ham/utils/dash/jsonToXml.ts deleted file mode 100644 index a79a11d6..00000000 --- a/lib/src/cmaf/ham/utils/dash/jsonToXml.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Builder } from 'xml2js'; - -export function jsonToXml(json: object): string { - const builder = new Builder(); - return builder.buildObject(json); -} diff --git a/lib/src/cmaf/ham/utils/dash/parseDashManifest.ts b/lib/src/cmaf/ham/utils/dash/parseDashManifest.ts new file mode 100644 index 00000000..16040ae5 --- /dev/null +++ b/lib/src/cmaf/ham/utils/dash/parseDashManifest.ts @@ -0,0 +1,30 @@ +import type { DashManifest } from '../../types/mapper/dash/DashManifest.js'; + +export type DashParser = (raw: string) => DashManifest; + +let dashParser: DashParser; + +/** + * @internal + */ +export function setDashParser(parser: DashParser): void { + dashParser = parser; +} + +/** + * @internal + */ +export function getDashParser(): DashParser { + return dashParser; +} + +/** + * @internal + * Parse XML to Json + * + * @param raw - Raw string containing the xml from the Dash Manifest + * @returns json with the Dash Manifest structure + */ +export function parseDashManifest(raw: string): DashManifest { + return dashParser(raw); +} diff --git a/lib/src/cmaf/ham/utils/dash/serializeDashManifest.ts b/lib/src/cmaf/ham/utils/dash/serializeDashManifest.ts new file mode 100644 index 00000000..212e7e34 --- /dev/null +++ b/lib/src/cmaf/ham/utils/dash/serializeDashManifest.ts @@ -0,0 +1,23 @@ +import type { DashManifest } from '../../types/mapper/dash/DashManifest'; + +export type DashSerializer = (json: DashManifest) => string; + +let xmlSerializer: DashSerializer; + +/** + * @internal + */ +export function setDashSerializer(serializer: DashSerializer): void { + xmlSerializer = serializer; +} + +/** + * @internal + */ +export function getDashSerializer(): DashSerializer { + return xmlSerializer; +} + +export function serializeDashManifest(json: DashManifest): string { + return xmlSerializer(json); +} diff --git a/lib/src/cmaf/ham/utils/dash/xmlToJson.ts b/lib/src/cmaf/ham/utils/dash/xmlToJson.ts deleted file mode 100644 index 5f000a88..00000000 --- a/lib/src/cmaf/ham/utils/dash/xmlToJson.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { parseString } from 'xml2js'; -import type { DashManifest } from '../../types/mapper/dash/DashManifest.js'; - -/** - * @internal - * Parse XML to Json - * - * @param raw - Raw string containing the xml from the Dash Manifest - * @returns json with the Dash Manifest structure - */ -export function xmlToJson(raw: string): DashManifest | undefined { - let parsed: DashManifest | undefined; - parseString(raw, (err: Error | null, result: DashManifest) => { - if (err) { - throw new Error(err.message); - } - parsed = result as DashManifest; - }); - return parsed; -} diff --git a/lib/src/cmaf/ham/utils/hls/parseHlsManifest.ts b/lib/src/cmaf/ham/utils/hls/parseHlsManifest.ts index 6f502a31..946d43c0 100644 --- a/lib/src/cmaf/ham/utils/hls/parseHlsManifest.ts +++ b/lib/src/cmaf/ham/utils/hls/parseHlsManifest.ts @@ -1,21 +1,29 @@ // @ts-ignore -import { Parser } from 'm3u8-parser'; import type { HlsManifest } from '../../types/mapper/hls/HlsManifest.js'; -export function parseHlsManifest(text: string | undefined): HlsManifest { +export type HlsParser = (text: string) => HlsManifest; + +let hlsParser: HlsParser; + +/** + * @internal + */ +export function setHlsParser(parser: HlsParser): void { + hlsParser = parser; +} + +/** + * @internal + */ +export function getHlsParser(): HlsParser { + return hlsParser; +} + +export function parseHlsManifest(text?: string): HlsManifest { if (!text) { console.error("Can't parse empty HLS Manifest"); return {} as HlsManifest; } - const parser = new Parser(); - - parser.push(text); - parser.end(); - const parsedHlsManifest = parser.manifest; - if (!parsedHlsManifest) { - throw new Error(); - } - - return parsedHlsManifest; + return hlsParser(text); } diff --git a/package-lock.json b/package-lock.json index 4b2eef24..def62254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,12 +49,7 @@ "lib": { "name": "@svta/common-media-library", "version": "0.7.3", - "license": "Apache-2.0", - "dependencies": { - "@types/xml2js": "0.4.14", - "m3u8-parser": "7.1.0", - "xml2js": "0.6.2" - } + "license": "Apache-2.0" }, "node_modules/@babel/code-frame": { "version": "7.26.0", @@ -85,6 +80,7 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1511,6 +1507,7 @@ "version": "22.8.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.8.0.tgz", "integrity": "sha512-84rafSBHC/z1i1E3p0cJwKA+CfYDNSXX9WSZBRopjIzLET8oNt6ht2tei4C7izwDeEiLLfdeSVBv1egOH916hg==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.19.8" @@ -1595,6 +1592,7 @@ "version": "0.4.14", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -1837,14 +1835,14 @@ "license": "ISC" }, "node_modules/@videojs/vhs-utils": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz", - "integrity": "sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@videojs/vhs-utils/-/vhs-utils-4.1.1.tgz", + "integrity": "sha512-5iLX6sR2ownbv4Mtejw6Ax+naosGvoT9kY+gcuHzANyUZZ+4NpeNdKMUhb6ag0acYej1Y7cmr/F2+4PrggMiVA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", - "global": "^4.4.0", - "url-toolkit": "^2.2.1" + "global": "^4.4.0" }, "engines": { "node": ">=8", @@ -2871,7 +2869,8 @@ "node_modules/dom-walk": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true }, "node_modules/eastasianwidth": { "version": "0.2.0", @@ -3916,6 +3915,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, "license": "MIT", "dependencies": { "min-document": "^2.19.0", @@ -4711,13 +4711,14 @@ "license": "MIT" }, "node_modules/m3u8-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.1.0.tgz", - "integrity": "sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-7.2.0.tgz", + "integrity": "sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==", + "dev": true, "license": "Apache-2.0", "dependencies": { "@babel/runtime": "^7.12.5", - "@videojs/vhs-utils": "^3.0.5", + "@videojs/vhs-utils": "^4.1.1", "global": "^4.4.0" } }, @@ -4947,6 +4948,7 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", + "dev": true, "dependencies": { "dom-walk": "^0.1.0" } @@ -5391,6 +5393,7 @@ "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -5502,6 +5505,7 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, "license": "MIT" }, "node_modules/regex": { @@ -5774,6 +5778,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, "license": "ISC" }, "node_modules/semver": { @@ -6481,6 +6486,7 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, "license": "MIT" }, "node_modules/unist-util-is": { @@ -6587,12 +6593,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-toolkit": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/url-toolkit/-/url-toolkit-2.2.5.tgz", - "integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==", - "license": "Apache-2.0" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -6838,6 +6838,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, "license": "MIT", "dependencies": { "sax": ">=0.6.0", @@ -6851,6 +6852,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, "license": "MIT", "engines": { "node": ">=4.0" @@ -6918,7 +6920,10 @@ }, "devDependencies": { "@types/node": "^20.11.30", - "serve": "14.2.3" + "@types/xml2js": "0.4.14", + "m3u8-parser": "7.2.0", + "serve": "14.2.3", + "xml2js": "0.6.2" } }, "samples/cmaf-ham-conversion/node_modules/@types/node": { diff --git a/samples/cmaf-ham-conversion/package.json b/samples/cmaf-ham-conversion/package.json index c040c177..42dec07b 100644 --- a/samples/cmaf-ham-conversion/package.json +++ b/samples/cmaf-ham-conversion/package.json @@ -14,6 +14,9 @@ "license": "ISC", "devDependencies": { "@types/node": "^20.11.30", - "serve": "14.2.3" + "@types/xml2js": "0.4.14", + "m3u8-parser": "7.2.0", + "serve": "14.2.3", + "xml2js": "0.6.2" } } diff --git a/samples/cmaf-ham-conversion/src/index.ts b/samples/cmaf-ham-conversion/src/index.ts index b7476661..cf2c3fde 100644 --- a/samples/cmaf-ham-conversion/src/index.ts +++ b/samples/cmaf-ham-conversion/src/index.ts @@ -1,12 +1,18 @@ -import fs from 'fs'; import { - hamToHls, + dashToHam, hamToDash, + hamToHls, hlsToHam, - dashToHam, + setDashParser, + setDashSerializer, + setHlsParser, validatePresentation, + type DashManifest, } from '@svta/common-media-library/cmaf-ham'; -import { listM3U8Files, listDirectories, listMPDFiles } from './utils.js'; +import fs from 'fs'; +import { Parser } from 'm3u8-parser'; +import { Builder, parseString } from 'xml2js'; +import { listDirectories, listM3U8Files, listMPDFiles } from './utils.js'; const FILE_ENCODING = 'utf8'; @@ -16,6 +22,35 @@ const OUTPUT_PATH_HLS = `./dist/hls/`; const SAMPLES_PATH_DASH = `./input/dash/`; const OUTPUT_PATH_DASH = `./dist/dash/`; +setHlsParser((text: string) => { + const parser = new Parser(); + + parser.push(text); + parser.end(); + const parsedHlsManifest = parser.manifest; + if (!parsedHlsManifest) { + throw new Error(); + } + + return parsedHlsManifest; +}); + +setDashParser((raw: string) => { + let parsed: DashManifest | undefined; + parseString(raw, (err: Error | null, result: DashManifest) => { + if (err) { + throw new Error(err.message); + } + parsed = result as DashManifest; + }); + return parsed; +}); + +setDashSerializer((manifest: DashManifest) => { + const builder = new Builder(); + return builder.buildObject(manifest); +}); + /** * This function converts a given manifest file (along with optional playlists for HLS) * into different formats (HLS, DASH and HAM) and saves them to a specified output directory.