From 99bc635cd981e2564e154bd0ee6cb6d3614c11c2 Mon Sep 17 00:00:00 2001 From: Gerben Date: Sun, 20 Oct 2019 21:47:43 +0530 Subject: [PATCH] Factor out and improve media fragment parser --- app/audio-player/contentscript.js | 16 ++++----- app/util/parse-media-fragment-identifier.js | 38 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) create mode 100644 app/util/parse-media-fragment-identifier.js diff --git a/app/audio-player/contentscript.js b/app/audio-player/contentscript.js index ab4e90a..510b9ef 100644 --- a/app/audio-player/contentscript.js +++ b/app/audio-player/contentscript.js @@ -1,6 +1,8 @@ import * as WaveformPlaylist from 'waveform-playlist'; import delay from 'delay'; +import parseMediaFragmentIdentifier from '../util/parse-media-fragment-identifier.js'; + async function init() { document.body.innerHTML = `
@@ -46,15 +48,11 @@ async function init() { const fragmentIdentifier = window.location.hash; if (fragmentIdentifier) { - const match = fragmentIdentifier.match(/#t=(\d+(?:\.\d+)?)?(?:,(\d+(?:\.\d+)?))?/); - if (match) { - if (match[1] !== undefined) { - start = Number.parseFloat(match[1]); - } - if (match[2] !== undefined) { - end = Number.parseFloat(match[2]); - } - } + try { + const parsed = parseMediaFragmentIdentifier(fragmentIdentifier); + start = parsed.start; + end = parsed.end; + } catch (err) {} } // Emit event to update selection in player diff --git a/app/util/parse-media-fragment-identifier.js b/app/util/parse-media-fragment-identifier.js new file mode 100644 index 0000000..171f99e --- /dev/null +++ b/app/util/parse-media-fragment-identifier.js @@ -0,0 +1,38 @@ +// 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 }; +}