// Parses the time range from a media fragment identifier such as '#t=12.5,1:59:45.12'. // See https://www.w3.org/TR/media-frags/ // Note this only supports npt (normal playing time) format, not smpte or wall clock time. // Returns an object { start, end }; both numbers are given in seconds (possibly non-integer). export default function parseMediaFragmentIdentifier(fragmentIdentifier) { // Strip possible leading hash character '#'. if (fragmentIdentifier.startsWith('#')) { fragmentIdentifier = fragmentIdentifier.substring(1); } const temporalDimensionRegex = /(?:^|&)(?:t|%74)=([^&]+)(?=&|$)/g; const temporalDimensionMatches = [...fragmentIdentifier.matchAll(temporalDimensionRegex)]; if (temporalDimensionMatches.length === 0) { throw new Error('No time dimension defined'); } // If there are multiple occurrences (e.g. t=1&t=2), take the last one. const temporalDimensionMatch = temporalDimensionMatches[temporalDimensionMatches.length - 1]; const temporalDimensionValue = decodeURIComponent(temporalDimensionMatch[1]); const nptRegex = /^(?:npt:)?(?:(?:(\d+):)??(?:(\d+):)?(\d+(?:\.\d*)?))?(?:,(?:(\d+):)??(?:(\d+):)?(\d+(?:\.\d*)?))?$/; const nptMatch = temporalDimensionValue.match(nptRegex); if (!nptMatch) { throw new Error('Unable to parse fragment identifier.'); } const start = (nptMatch[3] ? Number.parseFloat(nptMatch[3]) : 0) + (nptMatch[2] ? Number.parseInt(nptMatch[2]) * 60 : 0) + (nptMatch[1] ? Number.parseInt(nptMatch[1]) * 60 * 60 : 0); let end; if (nptMatch[6]) { end = Number.parseFloat(nptMatch[6]) + (nptMatch[5] ? Number.parseInt(nptMatch[5]) * 60 : 0) + (nptMatch[4] ? Number.parseInt(nptMatch[4]) * 60 * 60 : 0); } return { start, end }; }