diff --git a/src/core/main/worker/worker_main.ts b/src/core/main/worker/worker_main.ts index 8fc8458657..8d68e98bec 100644 --- a/src/core/main/worker/worker_main.ts +++ b/src/core/main/worker/worker_main.ts @@ -10,7 +10,7 @@ import type { IReferenceUpdateMessage, } from "../../../multithread_types"; import { MainThreadMessageType, WorkerMessageType } from "../../../multithread_types"; -import DashFastJsParser from "../../../parsers/manifest/dash/fast-js-parser"; +import DashJsParser from "../../../parsers/manifest/dash/js-parser"; import DashWasmParser from "../../../parsers/manifest/dash/wasm-parser"; import { ObservationPosition } from "../../../playback_observer"; import type { IWorkerPlaybackObservation } from "../../../playback_observer/worker_playback_observer"; @@ -72,7 +72,7 @@ export default function initializeWorkerMain() { // TODO allow worker-side feature-switching? Not sure how const dashWasmParser = new DashWasmParser(); features.dashParsers.wasm = dashWasmParser; - features.dashParsers.fastJs = DashFastJsParser; + features.dashParsers.js = DashJsParser; features.transports.dash = createDashPipelines; /** diff --git a/src/features/features_object.ts b/src/features/features_object.ts index 3adfdbdc04..fd4f632665 100644 --- a/src/features/features_object.ts +++ b/src/features/features_object.ts @@ -21,7 +21,7 @@ import type { IFeaturesObject } from "./types"; * @type {Object} */ const features: IFeaturesObject = { - dashParsers: { wasm: null, native: null, fastJs: null }, + dashParsers: { wasm: null, js: null }, createDebugElement: null, directfile: null, decrypt: null, diff --git a/src/features/list/__tests__/dash.test.ts b/src/features/list/__tests__/dash.test.ts index 8f996ffc72..c3896dc28e 100644 --- a/src/features/list/__tests__/dash.test.ts +++ b/src/features/list/__tests__/dash.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; import MediaSourceContentInitializer from "../../../main_thread/init/media_source_content_initializer"; -import nativeDashParser from "../../../parsers/manifest/dash/native-parser"; +import dashJsParser from "../../../parsers/manifest/dash/js-parser"; import DASHFeature from "../../../transports/dash"; import type { IFeaturesObject } from "../../types"; import addDASHFeature from "../dash"; @@ -9,13 +9,13 @@ describe("Features list - DASH", () => { it("should add DASH in the current features", () => { const featureObject = { transports: {}, - dashParsers: { fastJs: null, native: null, wasm: null }, + dashParsers: { js: null, wasm: null }, mainThreadMediaSourceInit: null, } as unknown as IFeaturesObject; addDASHFeature(featureObject); expect(featureObject).toEqual({ transports: { dash: DASHFeature }, - dashParsers: { native: nativeDashParser, fastJs: null, wasm: null }, + dashParsers: { js: dashJsParser, wasm: null }, mainThreadMediaSourceInit: MediaSourceContentInitializer, }); expect(featureObject.transports.dash).toBe(DASHFeature); diff --git a/src/features/list/__tests__/dash_wasm.test.ts b/src/features/list/__tests__/dash_wasm.test.ts index 4d88d34e53..f9a3207417 100644 --- a/src/features/list/__tests__/dash_wasm.test.ts +++ b/src/features/list/__tests__/dash_wasm.test.ts @@ -21,12 +21,11 @@ describe("Features list - DASH WASM Parser", () => { const featureObject = { transports: {}, - dashParsers: { native: null, fastJs: null, wasm: null }, + dashParsers: { js: null, wasm: null }, } as unknown as IFeaturesObject; DASH_WASM._addFeature(featureObject); expect(featureObject.transports).toEqual({ dash: DASHFeature }); - expect(featureObject.dashParsers.native).toEqual(null); - expect(featureObject.dashParsers.fastJs).toEqual(null); + expect(featureObject.dashParsers.js).toEqual(null); expect(featureObject.dashParsers.wasm).toBeInstanceOf(DashWasmParser); }); }); diff --git a/src/features/list/dash.ts b/src/features/list/dash.ts index 4f1f70b338..eb1b31d29e 100644 --- a/src/features/list/dash.ts +++ b/src/features/list/dash.ts @@ -15,7 +15,7 @@ */ import MediaSourceContentInitializer from "../../main_thread/init/media_source_content_initializer"; -import dashJsParser from "../../parsers/manifest/dash/native-parser"; +import dashJsParser from "../../parsers/manifest/dash/js-parser"; import dash from "../../transports/dash"; import type { IFeaturesObject } from "../types"; @@ -27,7 +27,7 @@ function addDASHFeature(features: IFeaturesObject): void { if (features.transports.dash === undefined) { features.transports.dash = dash; } - features.dashParsers.native = dashJsParser; + features.dashParsers.js = dashJsParser; features.mainThreadMediaSourceInit = MediaSourceContentInitializer; } diff --git a/src/features/types.ts b/src/features/types.ts index 26fcacafe1..082061e88c 100644 --- a/src/features/types.ts +++ b/src/features/types.ts @@ -61,12 +61,7 @@ export type IHTMLTextTracksBuffer = new ( */ export type INativeTextTracksBuffer = new (mediaElement: IMediaElement) => SegmentSink; -export type IDashNativeParser = ( - dom: Document, - args: IMPDParserArguments, -) => IDashParserResponse; - -export type IDashFastJsParser = ( +export type IDashJsParser = ( xml: string, args: IMPDParserArguments, ) => IDashParserResponse; @@ -142,11 +137,7 @@ export interface IFeaturesObject { /** * Entirely JavaScript-based Manifest DASH parser. */ - fastJs: IDashFastJsParser | null; - /** - * JavaScript+Browser's DOMParser-based Manifest DASH parser. - */ - native: IDashNativeParser | null; + js: IDashJsParser | null; }; /** Implement text track rendering through `` HTML elements. */ nativeTextDisplayer: typeof NativeTextDisplayer | null; diff --git a/src/main_thread/init/utils/__tests__/refresh_scheduled_events_list.test.ts b/src/main_thread/init/utils/__tests__/refresh_scheduled_events_list.test.ts index 39f02d7384..832326dce3 100644 --- a/src/main_thread/init/utils/__tests__/refresh_scheduled_events_list.test.ts +++ b/src/main_thread/init/utils/__tests__/refresh_scheduled_events_list.test.ts @@ -7,14 +7,35 @@ import { describe, it, expect, vi } from "vitest"; /* eslint-disable @typescript-eslint/no-explicit-any */ describe("init - refreshScheduledEventsList", () => { - it("should correclty refresh scheduled events", async () => { - function generateEventData() { + it("should correctly refresh scheduled events", async () => { + function generateInputEventData() { return { type: "dash-event-stream", value: { schemeIdUri: "toto", timescale: 1, - element: document.createElement("div"), + xmlData: { data: "
", namespaces: [] }, + }, + }; + } + function generateOutputEventData() { + const parsedDom = new DOMParser().parseFromString( + "
", + "application/xml", + ).documentElement; + + const element = + parsedDom.children.length > 0 + ? parsedDom.children[0] + : (parsedDom.childNodes[0] as HTMLElement); + + return { + type: "dash-event-stream", + value: { + schemeIdUri: "toto", + timescale: 1, + element, + xmlData: { data: "
", namespaces: [] }, }, }; } @@ -22,21 +43,21 @@ describe("init - refreshScheduledEventsList", () => { periods: [ { start: 0, - streamEvents: [{ start: 0, end: 1, data: generateEventData(), id: "1" }], + streamEvents: [{ start: 0, end: 1, data: generateInputEventData(), id: "1" }], }, { start: 10, streamEvents: [ - { start: 11, end: 20, data: generateEventData(), id: "2" }, - { start: 12, data: generateEventData(), id: "3" }, - { start: 13, end: 13.1, data: generateEventData(), id: "4" }, + { start: 11, end: 20, data: generateInputEventData(), id: "2" }, + { start: 12, data: generateInputEventData(), id: "3" }, + { start: 13, end: 13.1, data: generateInputEventData(), id: "4" }, ], }, ], }; const oldScheduledEvents = [ { start: 1000, end: 1000000, id: "must-disapear", _isBeingPlayed: true }, - { start: 0, end: 1, data: generateEventData(), id: "1" }, + { start: 0, end: 1, data: generateOutputEventData(), id: "1" }, ]; const refreshScheduledEventsList = ( (await vi.importActual( @@ -46,27 +67,26 @@ describe("init - refreshScheduledEventsList", () => { const scheduledEvents = refreshScheduledEventsList(oldScheduledEvents, manifest); expect(scheduledEvents).toEqual([ - { start: 0, end: 1, id: "1", data: generateEventData() }, + { start: 0, end: 1, id: "1", data: generateOutputEventData() }, { start: 11, end: 20, id: "2", - publicEvent: { start: 11, end: 20, data: generateEventData() }, - data: generateEventData(), + publicEvent: { start: 11, end: 20, data: generateOutputEventData() }, + data: generateOutputEventData(), }, { start: 12, - end: undefined, id: "3", - publicEvent: { start: 12, data: generateEventData() }, - data: generateEventData(), + publicEvent: { start: 12, data: generateOutputEventData() }, + data: generateOutputEventData(), }, { start: 13, end: 13.1, id: "4", - publicEvent: { start: 13, end: 13.1, data: generateEventData() }, - data: generateEventData(), + publicEvent: { start: 13, end: 13.1, data: generateOutputEventData() }, + data: generateOutputEventData(), }, ]); }); diff --git a/src/main_thread/init/utils/stream_events_emitter/refresh_scheduled_events_list.ts b/src/main_thread/init/utils/stream_events_emitter/refresh_scheduled_events_list.ts index b57de0d57e..4b8590cb6c 100644 --- a/src/main_thread/init/utils/stream_events_emitter/refresh_scheduled_events_list.ts +++ b/src/main_thread/init/utils/stream_events_emitter/refresh_scheduled_events_list.ts @@ -41,9 +41,7 @@ function refreshScheduledEventsList( } let element: Element; - if (data.value.element !== undefined) { - element = data.value.element; - } else if (data.value.xmlData !== undefined) { + if (data.value.xmlData !== undefined) { // First, we will create a parent Element defining all namespaces that // should have been encountered until know. // This is needed because the DOMParser API might throw when diff --git a/src/parsers/manifest/dash/common/indexes/timeline/__tests__/parse_s_element.test.ts b/src/parsers/manifest/dash/common/indexes/timeline/__tests__/parse_s_element.test.ts index 2dbccd3d73..3f0619e534 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/__tests__/parse_s_element.test.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/__tests__/parse_s_element.test.ts @@ -2,64 +2,11 @@ import { describe, it, expect, vi } from "vitest"; import log from "../../../../../../../log"; import type { ITNode } from "../../../../../../../utils/xml-parser"; import { parseXml } from "../../../../../../../utils/xml-parser"; -import { parseSHTMLElement, parseSElementNode } from "../parse_s_element"; +import { parseSElementNode } from "../parse_s_element"; function testNumberAttribute(attributeName: string, variableName?: string): void { const _variableName = variableName ?? attributeName; - it(`should correctly parse an HTML S element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element1)).toEqual({ [_variableName]: 12 }); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element2)).toEqual({ [_variableName]: 0 }); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element3)).toEqual({ [_variableName]: -50 }); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an HTML S element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element1)).toEqual({}); - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenCalledWith(`DASH: invalid ${attributeName} ("toto")`); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element2)).toEqual({}); - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenCalledWith(`DASH: invalid ${attributeName} ("PT5M")`); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - - expect(parseSHTMLElement(element3)).toEqual({}); - expect(spyLog).toHaveBeenCalledTimes(3); - expect(spyLog).toHaveBeenCalledWith(`DASH: invalid ${attributeName} ("")`); - spyLog.mockRestore(); - }); - it(`should correctly parse a node S element with a correct ${attributeName} attribute`, () => { const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); const element1 = parseXml(``)[0] as ITNode; @@ -97,43 +44,10 @@ function testNumberAttribute(attributeName: string, variableName?: string): void } describe("DASH Node Parsers - S", () => { - it("should correctly parse an HTML S element without attributes", () => { - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseSHTMLElement(element)).toEqual({}); - }); - - it("should correctly parse a Node S element without attributes", () => { - const element = parseXml("")[0] as ITNode; - expect(parseSElementNode(element)).toEqual({}); - }); - testNumberAttribute("t", "start"); testNumberAttribute("r", "repeatCount"); testNumberAttribute("d", "duration"); - it("should correctly parse an HTML S Element with every attributes", () => { - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element1)).toEqual({ - start: 0, - repeatCount: 12, - duration: 4, - }); - - const element2 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element2)).toEqual({ - start: 99, - repeatCount: 0, - duration: 4, - }); - }); - it("should correctly parse a node S Element with every attributes", () => { const element1 = parseXml('')[0] as ITNode; expect(parseSElementNode(element1)).toEqual({ @@ -150,22 +64,6 @@ describe("DASH Node Parsers - S", () => { }); }); - it("should correctly parse an HTML S Element with unknown attributes", () => { - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSHTMLElement(element1)).toEqual({ - start: 0, - repeatCount: 12, - duration: 4, - }); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSHTMLElement(element2)).toEqual({}); - }); - it("should correctly parse a node S Element with unknown attributes", () => { const element1 = parseXml('')[0] as ITNode; expect(parseSElementNode(element1)).toEqual({ diff --git a/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_elements.ts b/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_elements.ts index 9f2d905177..e260518302 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_elements.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_elements.ts @@ -18,29 +18,23 @@ import type { ITNode } from "../../../../../../utils/xml-parser"; import type { IIndexSegment } from "../../../../utils/index_helpers"; import convertElementsToIndexSegment from "./convert_element_to_index_segment"; import type { IParsedS } from "./parse_s_element"; -import { parseSElementNode, parseSHTMLElement } from "./parse_s_element"; +import { parseSElementNode } from "./parse_s_element"; /** * Allows to generate the "timeline" for the "Timeline" RepresentationIndex. * Call this function when the timeline is unknown. * This function was added to only perform that task lazily, i.e. only when * first needed. - * @param {Array.|HTMLCollection} elements - All S nodes constituting + * @param {Array.} elements - All S nodes constituting * the corresponding SegmentTimeline node. * @returns {Array.} */ export default function constructTimelineFromElements( - elements: ITNode[] | HTMLCollection, + elements: ITNode[], ): IIndexSegment[] { const initialTimeline: IParsedS[] = []; - if (Array.isArray(elements)) { - for (let i = 0; i < elements.length; i++) { - initialTimeline.push(parseSElementNode(elements[i])); - } - } else { - for (let i = 0; i < elements.length; i++) { - initialTimeline.push(parseSHTMLElement(elements[i])); - } + for (let i = 0; i < elements.length; i++) { + initialTimeline.push(parseSElementNode(elements[i])); } const timeline: IIndexSegment[] = []; for (let i = 0; i < initialTimeline.length; i++) { diff --git a/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_previous_timeline.ts b/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_previous_timeline.ts index c038b2b88a..413f1ce32d 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_previous_timeline.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/construct_timeline_from_previous_timeline.ts @@ -21,10 +21,10 @@ import constructTimelineFromElements from "./construct_timeline_from_elements"; import convertElementToIndexSegment from "./convert_element_to_index_segment"; import findFirstCommonStartTime from "./find_first_common_start_time"; import type { IParsedS } from "./parse_s_element"; -import { parseSElementNode, parseSHTMLElement } from "./parse_s_element"; +import { parseSElementNode } from "./parse_s_element"; export default function constructTimelineFromPreviousTimeline( - newElements: ITNode[] | HTMLCollection, + newElements: ITNode[], prevTimeline: IIndexSegment[], ): IIndexSegment[] { // Find first index in both timeline where a common segment is found. @@ -65,9 +65,7 @@ export default function constructTimelineFromPreviousTimeline( } const prevLastElement = newTimeline[newTimeline.length - 1]; - const newCommonElt = Array.isArray(newElements) - ? parseSElementNode(newElements[lastCommonEltNewEltsIdx]) - : parseSHTMLElement(newElements[lastCommonEltNewEltsIdx]); + const newCommonElt = parseSElementNode(newElements[lastCommonEltNewEltsIdx]); const newRepeatCountOffseted = (newCommonElt.repeatCount ?? 0) - repeatNumberInNewElements; if ( @@ -90,14 +88,8 @@ export default function constructTimelineFromPreviousTimeline( const newEltsToPush: IIndexSegment[] = []; const items: IParsedS[] = []; - if (Array.isArray(newElements)) { - for (let i = lastCommonEltNewEltsIdx + 1; i < newElements.length; i++) { - items.push(parseSElementNode(newElements[i])); - } - } else { - for (let i = lastCommonEltNewEltsIdx + 1; i < newElements.length; i++) { - items.push(parseSHTMLElement(newElements[i])); - } + for (let i = lastCommonEltNewEltsIdx + 1; i < newElements.length; i++) { + items.push(parseSElementNode(newElements[i])); } for (let i = 0; i < items.length; i++) { const item = items[i]; diff --git a/src/parsers/manifest/dash/common/indexes/timeline/find_first_common_start_time.ts b/src/parsers/manifest/dash/common/indexes/timeline/find_first_common_start_time.ts index bb793ea602..6bd7dcc3eb 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/find_first_common_start_time.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/find_first_common_start_time.ts @@ -29,7 +29,7 @@ import type { IIndexSegment } from "../../../../utils/index_helpers"; */ export default function findFirstCommonStartTime( prevTimeline: IIndexSegment[], - newElements: ITNode[] | HTMLCollection, + newElements: ITNode[], ): { /** Index in `prevSegments` where the first common segment start is found. */ prevSegmentsIdx: number; @@ -50,9 +50,7 @@ export default function findFirstCommonStartTime( return null; } const prevInitialStart = prevTimeline[0].start; - const newFirstTAttr = Array.isArray(newElements) - ? newElements[0].attributes.t - : newElements[0].getAttribute("t"); + const newFirstTAttr = newElements[0].attributes.t; const newInitialStart = isNullOrUndefined(newFirstTAttr) ? null : parseInt(newFirstTAttr, 10); @@ -104,18 +102,15 @@ export default function findFirstCommonStartTime( } } else { let newElementsIdx = 0; - let newNodeElt: ITNode | null = Array.isArray(newElements) ? newElements[0] : null; - let newDomElt: Element | null = Array.isArray(newElements) ? null : newElements[0]; + let newNodeElt: ITNode | null = newElements[0]; let currentTimeOffset = newInitialStart; while (true) { - const dAttr = - newNodeElt !== null ? newNodeElt.attributes.d : newDomElt?.getAttribute("d"); + const dAttr = newNodeElt.attributes.d; const duration = isNullOrUndefined(dAttr) ? null : parseInt(dAttr, 10); if (duration === null || Number.isNaN(duration)) { return null; } - const rAttr = - newNodeElt !== null ? newNodeElt.attributes.r : newDomElt?.getAttribute("r"); + const rAttr = newNodeElt.attributes.r; const repeatCount = isNullOrUndefined(rAttr) ? null : parseInt(rAttr, 10); if (repeatCount !== null) { @@ -142,13 +137,8 @@ export default function findFirstCommonStartTime( if (newElementsIdx >= newElements.length) { return null; } - if (Array.isArray(newElements)) { - newNodeElt = newElements[newElementsIdx]; - } else { - newDomElt = newElements[newElementsIdx]; - } - const tAttr = - newNodeElt !== null ? newNodeElt.attributes.t : newDomElt?.getAttribute("t"); + newNodeElt = newElements[newElementsIdx]; + const tAttr = newNodeElt.attributes.t; const time = isNullOrUndefined(tAttr) ? null : parseInt(tAttr, 10); if (time !== null) { if (Number.isNaN(time)) { diff --git a/src/parsers/manifest/dash/common/indexes/timeline/parse_s_element.ts b/src/parsers/manifest/dash/common/indexes/timeline/parse_s_element.ts index b163707f3a..a3f9f3ba05 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/parse_s_element.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/parse_s_element.ts @@ -75,44 +75,3 @@ export function parseSElementNode(root: ITNode): IParsedS { } return parsedS; } - -/** - * Parse a given element in the MPD under an `Element` form into a JS - * Object. - * @param {Element} root - * @returns {Object} - */ -export function parseSHTMLElement(root: Element): IParsedS { - const parsedS: IParsedS = {}; - - for (let j = 0; j < root.attributes.length; j++) { - const attribute = root.attributes[j]; - switch (attribute.name) { - case "t": - const start = parseInt(attribute.value, 10); - if (isNaN(start)) { - log.warn(`DASH: invalid t ("${attribute.value}")`); - } else { - parsedS.start = start; - } - break; - case "d": - const duration = parseInt(attribute.value, 10); - if (isNaN(duration)) { - log.warn(`DASH: invalid d ("${attribute.value}")`); - } else { - parsedS.duration = duration; - } - break; - case "r": - const repeatCount = parseInt(attribute.value, 10); - if (isNaN(repeatCount)) { - log.warn(`DASH: invalid r ("${attribute.value}")`); - } else { - parsedS.repeatCount = repeatCount; - } - break; - } - } - return parsedS; -} diff --git a/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts b/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts index 284ebaaefc..5e5b8daeee 100644 --- a/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +++ b/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts @@ -150,7 +150,7 @@ export interface ITimelineIndexIndexArgument { */ presentationTimeOffset?: number | undefined; - timelineParser?: (() => ITNode[] | HTMLCollection) | undefined; + timelineParser?: (() => ITNode[]) | undefined; timeline?: ISegmentTimelineElement[] | undefined; } @@ -248,7 +248,7 @@ export default class TimelineRepresentationIndex implements IRepresentationIndex * Lazily get the S elements from this timeline. * `null` once this call has been done once, to free memory. */ - private _parseTimeline: (() => ITNode[] | HTMLCollection) | null; + private _parseTimeline: (() => ITNode[]) | null; /** * This variable represents the same `TimelineRepresentationIndex` at the diff --git a/src/parsers/manifest/dash/common/parse_periods.ts b/src/parsers/manifest/dash/common/parse_periods.ts index ffe26f737c..51ece0b271 100644 --- a/src/parsers/manifest/dash/common/parse_periods.ts +++ b/src/parsers/manifest/dash/common/parse_periods.ts @@ -19,7 +19,6 @@ import type { IManifest } from "../../../../manifest"; import flatMap from "../../../../utils/flat_map"; import idGenerator from "../../../../utils/id_generator"; import isNullOrUndefined from "../../../../utils/is_null_or_undefined"; -import isWorker from "../../../../utils/is_worker"; import getMonotonicTimeStamp from "../../../../utils/monotonic_timestamp"; import objectValues from "../../../../utils/object_values"; import { utf8ToStr } from "../../../../utils/string_parsing"; @@ -313,23 +312,19 @@ function generateStreamEvents( let element; let xmlData; - if (!isWorker && eventIr.eventStreamData instanceof Element) { - element = eventIr.eventStreamData; - } else { - try { - xmlData = { - namespaces: allNamespaces, - data: - typeof eventIr.eventStreamData === "string" - ? eventIr.eventStreamData - : utf8ToStr(new Uint8Array(eventIr.eventStreamData as ArrayBuffer)), - }; - } catch (err) { - log.error( - "DASH: Error while parsing event-stream:", - err instanceof Error ? err.message : "Unknown error", - ); - } + try { + xmlData = { + namespaces: allNamespaces, + data: + typeof eventIr.eventStreamData === "string" + ? eventIr.eventStreamData + : utf8ToStr(new Uint8Array(eventIr.eventStreamData)), + }; + } catch (err) { + log.error( + "DASH: Error while parsing event-stream:", + err instanceof Error ? err.message : "Unknown error", + ); } res.push({ start, diff --git a/src/parsers/manifest/dash/fast-js-parser/__tests__/parse_from_xml_string.test.ts b/src/parsers/manifest/dash/js-parser/__tests__/parse_from_xml_string.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/__tests__/parse_from_xml_string.test.ts rename to src/parsers/manifest/dash/js-parser/__tests__/parse_from_xml_string.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/index.ts b/src/parsers/manifest/dash/js-parser/index.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/index.ts rename to src/parsers/manifest/dash/js-parser/index.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/AdaptationSet.ts b/src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/AdaptationSet.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/AdaptationSet.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/BaseURL.ts b/src/parsers/manifest/dash/js-parser/node_parsers/BaseURL.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/BaseURL.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/BaseURL.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/ContentComponent.ts b/src/parsers/manifest/dash/js-parser/node_parsers/ContentComponent.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/ContentComponent.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/ContentComponent.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/ContentProtection.ts b/src/parsers/manifest/dash/js-parser/node_parsers/ContentProtection.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/ContentProtection.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/ContentProtection.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/EventStream.ts b/src/parsers/manifest/dash/js-parser/node_parsers/EventStream.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/EventStream.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/EventStream.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/Initialization.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Initialization.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/Initialization.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/Initialization.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/MPD.ts b/src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/MPD.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/MPD.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/Period.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Period.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/Period.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/Period.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/Representation.ts b/src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/Representation.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/Representation.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentBase.ts b/src/parsers/manifest/dash/js-parser/node_parsers/SegmentBase.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentBase.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/SegmentBase.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentList.ts b/src/parsers/manifest/dash/js-parser/node_parsers/SegmentList.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentList.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/SegmentList.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentTemplate.ts b/src/parsers/manifest/dash/js-parser/node_parsers/SegmentTemplate.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentTemplate.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/SegmentTemplate.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentTimeline.ts b/src/parsers/manifest/dash/js-parser/node_parsers/SegmentTimeline.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentTimeline.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/SegmentTimeline.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentURL.ts b/src/parsers/manifest/dash/js-parser/node_parsers/SegmentURL.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/SegmentURL.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/SegmentURL.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/AdaptationSet.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/AdaptationSet.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/AdaptationSet.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/AdaptationSet.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/ContentComponent.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/ContentComponent.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/ContentComponent.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/ContentComponent.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/ContentProtection.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/ContentProtection.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/ContentProtection.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/ContentProtection.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/Initialization.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/Initialization.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/Initialization.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/Initialization.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/SegmentURL.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/SegmentURL.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/SegmentURL.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/SegmentURL.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/utils.test.ts b/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/utils.test.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/__tests__/utils.test.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/__tests__/utils.test.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/node_parsers/utils.ts b/src/parsers/manifest/dash/js-parser/node_parsers/utils.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/node_parsers/utils.ts rename to src/parsers/manifest/dash/js-parser/node_parsers/utils.ts diff --git a/src/parsers/manifest/dash/fast-js-parser/parse_from_xml_string.ts b/src/parsers/manifest/dash/js-parser/parse_from_xml_string.ts similarity index 100% rename from src/parsers/manifest/dash/fast-js-parser/parse_from_xml_string.ts rename to src/parsers/manifest/dash/js-parser/parse_from_xml_string.ts diff --git a/src/parsers/manifest/dash/native-parser/__tests__/parse_from_document.test.ts b/src/parsers/manifest/dash/native-parser/__tests__/parse_from_document.test.ts deleted file mode 100644 index 21196c1540..0000000000 --- a/src/parsers/manifest/dash/native-parser/__tests__/parse_from_document.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, it, expect } from "vitest"; -import type { IManifest } from "../../../../../manifest"; -import parseFromDocument from "../index"; - -describe("parseFromDocument", () => { - function setDocumentFromString(str: string): Document { - return new DOMParser().parseFromString(str, "application/xml"); - } - - it("throws root if not MPD", function () { - const doc = setDocumentFromString(""); - - expect(function () { - parseFromDocument(doc, { - url: "", - externalClockOffset: 10, - unsafelyBaseOnPreviousManifest: null, - }); - }).toThrow("document root should be MPD"); - expect(function () { - const prevManifest = {} as unknown as IManifest; - parseFromDocument(doc, { - url: "", - unsafelyBaseOnPreviousManifest: prevManifest, - }); - }).toThrow("document root should be MPD"); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/index.ts b/src/parsers/manifest/dash/native-parser/index.ts deleted file mode 100644 index 4599d5561a..0000000000 --- a/src/parsers/manifest/dash/native-parser/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import parseFromDocument from "./parse_from_document"; -export default parseFromDocument; diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/AdaptationSet.ts b/src/parsers/manifest/dash/native-parser/node_parsers/AdaptationSet.ts deleted file mode 100644 index 464bb09584..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/AdaptationSet.ts +++ /dev/null @@ -1,425 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import isNullOrUndefined from "../../../../../utils/is_null_or_undefined"; -import type { - IAdaptationSetAttributes, - IAdaptationSetChildren, - IAdaptationSetIntermediateRepresentation, -} from "../../node_parser_types"; -import parseBaseURL from "./BaseURL"; -import parseContentComponent from "./ContentComponent"; -import parseContentProtection from "./ContentProtection"; -import { createRepresentationIntermediateRepresentation } from "./Representation"; -import parseSegmentBase from "./SegmentBase"; -import parseSegmentList from "./SegmentList"; -import parseSegmentTemplate from "./SegmentTemplate"; -import { - parseBoolean, - parseIntOrBoolean, - parseMaybeDividedNumber, - parseMPDFloat, - parseMPDInteger, - parseScheme, - ValueParser, -} from "./utils"; - -/** - * Parse child nodes from an AdaptationSet. - * @param {NodeList} adaptationSetChildren - The AdaptationSet child nodes. - * @returns {Array.} - */ -function parseAdaptationSetChildren( - adaptationSetChildren: NodeList, -): [IAdaptationSetChildren, Error[]] { - const children: IAdaptationSetChildren = { - baseURLs: [], - representations: [], - }; - const contentProtections = []; - let warnings: Error[] = []; - for (let i = 0; i < adaptationSetChildren.length; i++) { - if (adaptationSetChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentElement = adaptationSetChildren[i] as Element; - - switch (currentElement.nodeName) { - case "Accessibility": - if (children.accessibilities === undefined) { - children.accessibilities = [parseScheme(currentElement)]; - } else { - children.accessibilities.push(parseScheme(currentElement)); - } - break; - - case "BaseURL": - const [baseURLObj, baseURLWarnings] = parseBaseURL(currentElement); - if (baseURLObj !== undefined) { - children.baseURLs.push(baseURLObj); - } - if (baseURLWarnings.length > 0) { - warnings = warnings.concat(baseURLWarnings); - } - break; - - case "ContentComponent": - children.contentComponent = parseContentComponent(currentElement); - break; - - case "EssentialProperty": - if (isNullOrUndefined(children.essentialProperties)) { - children.essentialProperties = [parseScheme(currentElement)]; - } else { - children.essentialProperties.push(parseScheme(currentElement)); - } - break; - - case "InbandEventStream": - if (children.inbandEventStreams === undefined) { - children.inbandEventStreams = []; - } - children.inbandEventStreams.push(parseScheme(currentElement)); - break; - - case "Label": - const label = currentElement.textContent; - - if (label !== null && label !== undefined) { - children.label = label; - } - break; - - case "Representation": - const [representation, representationWarnings] = - createRepresentationIntermediateRepresentation(currentElement); - children.representations.push(representation); - if (representationWarnings.length > 0) { - warnings = warnings.concat(representationWarnings); - } - break; - - case "Role": - if (isNullOrUndefined(children.roles)) { - children.roles = [parseScheme(currentElement)]; - } else { - children.roles.push(parseScheme(currentElement)); - } - break; - - case "SupplementalProperty": - if (isNullOrUndefined(children.supplementalProperties)) { - children.supplementalProperties = [parseScheme(currentElement)]; - } else { - children.supplementalProperties.push(parseScheme(currentElement)); - } - break; - - case "SegmentBase": - const [segmentBase, segmentBaseWarnings] = parseSegmentBase(currentElement); - children.segmentBase = segmentBase; - if (segmentBaseWarnings.length > 0) { - warnings = warnings.concat(segmentBaseWarnings); - } - break; - - case "SegmentList": - const [segmentList, segmentListWarnings] = parseSegmentList(currentElement); - children.segmentList = segmentList; - if (segmentListWarnings.length > 0) { - warnings = warnings.concat(segmentListWarnings); - } - break; - - case "SegmentTemplate": - const [segmentTemplate, segmentTemplateWarnings] = - parseSegmentTemplate(currentElement); - children.segmentTemplate = segmentTemplate; - if (segmentTemplateWarnings.length > 0) { - warnings = warnings.concat(segmentTemplateWarnings); - } - break; - - case "ContentProtection": - const [contentProtection, contentProtectionWarnings] = - parseContentProtection(currentElement); - if (contentProtectionWarnings.length > 0) { - warnings = warnings.concat(contentProtectionWarnings); - } - if (contentProtection !== undefined) { - contentProtections.push(contentProtection); - } - break; - - // case "Rating": - // children.rating = currentElement; - // break; - - // case "Viewpoint": - // children.viewpoint = currentElement; - // break; - } - } - } - if (contentProtections.length > 0) { - children.contentProtections = contentProtections; - } - return [children, warnings]; -} - -/** - * Parse every attributes from an AdaptationSet root element into a simple JS - * object. - * @param {Element} root - The AdaptationSet root element. - * @returns {Array.} - */ -function parseAdaptationSetAttributes( - root: Element, -): [IAdaptationSetAttributes, Error[]] { - const parsedAdaptation: IAdaptationSetAttributes = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(parsedAdaptation, warnings); - - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.name) { - case "id": - parsedAdaptation.id = attribute.value; - break; - - case "group": - parseValue(attribute.value, { - asKey: "group", - parser: parseMPDInteger, - dashName: "group", - }); - break; - - case "lang": - parsedAdaptation.language = attribute.value; - break; - - case "contentType": - parsedAdaptation.contentType = attribute.value; - break; - - case "par": - parsedAdaptation.par = attribute.value; - break; - - case "minBandwidth": - parseValue(attribute.value, { - asKey: "minBitrate", - parser: parseMPDInteger, - dashName: "minBandwidth", - }); - break; - - case "maxBandwidth": - parseValue(attribute.value, { - asKey: "maxBitrate", - parser: parseMPDInteger, - dashName: "maxBandwidth", - }); - break; - - case "minWidth": - parseValue(attribute.value, { - asKey: "minWidth", - parser: parseMPDInteger, - dashName: "minWidth", - }); - break; - - case "maxWidth": - parseValue(attribute.value, { - asKey: "maxWidth", - parser: parseMPDInteger, - dashName: "maxWidth", - }); - break; - - case "minHeight": - parseValue(attribute.value, { - asKey: "minHeight", - parser: parseMPDInteger, - dashName: "minHeight", - }); - break; - - case "maxHeight": - parseValue(attribute.value, { - asKey: "maxHeight", - parser: parseMPDInteger, - dashName: "maxHeight", - }); - break; - - case "minFrameRate": - parseValue(attribute.value, { - asKey: "minFrameRate", - parser: parseMaybeDividedNumber, - dashName: "minFrameRate", - }); - break; - - case "maxFrameRate": - parseValue(attribute.value, { - asKey: "maxFrameRate", - parser: parseMaybeDividedNumber, - dashName: "maxFrameRate", - }); - break; - - case "selectionPriority": - parseValue(attribute.value, { - asKey: "selectionPriority", - parser: parseMPDInteger, - dashName: "selectionPriority", - }); - break; - - case "segmentAlignment": - parseValue(attribute.value, { - asKey: "segmentAlignment", - parser: parseIntOrBoolean, - dashName: "segmentAlignment", - }); - break; - - case "subsegmentAlignment": - parseValue(attribute.value, { - asKey: "subsegmentAlignment", - parser: parseIntOrBoolean, - dashName: "subsegmentAlignment", - }); - break; - - case "bitstreamSwitching": - parseValue(attribute.value, { - asKey: "bitstreamSwitching", - parser: parseBoolean, - dashName: "bitstreamSwitching", - }); - break; - - case "audioSamplingRate": - parsedAdaptation.audioSamplingRate = attribute.value; - break; - - case "codecs": - parsedAdaptation.codecs = attribute.value; - break; - - case "scte214:supplementalCodecs": - parsedAdaptation.supplementalCodecs = attribute.value; - break; - - case "codingDependency": - parseValue(attribute.value, { - asKey: "codingDependency", - parser: parseBoolean, - dashName: "codingDependency", - }); - break; - - case "frameRate": - parseValue(attribute.value, { - asKey: "frameRate", - parser: parseMaybeDividedNumber, - dashName: "frameRate", - }); - break; - - case "height": - parseValue(attribute.value, { - asKey: "height", - parser: parseMPDInteger, - dashName: "height", - }); - break; - - case "maxPlayoutRate": - parseValue(attribute.value, { - asKey: "maxPlayoutRate", - parser: parseMPDFloat, - dashName: "maxPlayoutRate", - }); - break; - - case "maximumSAPPeriod": - parseValue(attribute.value, { - asKey: "maximumSAPPeriod", - parser: parseMPDFloat, - dashName: "maximumSAPPeriod", - }); - break; - - case "mimeType": - parsedAdaptation.mimeType = attribute.value; - break; - - case "profiles": - parsedAdaptation.profiles = attribute.value; - break; - - case "segmentProfiles": - parsedAdaptation.segmentProfiles = attribute.value; - break; - - case "width": - parseValue(attribute.value, { - asKey: "width", - parser: parseMPDInteger, - dashName: "width", - }); - break; - - case "availabilityTimeOffset": - parseValue(attribute.value, { - asKey: "availabilityTimeOffset", - parser: parseMPDFloat, - dashName: "availabilityTimeOffset", - }); - break; - - case "availabilityTimeComplete": - parseValue(attribute.value, { - asKey: "availabilityTimeComplete", - parser: parseBoolean, - dashName: "availabilityTimeComplete", - }); - break; - } - } - - return [parsedAdaptation, warnings]; -} - -/** - * Parse an AdaptationSet element into an AdaptationSet intermediate - * representation. - * @param {Element} adaptationSetElement - The AdaptationSet root element. - * @returns {Array.} - */ -export function createAdaptationSetIntermediateRepresentation( - adaptationSetElement: Element, -): [IAdaptationSetIntermediateRepresentation, Error[]] { - const childNodes = adaptationSetElement.childNodes; - const [children, childrenWarnings] = parseAdaptationSetChildren(childNodes); - const [attributes, attrsWarnings] = parseAdaptationSetAttributes(adaptationSetElement); - const warnings = childrenWarnings.concat(attrsWarnings); - return [{ children, attributes }, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/BaseURL.ts b/src/parsers/manifest/dash/native-parser/node_parsers/BaseURL.ts deleted file mode 100644 index c7eb5068f4..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/BaseURL.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { IBaseUrlIntermediateRepresentation } from "../../node_parser_types"; - -/** - * Parse an BaseURL element into an BaseURL intermediate - * representation. - * @param {Element} root - The BaseURL root element. - * @returns {Array.} - */ -export default function parseBaseURL( - root: Element, -): [IBaseUrlIntermediateRepresentation | undefined, Error[]] { - const value = root.textContent; - const warnings: Error[] = []; - if (value === null || value.length === 0) { - return [undefined, warnings]; - } - return [{ value }, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/ContentComponent.ts b/src/parsers/manifest/dash/native-parser/node_parsers/ContentComponent.ts deleted file mode 100644 index 4baa71672a..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/ContentComponent.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { IContentComponentAttributes } from "../../node_parser_types"; - -/** - * Parse a "ContentComponent" Element in a DASH MPD. - * @param {Element} root - * @returns {Object} - */ -export default function parseContentComponent( - root: Element, -): IContentComponentAttributes { - const ret: IContentComponentAttributes = {}; - - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.name) { - case "id": - ret.id = attribute.value; - break; - case "lang": - ret.language = attribute.value; - break; - case "contentType": - ret.contentType = attribute.value; - break; - case "par": - ret.par = attribute.value; - break; - } - } - - return ret; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/ContentProtection.ts b/src/parsers/manifest/dash/native-parser/node_parsers/ContentProtection.ts deleted file mode 100644 index 996706d73b..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/ContentProtection.ts +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import log from "../../../../../log"; -import { hexToBytes } from "../../../../../utils/string_parsing"; -import type { - IContentProtectionAttributes, - IContentProtectionChildren, - IContentProtectionIntermediateRepresentation, -} from "../../node_parser_types"; -import { parseBase64 } from "./utils"; - -/** - * @param {NodeList} contentProtectionChildren - * @Returns {Object} - */ -function parseContentProtectionChildren( - contentProtectionChildren: NodeList, -): [IContentProtectionChildren, Error[]] { - const warnings: Error[] = []; - const cencPssh: Uint8Array[] = []; - for (let i = 0; i < contentProtectionChildren.length; i++) { - if (contentProtectionChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentElement = contentProtectionChildren[i] as Element; - if (currentElement.nodeName === "cenc:pssh") { - const content = currentElement.textContent; - if (content !== null && content.length > 0) { - const [toUint8Array, error] = parseBase64(content, "cenc:pssh"); - if (error !== null) { - log.warn(error.message); - warnings.push(error); - } - if (toUint8Array !== null) { - cencPssh.push(toUint8Array); - } - } - } - } - } - return [{ cencPssh }, warnings]; -} - -/** - * @param {Element} root - * @returns {Object} - */ -function parseContentProtectionAttributes(root: Element): IContentProtectionAttributes { - const ret: IContentProtectionAttributes = {}; - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.name) { - case "schemeIdUri": - ret.schemeIdUri = attribute.value; - break; - case "value": - ret.value = attribute.value; - break; - case "cenc:default_KID": - ret.keyId = hexToBytes(attribute.value.replace(/-/g, "")); - break; - case "ref": - ret.ref = attribute.value; - break; - case "refId": - ret.refId = attribute.value; - break; - } - } - - return ret; -} - -/** - * @param {Element} contentProtectionElement - * @returns {Object} - */ -export default function parseContentProtection( - contentProtectionElement: Element, -): [IContentProtectionIntermediateRepresentation, Error[]] { - const [children, childrenWarnings] = parseContentProtectionChildren( - contentProtectionElement.childNodes, - ); - const attributes = parseContentProtectionAttributes(contentProtectionElement); - return [{ children, attributes }, childrenWarnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/EventStream.ts b/src/parsers/manifest/dash/native-parser/node_parsers/EventStream.ts deleted file mode 100644 index 40e8561200..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/EventStream.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - IEventStreamEventIntermediateRepresentation, - IEventStreamIntermediateRepresentation, -} from "../../node_parser_types"; -import { parseMPDInteger, ValueParser } from "./utils"; - -/** - * Parse the EventStream node to extract Event nodes and their - * content. - * @param {Element} element - * @returns {Array} - */ -export default function parseEventStream( - element: Element, -): [IEventStreamIntermediateRepresentation, Error[]] { - const eventStreamIR: IEventStreamIntermediateRepresentation = { - children: { events: [] }, - attributes: {}, - }; - let warnings: Error[] = []; - - // 1 - Parse attributes - const parseValue = ValueParser(eventStreamIR.attributes, warnings); - for (let i = 0; i < element.attributes.length; i++) { - const attr = element.attributes[i]; - switch (attr.name) { - case "schemeIdUri": - eventStreamIR.attributes.schemeIdUri = attr.value; - break; - - case "timescale": - parseValue(attr.value, { - asKey: "timescale", - parser: parseMPDInteger, - dashName: "timescale", - }); - break; - - case "value": - eventStreamIR.attributes.value = attr.value; - break; - } - } - - for (let i = 0; i < element.childNodes.length; i++) { - if (element.childNodes[i].nodeType === Node.ELEMENT_NODE) { - const currentElement = element.childNodes[i] as Element; - - switch (currentElement.nodeName) { - case "Event": - const [event, eventWarnings] = parseEvent(currentElement); - eventStreamIR.children.events.push(event); - if (eventWarnings.length > 0) { - warnings = warnings.concat(eventWarnings); - } - break; - } - } - } - - return [eventStreamIR, warnings]; -} - -/** - * Parse `Event` Element, as found in EventStream nodes. - * @param {Element} element - * @returns {Array} - */ -function parseEvent( - element: Element, -): [IEventStreamEventIntermediateRepresentation, Error[]] { - const eventIR: IEventStreamEventIntermediateRepresentation = { - eventStreamData: element, - }; - const warnings: Error[] = []; - - // 1 - Parse attributes - const parseValue = ValueParser(eventIR, warnings); - for (let i = 0; i < element.attributes.length; i++) { - const attr = element.attributes[i]; - switch (attr.name) { - case "presentationTime": - parseValue(attr.value, { - asKey: "presentationTime", - parser: parseMPDInteger, - dashName: "presentationTime", - }); - break; - case "duration": - parseValue(attr.value, { - asKey: "duration", - parser: parseMPDInteger, - dashName: "duration", - }); - break; - case "id": - eventIR.id = attr.value; - break; - } - } - return [eventIR, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/Initialization.ts b/src/parsers/manifest/dash/native-parser/node_parsers/Initialization.ts deleted file mode 100644 index a5dbd418ce..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/Initialization.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { IInitializationAttributes } from "../../node_parser_types"; -import { parseByteRange, ValueParser } from "./utils"; - -/** - * @param {Element} root - * @returns {Array.} - */ -export default function parseInitialization( - root: Element, -): [IInitializationAttributes, Error[]] { - const parsedInitialization: IInitializationAttributes = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(parsedInitialization, warnings); - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - switch (attribute.name) { - case "range": - parseValue(attribute.value, { - asKey: "range", - parser: parseByteRange, - dashName: "range", - }); - break; - - case "sourceURL": - parsedInitialization.media = attribute.value; - break; - } - } - return [parsedInitialization, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/MPD.ts b/src/parsers/manifest/dash/native-parser/node_parsers/MPD.ts deleted file mode 100644 index ed38411144..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/MPD.ts +++ /dev/null @@ -1,198 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - IBaseUrlIntermediateRepresentation, - IContentProtectionIntermediateRepresentation, - IMPDAttributes, - IMPDChildren, - IMPDIntermediateRepresentation, - IPeriodIntermediateRepresentation, - IScheme, -} from "../../node_parser_types"; -import parseBaseURL from "./BaseURL"; -import parseContentProtection from "./ContentProtection"; -import { createPeriodIntermediateRepresentation } from "./Period"; -import { parseDateTime, parseDuration, parseScheme, ValueParser } from "./utils"; - -/** - * Parse children of the MPD's root into a simple object. - * @param {NodeList} mpdChildren - * @returns {Array.} - */ -function parseMPDChildren(mpdChildren: NodeList): [IMPDChildren, Error[]] { - const baseURLs: IBaseUrlIntermediateRepresentation[] = []; - const locations: string[] = []; - const periods: IPeriodIntermediateRepresentation[] = []; - const utcTimings: IScheme[] = []; - const contentProtections: IContentProtectionIntermediateRepresentation[] = []; - - let warnings: Error[] = []; - for (let i = 0; i < mpdChildren.length; i++) { - if (mpdChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentNode = mpdChildren[i] as Element; - switch (currentNode.nodeName) { - case "BaseURL": - const [baseURLObj, baseURLWarnings] = parseBaseURL(currentNode); - if (baseURLObj !== undefined) { - baseURLs.push(baseURLObj); - } - warnings = warnings.concat(baseURLWarnings); - break; - - case "Location": - locations.push(currentNode.textContent === null ? "" : currentNode.textContent); - break; - - case "Period": - const [period, periodWarnings] = - createPeriodIntermediateRepresentation(currentNode); - periods.push(period); - warnings = warnings.concat(periodWarnings); - break; - - case "UTCTiming": - const utcTiming = parseScheme(currentNode); - utcTimings.push(utcTiming); - break; - - case "ContentProtection": - const [contentProtection, contentProtectionWarnings] = - parseContentProtection(currentNode); - if (contentProtectionWarnings.length > 0) { - warnings = warnings.concat(contentProtectionWarnings); - } - if (contentProtection !== undefined) { - contentProtections.push(contentProtection); - } - break; - } - } - } - - return [{ baseURLs, locations, periods, utcTimings, contentProtections }, warnings]; -} - -/** - * @param {Element} root - * @returns {Array.} - */ -function parseMPDAttributes(root: Element): [IMPDAttributes, Error[]] { - const res: IMPDAttributes = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(res, warnings); - - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.name) { - case "id": - res.id = attribute.value; - break; - case "profiles": - res.profiles = attribute.value; - break; - case "type": - res.type = attribute.value; - break; - - case "availabilityStartTime": - parseValue(attribute.value, { - asKey: "availabilityStartTime", - parser: parseDateTime, - dashName: "availabilityStartTime", - }); - break; - case "availabilityEndTime": - parseValue(attribute.value, { - asKey: "availabilityEndTime", - parser: parseDateTime, - dashName: "availabilityEndTime", - }); - break; - case "publishTime": - parseValue(attribute.value, { - asKey: "publishTime", - parser: parseDateTime, - dashName: "publishTime", - }); - break; - case "mediaPresentationDuration": - parseValue(attribute.value, { - asKey: "duration", - parser: parseDuration, - dashName: "mediaPresentationDuration", - }); - break; - case "minimumUpdatePeriod": - parseValue(attribute.value, { - asKey: "minimumUpdatePeriod", - parser: parseDuration, - dashName: "minimumUpdatePeriod", - }); - break; - case "minBufferTime": - parseValue(attribute.value, { - asKey: "minBufferTime", - parser: parseDuration, - dashName: "minBufferTime", - }); - break; - case "timeShiftBufferDepth": - parseValue(attribute.value, { - asKey: "timeShiftBufferDepth", - parser: parseDuration, - dashName: "timeShiftBufferDepth", - }); - break; - case "suggestedPresentationDelay": - parseValue(attribute.value, { - asKey: "suggestedPresentationDelay", - parser: parseDuration, - dashName: "suggestedPresentationDelay", - }); - break; - case "maxSegmentDuration": - parseValue(attribute.value, { - asKey: "maxSegmentDuration", - parser: parseDuration, - dashName: "maxSegmentDuration", - }); - break; - case "maxSubsegmentDuration": - parseValue(attribute.value, { - asKey: "maxSubsegmentDuration", - parser: parseDuration, - dashName: "maxSubsegmentDuration", - }); - break; - } - } - return [res, warnings]; -} - -/** - * @param {Element} root - * @returns {Array.} - */ -export function createMPDIntermediateRepresentation( - root: Element, -): [IMPDIntermediateRepresentation, Error[]] { - const [children, childrenWarnings] = parseMPDChildren(root.childNodes); - const [attributes, attrsWarnings] = parseMPDAttributes(root); - const warnings = childrenWarnings.concat(attrsWarnings); - return [{ children, attributes }, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/Period.ts b/src/parsers/manifest/dash/native-parser/node_parsers/Period.ts deleted file mode 100644 index 824b1b08ec..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/Period.ts +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { - IAdaptationSetIntermediateRepresentation, - IBaseUrlIntermediateRepresentation, - IContentProtectionIntermediateRepresentation, - IPeriodAttributes, - IPeriodChildren, - IPeriodIntermediateRepresentation, - ISegmentTemplateIntermediateRepresentation, -} from "../../node_parser_types"; -import { createAdaptationSetIntermediateRepresentation } from "./AdaptationSet"; -import parseBaseURL from "./BaseURL"; -import parseContentProtection from "./ContentProtection"; -import parseEventStream from "./EventStream"; -import parseSegmentTemplate from "./SegmentTemplate"; -import { parseBoolean, parseDuration, ValueParser } from "./utils"; - -/** - * @param {NodeList} periodChildren - * @returns {Array} - */ -function parsePeriodChildren(periodChildren: NodeList): [IPeriodChildren, Error[]] { - const baseURLs: IBaseUrlIntermediateRepresentation[] = []; - const adaptations: IAdaptationSetIntermediateRepresentation[] = []; - let segmentTemplate: ISegmentTemplateIntermediateRepresentation | undefined; - const contentProtections: IContentProtectionIntermediateRepresentation[] = []; - - let warnings: Error[] = []; - const eventStreams = []; - for (let i = 0; i < periodChildren.length; i++) { - if (periodChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentElement = periodChildren[i] as Element; - - switch (currentElement.nodeName) { - case "BaseURL": - const [baseURLObj, baseURLWarnings] = parseBaseURL(currentElement); - if (baseURLObj !== undefined) { - baseURLs.push(baseURLObj); - } - warnings = warnings.concat(baseURLWarnings); - break; - - case "AdaptationSet": - const [adaptation, adaptationWarnings] = - createAdaptationSetIntermediateRepresentation(currentElement); - adaptations.push(adaptation); - warnings = warnings.concat(adaptationWarnings); - break; - - case "EventStream": - const [eventStream, eventStreamWarnings] = parseEventStream(currentElement); - eventStreams.push(eventStream); - warnings = warnings.concat(eventStreamWarnings); - break; - - case "SegmentTemplate": - const [parsedSegmentTemplate, segmentTemplateWarnings] = - parseSegmentTemplate(currentElement); - segmentTemplate = parsedSegmentTemplate; - if (segmentTemplateWarnings.length > 0) { - warnings = warnings.concat(segmentTemplateWarnings); - } - break; - - case "ContentProtection": - const [contentProtection, contentProtectionWarnings] = - parseContentProtection(currentElement); - if (contentProtectionWarnings.length > 0) { - warnings = warnings.concat(contentProtectionWarnings); - } - if (contentProtection !== undefined) { - contentProtections.push(contentProtection); - } - break; - } - } - } - - return [ - { baseURLs, adaptations, eventStreams, segmentTemplate, contentProtections }, - warnings, - ]; -} - -/** - * @param {Element} periodElement - * @returns {Array} - */ -function parsePeriodAttributes(periodElement: Element): [IPeriodAttributes, Error[]] { - const res: IPeriodAttributes = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(res, warnings); - for (let i = 0; i < periodElement.attributes.length; i++) { - const attr = periodElement.attributes[i]; - - switch (attr.name) { - case "id": - res.id = attr.value; - break; - - case "start": - parseValue(attr.value, { - asKey: "start", - parser: parseDuration, - dashName: "start", - }); - break; - - case "duration": - parseValue(attr.value, { - asKey: "duration", - parser: parseDuration, - dashName: "duration", - }); - break; - - case "bitstreamSwitching": - parseValue(attr.value, { - asKey: "bitstreamSwitching", - parser: parseBoolean, - dashName: "bitstreamSwitching", - }); - break; - - case "xlink:href": - res.xlinkHref = attr.value; - break; - - case "xlink:actuate": - res.xlinkActuate = attr.value; - break; - } - } - return [res, warnings]; -} - -/** - * @param {Element} periodElement - * @returns {Array} - */ -export function createPeriodIntermediateRepresentation( - periodElement: Element, -): [IPeriodIntermediateRepresentation, Error[]] { - const [children, childrenWarnings] = parsePeriodChildren(periodElement.childNodes); - const [attributes, attrsWarnings] = parsePeriodAttributes(periodElement); - const warnings = childrenWarnings.concat(attrsWarnings); - return [{ children, attributes }, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/Representation.ts b/src/parsers/manifest/dash/native-parser/node_parsers/Representation.ts deleted file mode 100644 index b6dc532068..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/Representation.ts +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import isNullOrUndefined from "../../../../../utils/is_null_or_undefined"; -import type { - IRepresentationAttributes, - IRepresentationChildren, - IRepresentationIntermediateRepresentation, -} from "../../node_parser_types"; -import parseBaseURL from "./BaseURL"; -import parseContentProtection from "./ContentProtection"; -import parseSegmentBase from "./SegmentBase"; -import parseSegmentList from "./SegmentList"; -import parseSegmentTemplate from "./SegmentTemplate"; -import { - MPDError, - parseBoolean, - parseMaybeDividedNumber, - parseMPDFloat, - parseMPDInteger, - parseScheme, - ValueParser, -} from "./utils"; - -/** - * @param {NodeList} representationChildren - * @returns {Object} - */ -function parseRepresentationChildren( - representationChildren: NodeList, -): [IRepresentationChildren, Error[]] { - const children: IRepresentationChildren = { - baseURLs: [], - }; - const contentProtections = []; - - let warnings: Error[] = []; - for (let i = 0; i < representationChildren.length; i++) { - if (representationChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentElement = representationChildren[i] as Element; - - switch (currentElement.nodeName) { - case "BaseURL": - const [baseURLObj, baseURLWarnings] = parseBaseURL(currentElement); - if (baseURLObj !== undefined) { - children.baseURLs.push(baseURLObj); - } - warnings = warnings.concat(baseURLWarnings); - break; - case "InbandEventStream": - if (children.inbandEventStreams === undefined) { - children.inbandEventStreams = []; - } - children.inbandEventStreams.push(parseScheme(currentElement)); - break; - case "SegmentBase": - const [segmentBase, segmentBaseWarnings] = parseSegmentBase(currentElement); - children.segmentBase = segmentBase; - if (segmentBaseWarnings.length > 0) { - warnings = warnings.concat(segmentBaseWarnings); - } - break; - case "SegmentList": - const [segmentList, segmentListWarnings] = parseSegmentList(currentElement); - warnings = warnings.concat(segmentListWarnings); - children.segmentList = segmentList; - break; - case "SegmentTemplate": - const [segmentTemplate, segmentTemplateWarnings] = - parseSegmentTemplate(currentElement); - warnings = warnings.concat(segmentTemplateWarnings); - children.segmentTemplate = segmentTemplate; - break; - - case "ContentProtection": - const [contentProtection, contentProtectionWarnings] = - parseContentProtection(currentElement); - if (contentProtectionWarnings.length > 0) { - warnings = warnings.concat(contentProtectionWarnings); - } - if (contentProtection !== undefined) { - contentProtections.push(contentProtection); - } - break; - case "SupplementalProperty": - if (isNullOrUndefined(children.supplementalProperties)) { - children.supplementalProperties = [parseScheme(currentElement)]; - } else { - children.supplementalProperties.push(parseScheme(currentElement)); - } - break; - } - } - } - if (contentProtections.length > 0) { - children.contentProtections = contentProtections; - } - return [children, warnings]; -} - -/** - * @param {Element} representationElement - * @returns {Array} - */ -function parseRepresentationAttributes( - representationElement: Element, -): [IRepresentationAttributes, Error[]] { - const attributes: IRepresentationAttributes = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(attributes, warnings); - for (let i = 0; i < representationElement.attributes.length; i++) { - const attr = representationElement.attributes[i]; - - switch (attr.name) { - case "audioSamplingRate": - attributes.audioSamplingRate = attr.value; - break; - - case "bandwidth": - parseValue(attr.value, { - asKey: "bitrate", - parser: parseMPDInteger, - dashName: "bandwidth", - }); - break; - - case "codecs": - attributes.codecs = attr.value; - break; - - case "codingDependency": - parseValue(attr.value, { - asKey: "codingDependency", - parser: parseBoolean, - dashName: "codingDependency", - }); - break; - - case "frameRate": - parseValue(attr.value, { - asKey: "frameRate", - parser: parseMaybeDividedNumber, - dashName: "frameRate", - }); - break; - - case "height": - parseValue(attr.value, { - asKey: "height", - parser: parseMPDInteger, - dashName: "height", - }); - break; - - case "id": - attributes.id = attr.value; - break; - - case "maxPlayoutRate": - parseValue(attr.value, { - asKey: "maxPlayoutRate", - parser: parseMPDFloat, - dashName: "maxPlayoutRate", - }); - break; - - case "maximumSAPPeriod": - parseValue(attr.value, { - asKey: "maximumSAPPeriod", - parser: parseMPDFloat, - dashName: "maximumSAPPeriod", - }); - break; - - case "mimeType": - attributes.mimeType = attr.value; - break; - - case "profiles": - attributes.profiles = attr.value; - break; - - case "qualityRanking": - parseValue(attr.value, { - asKey: "qualityRanking", - parser: parseMPDInteger, - dashName: "qualityRanking", - }); - break; - - case "scte214:supplementalCodecs": - attributes.supplementalCodecs = attr.value; - break; - - case "segmentProfiles": - attributes.segmentProfiles = attr.value; - break; - - case "width": - parseValue(attr.value, { - asKey: "width", - parser: parseMPDInteger, - dashName: "width", - }); - break; - - case "availabilityTimeOffset": - parseValue(attr.value, { - asKey: "availabilityTimeOffset", - parser: parseMPDFloat, - dashName: "availabilityTimeOffset", - }); - break; - - case "availabilityTimeComplete": - parseValue(attr.value, { - asKey: "availabilityTimeComplete", - parser: parseBoolean, - dashName: "availabilityTimeComplete", - }); - break; - } - } - if (attributes.bitrate === undefined) { - warnings.push(new MPDError("No bitrate found on a Representation")); - } - return [attributes, warnings]; -} - -/** - * @param {Element} representationElement - * @returns {Array} - */ -export function createRepresentationIntermediateRepresentation( - representationElement: Element, -): [IRepresentationIntermediateRepresentation, Error[]] { - const [children, childrenWarnings] = parseRepresentationChildren( - representationElement.childNodes, - ); - const [attributes, attrsWarnings] = - parseRepresentationAttributes(representationElement); - const warnings = childrenWarnings.concat(attrsWarnings); - return [{ children, attributes }, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentBase.ts b/src/parsers/manifest/dash/native-parser/node_parsers/SegmentBase.ts deleted file mode 100644 index ce9fd102bd..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentBase.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ISegmentBaseIntermediateRepresentation } from "../../node_parser_types"; -import parseInitialization from "./Initialization"; -import { - parseBoolean, - parseByteRange, - parseMPDFloat, - parseMPDInteger, - ValueParser, -} from "./utils"; - -/** - * Parse a SegmentBase element into a SegmentBase intermediate representation. - * @param {Element} root - The SegmentBase root element. - * @returns {Array} - */ -export default function parseSegmentBase( - root: Element, -): [ISegmentBaseIntermediateRepresentation, Error[]] { - const attributes: ISegmentBaseIntermediateRepresentation = {}; - - let warnings: Error[] = []; - const parseValue = ValueParser(attributes, warnings); - const segmentBaseChildren = root.childNodes; - for (let i = 0; i < segmentBaseChildren.length; i++) { - if (segmentBaseChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentNode = segmentBaseChildren[i] as Element; - if (currentNode.nodeName === "Initialization") { - const [initialization, initializationWarnings] = parseInitialization(currentNode); - attributes.initialization = initialization; - warnings = warnings.concat(initializationWarnings); - } - } - } - - for (let i = 0; i < root.attributes.length; i++) { - const attr = root.attributes[i]; - switch (attr.name) { - case "timescale": - parseValue(attr.value, { - asKey: "timescale", - parser: parseMPDInteger, - dashName: "timescale", - }); - break; - - case "presentationTimeOffset": - parseValue(attr.value, { - asKey: "presentationTimeOffset", - parser: parseMPDFloat, - dashName: "presentationTimeOffset", - }); - break; - - case "indexRange": - parseValue(attr.value, { - asKey: "indexRange", - parser: parseByteRange, - dashName: "indexRange", - }); - break; - - case "indexRangeExact": - parseValue(attr.value, { - asKey: "indexRangeExact", - parser: parseBoolean, - dashName: "indexRangeExact", - }); - break; - - case "availabilityTimeOffset": - parseValue(attr.value, { - asKey: "availabilityTimeOffset", - parser: parseMPDFloat, - dashName: "availabilityTimeOffset", - }); - break; - - case "availabilityTimeComplete": - parseValue(attr.value, { - asKey: "availabilityTimeComplete", - parser: parseBoolean, - dashName: "availabilityTimeComplete", - }); - break; - - case "duration": - parseValue(attr.value, { - asKey: "duration", - parser: parseMPDInteger, - dashName: "duration", - }); - break; - - case "startNumber": - parseValue(attr.value, { - asKey: "startNumber", - parser: parseMPDInteger, - dashName: "startNumber", - }); - break; - - case "endNumber": - parseValue(attr.value, { - asKey: "endNumber", - parser: parseMPDInteger, - dashName: "endNumber", - }); - break; - } - } - - return [attributes, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentList.ts b/src/parsers/manifest/dash/native-parser/node_parsers/SegmentList.ts deleted file mode 100644 index 25d821ff0a..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentList.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import objectAssign from "../../../../../utils/object_assign"; -import type { - ISegmentListIntermediateRepresentation, - ISegmentUrlIntermediateRepresentation, -} from "../../node_parser_types"; -import parseSegmentBase from "./SegmentBase"; -import parseSegmentURL from "./SegmentURL"; - -/** - * @param {Element} root - * @returns {Array} - */ -export default function parseSegmentList( - root: Element, -): [ISegmentListIntermediateRepresentation, Error[]] { - const [base, baseWarnings] = parseSegmentBase(root); - let warnings: Error[] = baseWarnings; - - const list: ISegmentUrlIntermediateRepresentation[] = []; - - const segmentListChildren = root.childNodes; - for (let i = 0; i < segmentListChildren.length; i++) { - if (segmentListChildren[i].nodeType === Node.ELEMENT_NODE) { - const currentNode = segmentListChildren[i] as Element; - if (currentNode.nodeName === "SegmentURL") { - const [segmentURL, segmentURLWarnings] = parseSegmentURL(currentNode); - list.push(segmentURL); - warnings = warnings.concat(segmentURLWarnings); - } - } - } - const ret = objectAssign(base, { list }); - return [ret, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTemplate.ts b/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTemplate.ts deleted file mode 100644 index bce7570d7c..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTemplate.ts +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import isNullOrUndefined from "../../../../../utils/is_null_or_undefined"; -import objectAssign from "../../../../../utils/object_assign"; -import type { - ISegmentTemplateIntermediateRepresentation, - ITimelineParser, -} from "../../node_parser_types"; -import parseSegmentBase from "./SegmentBase"; -import createSegmentTimelineParser from "./SegmentTimeline"; -import { parseBoolean, parseMPDFloat, ValueParser } from "./utils"; - -/** - * Parse a SegmentTemplate element into a SegmentTemplate intermediate - * representation. - * @param {Element} root - The SegmentTemplate root element. - * @returns {Array} - */ -export default function parseSegmentTemplate( - root: Element, -): [ISegmentTemplateIntermediateRepresentation, Error[]] { - const [base, segmentBaseWarnings] = parseSegmentBase(root); - const warnings: Error[] = segmentBaseWarnings; - - let timelineParser: ITimelineParser | undefined; - - // First look for a possible SegmentTimeline - for (let i = 0; i < root.childNodes.length; i++) { - if (root.childNodes[i].nodeType === Node.ELEMENT_NODE) { - const currentNode = root.childNodes[i] as Element; - if (currentNode.nodeName === "SegmentTimeline") { - timelineParser = createSegmentTimelineParser(currentNode); - } - } - } - - const ret: ISegmentTemplateIntermediateRepresentation = objectAssign({}, base, { - duration: base.duration, - timelineParser, - }); - - const parseValue = ValueParser(ret, warnings); - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.nodeName) { - case "initialization": - if (isNullOrUndefined(ret.initialization)) { - ret.initialization = { media: attribute.value }; - } - break; - - case "index": - ret.index = attribute.value; - break; - - case "availabilityTimeOffset": - parseValue(attribute.value, { - asKey: "availabilityTimeOffset", - parser: parseMPDFloat, - dashName: "availabilityTimeOffset", - }); - break; - - case "availabilityTimeComplete": - parseValue(attribute.value, { - asKey: "availabilityTimeComplete", - parser: parseBoolean, - dashName: "availabilityTimeComplete", - }); - break; - - case "media": - ret.media = attribute.value; - break; - - case "bitstreamSwitching": - parseValue(attribute.value, { - asKey: "bitstreamSwitching", - parser: parseBoolean, - dashName: "bitstreamSwitching", - }); - break; - } - } - - return [ret, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTimeline.ts b/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTimeline.ts deleted file mode 100644 index 8aabea2b4d..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentTimeline.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ITimelineParser } from "../../node_parser_types"; - -/** - * @param {Element} root - * @returns {Function} - */ -export default function createSegmentTimelineParser(root: Element): ITimelineParser { - let result: HTMLCollection | null = null; - return function (): HTMLCollection { - if (result === null) { - const elements = root.getElementsByTagName("S"); - result = elements; - return elements; - } - return result; - }; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentURL.ts b/src/parsers/manifest/dash/native-parser/node_parsers/SegmentURL.ts deleted file mode 100644 index fe0e8a9a53..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/SegmentURL.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { ISegmentUrlIntermediateRepresentation } from "../../node_parser_types"; -import { parseByteRange, ValueParser } from "./utils"; - -/** - * Parse a SegmentURL element into a SegmentURL intermediate - * representation. - * @param {Element} root - The SegmentURL root element. - * @returns {Array} - */ -export default function parseSegmentURL( - root: Element, -): [ISegmentUrlIntermediateRepresentation, Error[]] { - const parsedSegmentURL: ISegmentUrlIntermediateRepresentation = {}; - const warnings: Error[] = []; - const parseValue = ValueParser(parsedSegmentURL, warnings); - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - switch (attribute.name) { - case "media": - parsedSegmentURL.media = attribute.value; - break; - - case "indexRange": - parseValue(attribute.value, { - asKey: "indexRange", - parser: parseByteRange, - dashName: "indexRange", - }); - break; - - case "index": - parsedSegmentURL.index = attribute.value; - break; - - case "mediaRange": - parseValue(attribute.value, { - asKey: "mediaRange", - parser: parseByteRange, - dashName: "mediaRange", - }); - break; - } - } - return [parsedSegmentURL, warnings]; -} diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/AdaptationSet.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/AdaptationSet.test.ts deleted file mode 100644 index 0ae4b69295..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/AdaptationSet.test.ts +++ /dev/null @@ -1,617 +0,0 @@ -import { describe, it, expect, vi } from "vitest"; -import log from "../../../../../../log"; -import { createAdaptationSetIntermediateRepresentation } from "../AdaptationSet"; -import { MPDError } from "../utils"; - -function testBooleanAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: true }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: false }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error1 = new MPDError( - `\`${attributeName}\` property is not a boolean value but "foobar"`, - ); - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: false }, - children: { baseURLs: [], representations: [] }, - }, - [error1], - ]); - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenNthCalledWith(1, error1.message); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error2 = new MPDError( - `\`${attributeName}\` property is not a boolean value but ""`, - ); - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: false }, - children: { baseURLs: [], representations: [] }, - }, - [error2], - ]); - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenNthCalledWith(2, error2.message); - spyLog.mockRestore(); - }); -} - -function testStringAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: "foobar" }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: "" }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); -} - -function testMaybeDividedNumber(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: 12.4 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: 0 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { - attributes: { [_variableName]: 13.5 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error1 = new MPDError(`\`${attributeName}\` property is invalid: "toto"`); - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error1], - ]); - - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenNthCalledWith(1, error1.message); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error2 = new MPDError(`\`${attributeName}\` property is invalid: "PT5M"`); - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error2], - ]); - - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenNthCalledWith(2, error2.message); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error3 = new MPDError(`\`${attributeName}\` property is invalid: ""`); - - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error3], - ]); - - expect(spyLog).toHaveBeenCalledTimes(3); - expect(spyLog).toHaveBeenNthCalledWith(3, error3.message); - spyLog.mockRestore(); - }); -} - -function testFloatAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: 12 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: 0 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { - attributes: { [_variableName]: -50.12 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error1 = new MPDError(`\`${attributeName}\` property is invalid: "toto"`); - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error1], - ]); - - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenNthCalledWith(1, error1.message); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error2 = new MPDError(`\`${attributeName}\` property is invalid: "PT5M"`); - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error2], - ]); - - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenNthCalledWith(2, error2.message); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error3 = new MPDError(`\`${attributeName}\` property is invalid: ""`); - - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error3], - ]); - - expect(spyLog).toHaveBeenCalledTimes(3); - expect(spyLog).toHaveBeenNthCalledWith(3, error3.message); - spyLog.mockRestore(); - }); -} - -function testIntegerAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: 12 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: 0 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { - attributes: { [_variableName]: -50 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error1 = new MPDError( - `\`${attributeName}\` property is not an integer value but "toto"`, - ); - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error1], - ]); - - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenNthCalledWith(1, error1.message); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error2 = new MPDError( - `\`${attributeName}\` property is not an integer value but "PT5M"`, - ); - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error2], - ]); - - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenNthCalledWith(2, error2.message); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error3 = new MPDError( - `\`${attributeName}\` property is not an integer value but ""`, - ); - - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error3], - ]); - - expect(spyLog).toHaveBeenCalledTimes(3); - expect(spyLog).toHaveBeenNthCalledWith(3, error3.message); - spyLog.mockRestore(); - }); -} - -function testNumberOrBooleanAttribute( - attributeName: string, - variableName?: string, -): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse an AdaptationSet element with a correct ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: 12 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: 0 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { - attributes: { [_variableName]: -50 }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with an incorrect ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error1 = new MPDError( - `\`${attributeName}\` property is not a boolean nor an integer but "toto"`, - ); - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error1], - ]); - - expect(spyLog).toHaveBeenCalledTimes(1); - expect(spyLog).toHaveBeenNthCalledWith(1, error1.message); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error2 = new MPDError( - `\`${attributeName}\` property is not a boolean nor an integer but "PT5M"`, - ); - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error2], - ]); - - expect(spyLog).toHaveBeenCalledTimes(2); - expect(spyLog).toHaveBeenNthCalledWith(2, error2.message); - - const element3 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - const error3 = new MPDError( - `\`${attributeName}\` property is not a boolean nor an integer but ""`, - ); - - expect(createAdaptationSetIntermediateRepresentation(element3)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [error3], - ]); - - expect(spyLog).toHaveBeenCalledTimes(3); - expect(spyLog).toHaveBeenNthCalledWith(3, error3.message); - spyLog.mockRestore(); - }); - - it(`should correctly parse an AdaptationSet element with a boolean ${attributeName} attribute`, () => { - const spyLog = vi.spyOn(log, "warn").mockImplementation(vi.fn()); - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: { [_variableName]: true }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: { [_variableName]: false }, - children: { baseURLs: [], representations: [] }, - }, - [], - ]); - - expect(spyLog).not.toHaveBeenCalled(); - spyLog.mockRestore(); - }); -} - -describe("DASH Node Parsers - AdaptationSet", () => { - it("should correctly parse an AdaptationSet element without attributes nor children", () => { - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [], - ]); - }); - - testStringAttribute("audioSamplingRate"); - testBooleanAttribute("bitstreamSwitching"); - testStringAttribute("codecs"); - testBooleanAttribute("codingDependency"); - testStringAttribute("contentType"); - testMaybeDividedNumber("frameRate"); - testIntegerAttribute("group"); - testIntegerAttribute("height"); - testStringAttribute("id"); - testStringAttribute("lang", "language"); - testIntegerAttribute("maxBandwidth", "maxBitrate"); - testMaybeDividedNumber("maxFrameRate"); - testIntegerAttribute("maxHeight"); - testFloatAttribute("maxPlayoutRate"); - testIntegerAttribute("maxWidth"); - testFloatAttribute("maximumSAPPeriod"); - testStringAttribute("mimeType"); - testIntegerAttribute("minBandwidth", "minBitrate"); - testMaybeDividedNumber("minFrameRate"); - testIntegerAttribute("minHeight"); - testIntegerAttribute("minWidth"); - testStringAttribute("par"); - testStringAttribute("profiles"); - testNumberOrBooleanAttribute("segmentAlignment"); - testStringAttribute("segmentProfiles"); - testNumberOrBooleanAttribute("subsegmentAlignment"); - testIntegerAttribute("width"); - testFloatAttribute("availabilityTimeOffset"); - - it("should correctly parse an empty baseURLs", () => { - const element1 = new DOMParser().parseFromString( - "", - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - "", - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { attributes: {}, children: { baseURLs: [], representations: [] } }, - [], - ]); - }); - - it("should correctly parse a non-empty baseURLs", () => { - const element1 = new DOMParser().parseFromString( - 'a', - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: {}, - children: { baseURLs: [{ value: "a" }], representations: [] }, - }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - 'foo bar', - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element2)).toEqual([ - { - attributes: {}, - children: { baseURLs: [{ value: "foo bar" }], representations: [] }, - }, - [], - ]); - }); - - it("should correctly parse multiple non-empty baseURLs", () => { - const element1 = new DOMParser().parseFromString( - 'ab', - "text/xml", - ).childNodes[0] as Element; - expect(createAdaptationSetIntermediateRepresentation(element1)).toEqual([ - { - attributes: {}, - children: { - baseURLs: [{ value: "a" }, { value: "b" }], - representations: [], - }, - }, - [], - ]); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentComponent.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentComponent.test.ts deleted file mode 100644 index 4b5b6a58b5..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentComponent.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { describe, it, expect } from "vitest"; -import parseContentComponent from "../ContentComponent"; - -function testStringAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse a contentComponent element with a correct ${attributeName} attribute`, () => { - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseContentComponent(element1)).toEqual({ - [_variableName]: "foobar", - }); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseContentComponent(element2)).toEqual({ [_variableName]: "" }); - }); -} - -describe("DASH Node Parsers - ContentComponent", () => { - it("should correctly parse a ContentComponent element without attributes", () => { - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseContentComponent(element)).toEqual({}); - }); - testStringAttribute("id"); - testStringAttribute("lang", "language"); - testStringAttribute("contentType"); - testStringAttribute("par"); - - it("should correctly parse a contentComponent with every attributes", () => { - const element = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseContentComponent(element)).toEqual({ - id: "foo", - language: "bar", - contentType: "audio/mp5", - par: "3/4", - }); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentProtection.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentProtection.test.ts deleted file mode 100644 index aa171818cc..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/ContentProtection.test.ts +++ /dev/null @@ -1,246 +0,0 @@ -import { describe, beforeEach, it, expect, vi } from "vitest"; - -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -function testStringAttribute(attributeName: string, variableName?: string): void { - const _variableName = variableName ?? attributeName; - - it(`should correctly parse a ContentProtection element with a correct ${attributeName} attribute`, async () => { - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element1 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseContentProtection(element1)).toEqual([ - { attributes: { [_variableName]: "foobar" }, children: { cencPssh: [] } }, - [], - ]); - - const element2 = new DOMParser().parseFromString( - ``, - "text/xml", - ).childNodes[0] as Element; - expect(parseContentProtection(element2)).toEqual([ - { attributes: { [_variableName]: "" }, children: { cencPssh: [] } }, - [], - ]); - }); -} - -describe("DASH Node Parsers - ContentProtection", () => { - beforeEach(() => { - vi.resetModules(); - }); - - it("should correctly parse a ContentProtection element without attributes", async () => { - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseContentProtection(element)).toEqual([ - { attributes: {}, children: { cencPssh: [] } }, - [], - ]); - }); - - testStringAttribute("schemeIdUri"); - testStringAttribute("value"); - - it("should correctly parse a ContentProtection element with a correct cenc:default_KID attribute", async () => { - const keyId = new Uint8Array([0, 1, 2, 3]); - const mockHexToBytes = vi.fn().mockImplementation(() => { - return keyId; - }); - vi.doMock("../../../../../../utils/string_parsing", () => ({ - hexToBytes: mockHexToBytes, - })); - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element1 = new DOMParser() - .parseFromString( - ` - - - -`, - "text/xml", - ) - .getElementsByTagName("ContentProtection")[0]; - - expect(parseContentProtection(element1)).toEqual([ - { attributes: { keyId }, children: { cencPssh: [] } }, - [], - ]); - expect(mockHexToBytes).toHaveBeenCalledTimes(1); - expect(mockHexToBytes).toHaveBeenCalledWith("deadbeef"); - }); - - it("should correctly parse a ContentProtection with every attributes", async () => { - const keyId = new Uint8Array([0, 1, 2, 3]); - const mockHexToBytes = vi.fn().mockImplementation(() => { - return keyId; - }); - vi.doMock("../../../../../../utils/string_parsing", () => ({ - hexToBytes: mockHexToBytes, - })); - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element = new DOMParser() - .parseFromString( - ` - - - -`, - "text/xml", - ) - .getElementsByTagName("ContentProtection")[0]; - expect(parseContentProtection(element)).toEqual([ - { - attributes: { keyId, schemeIdUri: "foo", value: "bar" }, - children: { cencPssh: [] }, - }, - [], - ]); - }); - - it("should correctly parse a ContentProtection with cenc:pssh children", async () => { - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element = new DOMParser() - .parseFromString( - ` - - - AABBCC - AAABAC - - -`, - "text/xml", - ) - .getElementsByTagName("ContentProtection")[0]; - expect(parseContentProtection(element)).toEqual([ - { - attributes: {}, - children: { - cencPssh: [new Uint8Array([0, 0, 65, 8]), new Uint8Array([0, 0, 1, 0])], - }, - }, - [], - ]); - }); - - it("should correctly parse a ContentProtection with both cenc:pssh children and every attributes", async () => { - const keyId = new Uint8Array([0, 1, 2, 3]); - const mockHexToBytes = vi.fn().mockImplementation(() => { - return keyId; - }); - vi.doMock("../../../../../../utils/string_parsing", () => ({ - hexToBytes: mockHexToBytes, - })); - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element = new DOMParser() - .parseFromString( - ` - - - AABBCC - AAABAC - - -`, - "text/xml", - ) - .getElementsByTagName("ContentProtection")[0]; - expect(parseContentProtection(element)).toEqual([ - { - attributes: { keyId, schemeIdUri: "foo", value: "bar" }, - children: { - cencPssh: [new Uint8Array([0, 0, 65, 8]), new Uint8Array([0, 0, 1, 0])], - }, - }, - [], - ]); - }); - - it("should return a warning if one of the cenc:pssh is invalid base64", async () => { - const parseContentProtection = ( - (await vi.importActual("../ContentProtection")) as any - ).default; - const element = new DOMParser() - .parseFromString( - ` - - - AA!BCC - AAABAC - - -`, - "text/xml", - ) - .getElementsByTagName("ContentProtection")[0]; - const parsed = parseContentProtection(element); - expect(parsed[0]).toEqual({ - attributes: {}, - children: { cencPssh: [new Uint8Array([0, 0, 1, 0])] }, - }); - expect(parsed[1]).not.toBe(null); - expect(parsed[1]).toHaveLength(1); - expect(parsed[1][0]).toBeInstanceOf(Error); - expect(parsed[1][0].message).toEqual( - '`cenc:pssh` is not a valid base64 string: "AA!BCC"', - ); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/Initialization.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/Initialization.test.ts deleted file mode 100644 index 5706afd9c9..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/Initialization.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { describe, beforeEach, it, expect, vi } from "vitest"; - -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -describe("DASH Node Parsers - Initialization", () => { - beforeEach(() => { - vi.resetModules(); - }); - - it("should correctly parse an element with no known attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseInitialization = ((await vi.importActual("../Initialization")) as any) - .default; - const element1 = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseInitialization(element1)).toEqual([{}, []]); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseInitialization(element2)).toEqual([{}, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a well-formed `range` attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseInitialization = ((await vi.importActual("../Initialization")) as any) - .default; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseInitialization(element1)).toEqual([{ range: [0, 1] }, []]); - - const element2 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseInitialization(element2)).toEqual([{ range: [100, 1000] }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with an incorrect `range` attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn").mockImplementation(vi.fn()); - - const parseInitialization = ((await vi.importActual("../Initialization")) as any) - .default; - const MPDError = ((await vi.importActual("../utils")) as any).MPDError; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error1 = new MPDError('`range` property has an unrecognized format "a"'); - expect(parseInitialization(element1)).toEqual([{}, [error1]]); - - expect(mockLog).toHaveBeenCalledTimes(1); - expect(mockLog).toHaveBeenCalledWith(error1.message); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error2 = new MPDError('`range` property has an unrecognized format ""'); - expect(parseInitialization(element2)).toEqual([{}, [error2]]); - - expect(mockLog).toHaveBeenCalledTimes(2); - expect(mockLog).toHaveBeenCalledWith(error2.message); - - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a sourceURL attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseInitialization = ((await vi.importActual("../Initialization")) as any) - .default; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseInitialization(element1)).toEqual([{ media: "a" }, []]); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseInitialization(element2)).toEqual([{ media: "" }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with both a sourceURL and range attributes", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseInitialization = ((await vi.importActual("../Initialization")) as any) - .default; - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseInitialization(element1)).toEqual([{ media: "a", range: [4, 10] }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentTimeline.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentTimeline.test.ts deleted file mode 100644 index cc74845f1f..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentTimeline.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { describe, beforeEach, it, expect, vi } from "vitest"; - -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -describe("DASH Node parsers - SegmentTimeline", () => { - beforeEach(() => { - vi.resetModules(); - }); - - it("should return a function to parse lazily the timeline", async () => { - const parseSegmentTimeline = ((await vi.importActual("../SegmentTimeline")) as any) - .default; - - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - const mockGetElementsByTagName = vi.spyOn(element, "getElementsByTagName"); - const timeline = parseSegmentTimeline(element); - expect(typeof timeline).toEqual("function"); - expect(timeline.length).toEqual(0); - expect(mockGetElementsByTagName).not.toHaveBeenCalled(); - mockGetElementsByTagName.mockReset(); - }); - - it("should return an empty HTMLCollection if no S element is present", async () => { - const parseSegmentTimeline = ((await vi.importActual("../SegmentTimeline")) as any) - .default; - - const element = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - const mockGetElementsByTagName = vi.spyOn(element, "getElementsByTagName"); - - const timeline = parseSegmentTimeline(element); - const res = timeline(); - expect(res).toBeInstanceOf(HTMLCollection); - expect(res).toHaveLength(0); - expect(mockGetElementsByTagName).toHaveBeenCalledTimes(1); - expect(mockGetElementsByTagName).toHaveBeenCalledWith("S"); - mockGetElementsByTagName.mockReset(); - }); - - it("should return an empty HTMLCollection for an Invalid XML", async () => { - const parseSegmentTimeline = ((await vi.importActual("../SegmentTimeline")) as any) - .default; - - const element = new DOMParser().parseFromString( - "", - "text/xml", - ).childNodes[0] as Element; - const mockGetElementsByTagName = vi.spyOn(element, "getElementsByTagName"); - - const timeline = parseSegmentTimeline(element); - const res = timeline(); - expect(res).toBeInstanceOf(HTMLCollection); - expect(res).toHaveLength(0); - expect(mockGetElementsByTagName).toHaveBeenCalledTimes(1); - expect(mockGetElementsByTagName).toHaveBeenCalledWith("S"); - mockGetElementsByTagName.mockReset(); - }); - - it("should parse S elements only when called for the first time", async () => { - const parseSegmentTimeline = ((await vi.importActual("../SegmentTimeline")) as any) - .default; - - const sElement1 = new DOMParser().parseFromString("1", "text/xml") - .childNodes[0] as Element; - const sElement2 = new DOMParser().parseFromString("2", "text/xml") - .childNodes[0] as Element; - const aElement = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - const oElement = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - const element = new DOMParser().parseFromString( - ' & ]]>', - "text/xml", - ).childNodes[0] as Element; - - element.appendChild(sElement1); - element.appendChild(aElement); - element.appendChild(oElement); - element.appendChild(sElement2); - const mockGetElementsByTagName = vi.spyOn(element, "getElementsByTagName"); - - const timeline = parseSegmentTimeline(element); - const res1 = timeline(); - expect(res1).toBeInstanceOf(HTMLCollection); - expect(res1).toHaveLength(2); - expect(mockGetElementsByTagName).toHaveBeenCalledTimes(1); - expect(mockGetElementsByTagName).toHaveBeenCalledWith("S"); - mockGetElementsByTagName.mockClear(); - const res2 = timeline(); - const res3 = timeline(); - expect(res2).toBe(res1); - expect(res2).toBeInstanceOf(HTMLCollection); - expect(res2).toHaveLength(2); - expect(res3).toBe(res1); - expect(res3).toBeInstanceOf(HTMLCollection); - expect(res3).toHaveLength(2); - expect(mockGetElementsByTagName).not.toHaveBeenCalled(); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentURL.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentURL.test.ts deleted file mode 100644 index 70be4e0b42..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/SegmentURL.test.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { describe, beforeEach, it, expect, vi } from "vitest"; - -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-explicit-any */ - -describe("DASH Node Parsers - SegmentURL", () => { - beforeEach(() => { - vi.resetModules(); - }); - - it("should correctly parse an element with no known attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const element1 = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element1)).toEqual([{}, []]); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element2)).toEqual([{}, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a well-formed `mediaRange` attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSegmentURL(element1)).toEqual([{ mediaRange: [10, 100] }, []]); - - const element2 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSegmentURL(element2)).toEqual([{ mediaRange: [0, 1] }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with an incorrect `mediaRange` attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn").mockImplementation(vi.fn()); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const MPDError = ((await vi.importActual("../utils")) as any).MPDError; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error1 = new MPDError('`mediaRange` property has an unrecognized format "a"'); - expect(parseSegmentURL(element1)).toEqual([{}, [error1]]); - - expect(mockLog).toHaveBeenCalledTimes(1); - expect(mockLog).toHaveBeenCalledWith(error1.message); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error2 = new MPDError('`mediaRange` property has an unrecognized format ""'); - expect(parseSegmentURL(element2)).toEqual([{}, [error2]]); - - expect(mockLog).toHaveBeenCalledTimes(2); - expect(mockLog).toHaveBeenCalledWith(error2.message); - - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a well-formed `indexRange` attribute", async () => { - const log = { - default: { warn: () => null }, - }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSegmentURL(element1)).toEqual([{ indexRange: [0, 100] }, []]); - - const element2 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseSegmentURL(element2)).toEqual([{ indexRange: [72, 47] }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with an incorrect `indexRange` attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn").mockImplementation(vi.fn()); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const MPDError = ((await vi.importActual("../utils")) as any).MPDError; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error1 = new MPDError('`indexRange` property has an unrecognized format "a"'); - expect(parseSegmentURL(element1)).toEqual([{}, [error1]]); - - expect(mockLog).toHaveBeenCalledTimes(1); - expect(mockLog).toHaveBeenCalledWith(error1.message); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - const error2 = new MPDError('`indexRange` property has an unrecognized format ""'); - expect(parseSegmentURL(element2)).toEqual([{}, [error2]]); - - expect(mockLog).toHaveBeenCalledTimes(2); - expect(mockLog).toHaveBeenCalledWith(error2.message); - - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a media attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element1)).toEqual([{ media: "a" }, []]); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element2)).toEqual([{ media: "" }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); - - it("should correctly parse an element with a index attribute", async () => { - const log = { default: { warn: () => null } }; - vi.doMock("../../../../../../log", () => log); - const mockLog = vi.spyOn(log.default, "warn"); - - const parseSegmentURL = ((await vi.importActual("../SegmentURL")) as any).default; - const element1 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element1)).toEqual([{ index: "a" }, []]); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseSegmentURL(element2)).toEqual([{ index: "" }, []]); - - expect(mockLog).not.toHaveBeenCalled(); - mockLog.mockRestore(); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/utils.test.ts b/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/utils.test.ts deleted file mode 100644 index 4146efaaa1..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/__tests__/utils.test.ts +++ /dev/null @@ -1,201 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - MPDError, - parseBoolean, - parseByteRange, - parseDateTime, - parseDuration, - parseIntOrBoolean, - parseScheme, -} from "../utils"; - -describe("dash parser helpers", function () { - describe("parseBoolean", () => { - it('should return true if value is "true"', () => { - expect(parseBoolean("true", "toto")).toEqual([true, null]); - }); - - it('should return false if value is "false"', () => { - expect(parseBoolean("false", "titi")).toEqual([false, null]); - }); - - it("should return false for and an error any other value", () => { - const parsed1 = parseBoolean("", "ab"); - const parsed2 = parseBoolean("foo", "ba"); - expect(parsed1[0]).toEqual(false); - expect(parsed2[0]).toEqual(false); - expect(parsed1[1]).toBeInstanceOf(MPDError); - expect(parsed2[1]).toBeInstanceOf(MPDError); - expect(parsed1[1]?.message).toEqual('`ab` property is not a boolean value but ""'); - expect(parsed2[1]?.message).toEqual( - '`ba` property is not a boolean value but "foo"', - ); - }); - }); - - describe("parseIntOrBoolean", () => { - it('should return true if value is "true"', () => { - expect(parseIntOrBoolean("true", "toto")).toEqual([true, null]); - }); - - it('should return false if value is "false"', () => { - expect(parseIntOrBoolean("false", "toto")).toEqual([false, null]); - }); - - it("should return a number for any number", () => { - expect(parseIntOrBoolean("0", "foob1")).toEqual([0, null]); - expect(parseIntOrBoolean("10", "foob2")).toEqual([10, null]); - expect(parseIntOrBoolean("072", "foob3")).toEqual([72, null]); - expect(parseIntOrBoolean("-698", "foob4")).toEqual([-698, null]); - }); - - it("should return null and an error for any other value", () => { - const parsed1 = parseIntOrBoolean("", "ab"); - const parsed2 = parseIntOrBoolean("foo", "ba"); - expect(parsed1[0]).toEqual(null); - expect(parsed2[0]).toEqual(null); - expect(parsed1[1]).toBeInstanceOf(MPDError); - expect(parsed2[1]).toBeInstanceOf(MPDError); - expect(parsed1[1]?.message).toEqual( - '`ab` property is not a boolean nor an integer but ""', - ); - expect(parsed2[1]?.message).toEqual( - '`ba` property is not a boolean nor an integer but "foo"', - ); - }); - }); - - describe("parseDateTime", () => { - it("should correctly parse a given date into a timestamp", () => { - expect(parseDateTime("1970-01-01T00:00:00Z", "a")).toEqual([0, null]); - expect(parseDateTime("1998-11-22T10:40:50Z", "b")).toEqual([911731250, null]); - expect(parseDateTime("1960-01-01T00:00:00Z", "c")).toEqual([-315619200, null]); - }); - - it("should return null and an error when the date is not recognized", () => { - const parsed1 = parseDateTime("foo bar", "ab"); - const parsed2 = parseDateTime("2047-41-52T30:40:50Z", "ba"); - expect(parsed1[0]).toEqual(null); - expect(parsed2[0]).toEqual(null); - expect(parsed1[1]).toBeInstanceOf(MPDError); - expect(parsed2[1]).toBeInstanceOf(MPDError); - expect(parsed1[1]?.message).toEqual('`ab` is in an invalid date format: "foo bar"'); - expect(parsed2[1]?.message).toEqual( - '`ba` is in an invalid date format: "2047-41-52T30:40:50Z"', - ); - }); - }); - - describe("parseDuration", () => { - it("should correctly parse duration in ISO8061 format", function () { - expect(parseDuration("P18Y9M4DT11H9M8S", "fooba")).toEqual([591361748, null]); - }); - - it("should correctly parse duration if missing the year", function () { - expect(parseDuration("P9M4DT11H9M8S", "fooba")).toEqual([23713748, null]); - }); - - it("should correctly parse duration if missing the month", function () { - expect(parseDuration("P18Y4DT11H9M8S", "fooba")).toEqual([568033748, null]); - }); - - it("should correctly parse duration if missing the day", function () { - expect(parseDuration("P18Y9MT11H9M8S", "fooba")).toEqual([591016148, null]); - }); - - it("should correctly parse duration if missing the hours", function () { - expect(parseDuration("P18Y9M4DT9M8S", "fooba")).toEqual([591322148, null]); - }); - - it("should correctly parse duration if missing the minutes", function () { - expect(parseDuration("P18Y9M4DT11H8S", "fooba")).toEqual([591361208, null]); - }); - - it("should correctly parse duration if missing the seconds", function () { - expect(parseDuration("P18Y9M4DT11H9M", "fooba")).toEqual([591361740, null]); - }); - - it("should return null and an error if duration not in ISO8061 format", function () { - const parsed1 = parseDuration("1000", "fooba"); - expect(parsed1[0]).toEqual(null); - expect(parsed1[1]).toBeInstanceOf(MPDError); - expect(parsed1[1]?.message).toEqual( - '`fooba` property has an unrecognized format "1000"', - ); - }); - it("should return 0 and an error if given an empty string", function () { - const parsed = parseDuration("", "fooba"); - expect(parsed[0]).toEqual(0); - expect(parsed[1]).toBeInstanceOf(MPDError); - expect(parsed[1]?.message).toEqual("`fooba` property is empty"); - }); - }); - - describe("parseByteRange", () => { - it("should correctly parse byte range", function () { - const parsedByteRange = parseByteRange("1-1000", "tots"); - expect(parsedByteRange[0]).not.toEqual(null); - expect(parsedByteRange[1]).toEqual(null); - expect((parsedByteRange[0] as [number, number]).length).toEqual(2); - expect((parsedByteRange[0] as [number, number])[0]).toEqual(1); - expect((parsedByteRange[0] as [number, number])[1]).toEqual(1000); - }); - it("should return null and an error if can't parse given byte range", function () { - const parsed1 = parseByteRange("main", "prop"); - expect(parsed1[0]).toEqual(null); - expect(parsed1[1]).toBeInstanceOf(MPDError); - expect(parsed1[1]?.message).toEqual( - '`prop` property has an unrecognized format "main"', - ); - }); - }); - - describe("parseScheme", () => { - it("should correctly parse an element with no known attribute", () => { - const element1 = new DOMParser().parseFromString("", "text/xml") - .childNodes[0] as Element; - expect(parseScheme(element1)).toEqual({}); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseScheme(element2)).toEqual({}); - }); - - it("should correctly parse an element with a correct schemeIdUri attribute", () => { - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseScheme(element1)).toEqual({ schemeIdUri: "foobar " }); - - const element2 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseScheme(element2)).toEqual({ schemeIdUri: "" }); - }); - - it("should correctly parse an element with a correct value attribute", () => { - const element1 = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseScheme(element1)).toEqual({ value: "foobar " }); - - const element2 = new DOMParser().parseFromString('', "text/xml") - .childNodes[0] as Element; - expect(parseScheme(element2)).toEqual({ value: "" }); - }); - - it("should correctly parse an element with both attributes", () => { - const element = new DOMParser().parseFromString( - '', - "text/xml", - ).childNodes[0] as Element; - expect(parseScheme(element)).toEqual({ - schemeIdUri: "baz", - value: "foobar ", - }); - }); - }); -}); diff --git a/src/parsers/manifest/dash/native-parser/node_parsers/utils.ts b/src/parsers/manifest/dash/native-parser/node_parsers/utils.ts deleted file mode 100644 index d44375b120..0000000000 --- a/src/parsers/manifest/dash/native-parser/node_parsers/utils.ts +++ /dev/null @@ -1,385 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// XML-Schema -// - -import log from "../../../../../log"; -import { base64ToBytes } from "../../../../../utils/base64"; -import isNonEmptyString from "../../../../../utils/is_non_empty_string"; -import type { IScheme } from "../../node_parser_types"; - -const iso8601Duration = - /^P(([\d.]*)Y)?(([\d.]*)M)?(([\d.]*)D)?T?(([\d.]*)H)?(([\d.]*)M)?(([\d.]*)S)?/; -const rangeRe = /([0-9]+)-([0-9]+)/; - -/** - * Parse MPD boolean attributes. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed boolean - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseBoolean(val: string, displayName: string): [boolean, MPDError | null] { - if (val === "true") { - return [true, null]; - } - if (val === "false") { - return [false, null]; - } - const error = new MPDError( - `\`${displayName}\` property is not a boolean value but "${val}"`, - ); - return [false, error]; -} - -/** - * Parse MPD integer attributes. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed boolean - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseMPDInteger( - val: string, - displayName: string, -): [number | null, MPDError | null] { - const toInt = parseInt(val, 10); - if (isNaN(toInt)) { - const error = new MPDError( - `\`${displayName}\` property is not an integer value but "${val}"`, - ); - return [null, error]; - } - return [toInt, null]; -} - -/** - * Parse MPD float attributes. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed boolean - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseMPDFloat( - val: string, - displayName: string, -): [number | null, MPDError | null] { - if (val === "INF") { - return [Infinity, null]; - } - const toInt = parseFloat(val); - if (isNaN(toInt)) { - const error = new MPDError(`\`${displayName}\` property is invalid: "${val}"`); - return [null, error]; - } - return [toInt, null]; -} - -/** - * Parse MPD attributes which are either integer or boolean values. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed value - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseIntOrBoolean( - val: string, - displayName: string, -): [boolean | number | null, MPDError | null] { - if (val === "true") { - return [true, null]; - } - if (val === "false") { - return [false, null]; - } - const toInt = parseInt(val, 10); - if (isNaN(toInt)) { - const error = new MPDError( - `\`${displayName}\` property is not a boolean nor an integer but "${val}"`, - ); - return [null, error]; - } - return [toInt, null]; -} - -/** - * Parse MPD date attributes. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed value - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseDateTime( - val: string, - displayName: string, -): [number | null, MPDError | null] { - const parsed = Date.parse(val); - if (isNaN(parsed)) { - const error = new MPDError( - `\`${displayName}\` is in an invalid date format: "${val}"`, - ); - return [null, error]; - } - return [new Date(Date.parse(val)).getTime() / 1000, null]; -} - -/** - * Parse MPD ISO8601 duration attributes into seconds. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed value - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - The value to parse - * @param {string} displayName - The name of the property. Used for error - * formatting. - * @returns {Array.} - */ -function parseDuration( - val: string, - displayName: string, -): [number | null, MPDError | null] { - if (!isNonEmptyString(val)) { - const error = new MPDError(`\`${displayName}\` property is empty`); - return [0, error]; - } - - const match = iso8601Duration.exec(val) as RegExpExecArray; - if (match === null) { - const error = new MPDError( - `\`${displayName}\` property has an unrecognized format "${val}"`, - ); - return [null, error]; - } - - const duration = - parseFloat(isNonEmptyString(match[2]) ? match[2] : "0") * 365 * 24 * 60 * 60 + - parseFloat(isNonEmptyString(match[4]) ? match[4] : "0") * 30 * 24 * 60 * 60 + - parseFloat(isNonEmptyString(match[6]) ? match[6] : "0") * 24 * 60 * 60 + - parseFloat(isNonEmptyString(match[8]) ? match[8] : "0") * 60 * 60 + - parseFloat(isNonEmptyString(match[10]) ? match[10] : "0") * 60 + - parseFloat(isNonEmptyString(match[12]) ? match[12] : "0"); - return [duration, null]; -} - -/** - * Parse MPD byterange attributes into arrays of two elements: the start and - * the end. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed value - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - * @param {string} displayName - * @returns {Array. | Error | null>} - */ -function parseByteRange( - val: string, - displayName: string, -): [[number, number] | null, MPDError | null] { - const match = rangeRe.exec(val); - if (match === null) { - const error = new MPDError( - `\`${displayName}\` property has an unrecognized format "${val}"`, - ); - return [null, error]; - } else { - return [[+match[1], +match[2]], null]; - } -} - -/** - * Parse MPD base64 attribute into an Uint8Array. - * the end. - * - * The returned value is a tuple of two elements where: - * 1. the first value is the parsed value - or `null` if we could not parse - * it - * 2. the second value is a possible error encountered while parsing this - * value - set to `null` if no error was encountered. - * @param {string} val - * @param {string} displayName - * @returns {Uint8Array | Error | null>} - */ -function parseBase64( - val: string, - displayName: string, -): [Uint8Array | null, MPDError | null] { - try { - return [base64ToBytes(val), null]; - } catch (_) { - const error = new MPDError( - `\`${displayName}\` is not a valid base64 string: "${val}"`, - ); - return [null, error]; - } -} - -/** - * Some values in the MPD can be expressed as divisions of integers (e.g. frame - * rates). - * This function tries to convert it to a floating point value. - * @param {string} val - * @param {string} displayName - * @returns {Array.} - */ -function parseMaybeDividedNumber( - val: string, - displayName: string, -): [number | null, MPDError | null] { - const matches = /^(\d+)\/(\d+)$/.exec(val); - if (matches !== null) { - // No need to check, we know both are numbers - return [+matches[1] / +matches[2], null]; - } - return parseMPDFloat(val, displayName); -} - -/** - * @param {Element} root - * @returns {Object} - */ -function parseScheme(root: Element): IScheme { - let schemeIdUri: string | undefined; - let value: string | undefined; - for (let i = 0; i < root.attributes.length; i++) { - const attribute = root.attributes[i]; - - switch (attribute.name) { - case "schemeIdUri": - schemeIdUri = attribute.value; - break; - case "value": - value = attribute.value; - break; - } - } - - return { schemeIdUri, value }; -} - -/** - * Create a function to factorize the MPD parsing logic. - * @param {Object} dest - The destination object which will contain the parsed - * values. - * @param {Array.} warnings - An array which will contain every parsing - * error encountered. - * @return {Function} - */ -function ValueParser(dest: T, warnings: Error[]) { - /** - * Parse a single value and add it to the `dest` objects. - * If an error arised while parsing, add it at the end of the `warnings` array. - * @param {string} objKey - The key which will be added to the `dest` object. - * @param {string} val - The value found in the MPD which we should parse. - * @param {Function} parsingFn - The parsing function adapted for this value. - * @param {string} displayName - The name of the key as it appears in the MPD. - * This is used only in error formatting, - */ - return function ( - val: string, - { - asKey, - parser, - dashName, - }: { - asKey: keyof T; - parser: ( - value: string, - displayName: string, - ) => [T[keyof T] | null, MPDError | null]; - dashName: string; - }, - ): void { - const [parsingResult, parsingError] = parser(val, dashName); - if (parsingError !== null) { - log.warn(parsingError.message); - warnings.push(parsingError); - } - - if (parsingResult !== null) { - dest[asKey] = parsingResult; - } - }; -} - -/** - * Error arising when parsing the MPD. - * @class MPDError - * @extends Error - */ -class MPDError extends Error { - public readonly name: "MPDError"; - /** - * @param {string} message - */ - constructor(message: string) { - super(message); - // @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class - Object.setPrototypeOf(this, MPDError.prototype); - - this.name = "MPDError"; - } -} - -export { - MPDError, - ValueParser, - parseBase64, - parseBoolean, - parseByteRange, - parseDateTime, - parseDuration, - parseIntOrBoolean, - parseMaybeDividedNumber, - parseMPDFloat, - parseMPDInteger, - parseScheme, -}; diff --git a/src/parsers/manifest/dash/native-parser/parse_from_document.ts b/src/parsers/manifest/dash/native-parser/parse_from_document.ts deleted file mode 100644 index ed67c41c7d..0000000000 --- a/src/parsers/manifest/dash/native-parser/parse_from_document.ts +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { assertUnreachable } from "../../../../utils/assert"; -import isNullOrUndefined from "../../../../utils/is_null_or_undefined"; -import type { IIrParserResponse, ILoadedXlinkData, IMPDParserArguments } from "../common"; -import parseMpdIr from "../common"; -import type { IPeriodIntermediateRepresentation } from "../node_parser_types"; -import type { IDashParserResponse, ILoadedResource } from "../parsers_types"; -import { createMPDIntermediateRepresentation } from "./node_parsers/MPD"; -import { createPeriodIntermediateRepresentation } from "./node_parsers/Period"; - -/** - * Parse MPD through the JS parser, on a `Document` instance. - * @param {Document} document - Original manifest as returned by the server - * @param {Object} args - Various parsing options and information. - * @returns {Object} - Response returned by the DASH-JS parser. - */ -export default function parseFromDocument( - document: Document, - args: IMPDParserArguments, -): IDashParserResponse { - const root = document.documentElement; - if (isNullOrUndefined(root) || root.nodeName !== "MPD") { - throw new Error("DASH Parser: document root should be MPD"); - } - - const [mpdIR, warnings] = createMPDIntermediateRepresentation(root); - const ret = parseMpdIr(mpdIR, args, warnings); - return processReturn(ret); - - /** - * Handle `parseMpdIr` return values, asking for resources if they are needed - * and pre-processing them before continuing parsing. - * - * @param {Object} initialRes - * @returns {Object} - */ - function processReturn(initialRes: IIrParserResponse): IDashParserResponse { - if (initialRes.type === "done") { - return initialRes; - } else if (initialRes.type === "needs-clock") { - return { - type: "needs-resources", - value: { - urls: [initialRes.value.url], - format: "string", - continue( - loadedClock: Array>, - ): IDashParserResponse { - if (loadedClock.length !== 1) { - throw new Error("DASH parser: wrong number of loaded ressources."); - } - const newRet = initialRes.value.continue(loadedClock[0].responseData); - return processReturn(newRet); - }, - }, - }; - } else if (initialRes.type === "needs-xlinks") { - return { - type: "needs-resources", - value: { - urls: initialRes.value.xlinksUrls, - format: "string", - continue( - loadedXlinks: Array>, - ): IDashParserResponse { - const resourceInfos: ILoadedXlinkData[] = []; - for (let i = 0; i < loadedXlinks.length; i++) { - const { - responseData: xlinkResp, - receivedTime, - sendingTime, - url, - } = loadedXlinks[i]; - if (!xlinkResp.success) { - throw xlinkResp.error; - } - const wrappedData = "" + xlinkResp.data + ""; - const dataAsXML = new DOMParser().parseFromString(wrappedData, "text/xml"); - if (isNullOrUndefined(dataAsXML) || dataAsXML.children.length === 0) { - throw new Error("DASH parser: Invalid external ressources"); - } - const periods = dataAsXML.children[0].children; - const periodsIR: IPeriodIntermediateRepresentation[] = []; - const periodsIRWarnings: Error[] = []; - for (let j = 0; j < periods.length; j++) { - if (periods[j].nodeType === Node.ELEMENT_NODE) { - const [periodIR, periodWarnings] = - createPeriodIntermediateRepresentation(periods[j]); - periodsIRWarnings.push(...periodWarnings); - periodsIR.push(periodIR); - } - } - resourceInfos.push({ - url, - receivedTime, - sendingTime, - parsed: periodsIR, - warnings: periodsIRWarnings, - }); - } - const newRet = initialRes.value.continue(resourceInfos); - return processReturn(newRet); - }, - }, - }; - } else { - assertUnreachable(initialRes); - } - } -} diff --git a/src/parsers/manifest/dash/node_parser_types.ts b/src/parsers/manifest/dash/node_parser_types.ts index 70083f2fff..eb612039ac 100644 --- a/src/parsers/manifest/dash/node_parser_types.ts +++ b/src/parsers/manifest/dash/node_parser_types.ts @@ -100,9 +100,8 @@ export interface IMPDAttributes { /** * XML namespaces linked to the `` element. * - * This property is only needed when the EventStream's `` elements are - * not parsed through the browser's DOMParser API, and thus might depend on - * parent namespaces to be parsed correctly. + * This property is needed when the EventStream's `` elements depend + * on parent namespaces to be parsed / reconstructed correctly. */ namespaces?: Array<{ key: string; value: string }>; } @@ -174,9 +173,8 @@ export interface IPeriodAttributes { /** * XML namespaces linked to the `` element. * - * This property is only needed when the EventStream's `` elements are - * not parsed through the browser's DOMParser API, and thus might depend on - * parent namespaces to be parsed correctly. + * This property is needed when the EventStream's `` elements depend + * on parent namespaces to be parsed / reconstructed correctly. */ namespaces?: Array<{ key: string; value: string }>; } @@ -411,9 +409,8 @@ export interface IEventStreamAttributes { /** * XML namespaces linked to the `` element. * - * This property is only needed when the EventStream's `` elements are - * not parsed through the browser's DOMParser API, and thus might depend on - * parent namespaces to be parsed correctly. + * This property is needed when the EventStream's `` elements depend + * on parent namespaces to be parsed / reconstructed correctly. */ namespaces?: Array<{ key: string; value: string }> | undefined; } @@ -434,7 +431,7 @@ export interface IEventStreamEventIntermediateRepresentation { * - Either as the Element's UTF-8 textual representation. * - Either as the Element's string representation. */ - eventStreamData?: Element | ArrayBuffer | string; + eventStreamData?: ArrayBuffer | string; } -export type ITimelineParser = () => ITNode[] | HTMLCollection; +export type ITimelineParser = () => ITNode[]; diff --git a/src/transports/dash/manifest_parser.ts b/src/transports/dash/manifest_parser.ts index 5b8345f743..4f72396223 100644 --- a/src/transports/dash/manifest_parser.ts +++ b/src/transports/dash/manifest_parser.ts @@ -123,13 +123,9 @@ export default function generateManifestParser( function runDefaultJsParser(): | IManifestParserResult | Promise { - if (parsers.fastJs !== null) { + if (parsers.js !== null) { const manifestStr = getManifestAsString(responseData); - const parsedManifest = parsers.fastJs(manifestStr, dashParserOpts); - return processMpdParserResponse(parsedManifest); - } else if (parsers.native !== null) { - const manifestDocument = getManifestAsDocument(responseData); - const parsedManifest = parsers.native(manifestDocument, dashParserOpts); + const parsedManifest = parsers.js(manifestStr, dashParserOpts); return processMpdParserResponse(parsedManifest); } else { throw new Error("No MPD parser is imported"); @@ -311,29 +307,6 @@ function getManifestAsString(manifestSrc: unknown): string { } } -/** - * Try to convert a Manifest from an unknown format to a `Document` format. - * Useful to exploit DOM-parsing APIs to quickly parse an XML Manifest. - * - * Throws if the format cannot be converted. - * @param {*} manifestSrc - * @returns {Document} - */ -function getManifestAsDocument(manifestSrc: unknown): Document { - if (manifestSrc instanceof ArrayBuffer) { - return new DOMParser().parseFromString( - utf8ToStr(new Uint8Array(manifestSrc)), - "text/xml", - ); - } else if (typeof manifestSrc === "string") { - return new DOMParser().parseFromString(manifestSrc, "text/xml"); - } else if (manifestSrc instanceof Document) { - return manifestSrc; - } else { - throw new Error("DASH Manifest Parser: Unrecognized Manifest format"); - } -} - /** * Try to convert a Manifest from an unknown format to an `ArrayBuffer` format. * Throws if the format cannot be converted.