class FallbackSourcesUtils { private static FORMATS = { dash: 'mpegdash', hls: 'applehttp', progressive: 'url' }; /** * Checks if a source matches the given condition. * @param source The source object to check. * @param condition The condition object. * @returns True if the source matches the condition, false otherwise. */ private static sourceMatches(source: any, condition: any): boolean { if (condition.deliveryProfileId && condition.format) { const format = FallbackSourcesUtils.FORMATS[condition.format]; return source.id.endsWith(`${condition.deliveryProfileId},${format}`); } return false; } /** * Groups fallback options by their target source format. * @param fallbackConfig The configuration for fallback sources. * @returns An object where keys are formats and values are arrays of fallback options. */ private static getFallbackOptionsByToSourceFormat(fallbackConfig: any): any { return fallbackConfig.reduce((acc: any, { fromSource, toSource }) => { if (!acc[toSource.format]) { acc[toSource.format] = []; } acc[toSource.format].push({ fromSource, toSource }); return acc; }, {}); } /** * Extracts fallback sources from the provided sources based on the fallback configuration. * @param allSources The complete set of sources. * @param fallbackConfig The configuration for fallback sources. * @returns An object containing fallback and non-fallback sources. */ public static splitSources(allSources: any, fallbackConfig: any = []): any { const nonFallbackSources: any = {}; const fallbackSources: any = {}; const fallbackOptionsByFormat = this.getFallbackOptionsByToSourceFormat(fallbackConfig); for (const format in this.FORMATS) { const formatSources = allSources[format] || []; nonFallbackSources[format] = []; fallbackSources[format] = []; for (const source of formatSources) { let isFallbackSource = false; for (const { toSource } of fallbackOptionsByFormat[format] || []) { if (this.sourceMatches(source, toSource)) { isFallbackSource = true; break; } } if (!isFallbackSource) { nonFallbackSources[format].push(source); } else { fallbackSources[format].push(source); } } } return { fallbackSources, nonFallbackSources }; } /** * Returns the first matching fallback source for the selected original source. * @param selectedSource The source to match against. * @param fallbackSources The available fallback sources. * @param fallbackConfig The configuration for fallback sources. * @returns The matching fallback source or null if none found. */ public static getMatchingFallbackSources(selectedSource: any, fallbackSources: any, fallbackConfig: any = []): any | null { const result = {}; for (const format in this.FORMATS) { result[format] = []; } for (const { fromSource, toSource } of fallbackConfig) { // Check if the from (current) source matches the condition if (fallbackSources[toSource.format] && this.sourceMatches(selectedSource, fromSource)) { for (const source of fallbackSources[toSource.format]) { // Check if the to (target) source matches the condition if (this.sourceMatches(source, toSource)) { result[toSource.format] = [source]; return result; } } } } return null; } } export { FallbackSourcesUtils };