feat: add deep link support for audio position (t= param) and set default transcript rows to 2
This commit is contained in:
56
app.js
56
app.js
@@ -78,6 +78,41 @@ function formatTime(seconds) {
|
||||
return `${mins}:${String(secs).padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
// Parse time from string: supports seconds (120), m:ss (2:30), h:mm:ss (1:30:45)
|
||||
function parseTimeString(timeStr) {
|
||||
if (!timeStr) return null;
|
||||
const parts = timeStr.split(":").map(Number);
|
||||
if (parts.some(isNaN)) return null;
|
||||
if (parts.length === 1) return parts[0]; // seconds only
|
||||
if (parts.length === 2) return parts[0] * 60 + parts[1]; // m:ss
|
||||
if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2]; // h:mm:ss
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get time parameter from URL (supports ?t=120 or #t=120)
|
||||
function getTimeFromURL() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const queryTime = params.get("t");
|
||||
if (queryTime) return parseTimeString(queryTime);
|
||||
|
||||
const hash = window.location.hash.slice(1); // remove #
|
||||
const hashParams = new URLSearchParams(hash);
|
||||
const hashTime = hashParams.get("t");
|
||||
if (hashTime) return parseTimeString(hashTime);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update URL with current time (uses hash to avoid page reload)
|
||||
function updateURLTime(seconds) {
|
||||
if (!Number.isFinite(seconds) || seconds < 0) return;
|
||||
const time = Math.floor(seconds);
|
||||
const newHash = `t=${time}`;
|
||||
if (window.location.hash !== `#${newHash}`) {
|
||||
history.replaceState(null, "", `#${newHash}`);
|
||||
}
|
||||
}
|
||||
|
||||
function hashString(input) {
|
||||
let hash = 0;
|
||||
for (let i = 0; i < input.length; i += 1) {
|
||||
@@ -786,12 +821,31 @@ async function init() {
|
||||
playPause.textContent = "Play";
|
||||
});
|
||||
audio.addEventListener("loadedmetadata", () => {
|
||||
// Start at configured offset, but don't exceed audio duration
|
||||
// Check URL for deep link time first
|
||||
const urlTime = getTimeFromURL();
|
||||
if (urlTime !== null && Number.isFinite(audio.duration) && audio.duration > 0) {
|
||||
audio.currentTime = Math.min(urlTime, audio.duration - 0.1);
|
||||
return;
|
||||
}
|
||||
// Fall back to configured offset, but don't exceed audio duration
|
||||
if (Number.isFinite(audio.duration) && audio.duration > 0 && START_OFFSET_SECONDS > 0) {
|
||||
audio.currentTime = Math.min(START_OFFSET_SECONDS, audio.duration - 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Update URL when user seeks (manual scrub or click)
|
||||
audio.addEventListener("seeked", () => {
|
||||
updateURLTime(audio.currentTime);
|
||||
});
|
||||
|
||||
// Handle hash changes (when user modifies URL directly)
|
||||
window.addEventListener("hashchange", () => {
|
||||
const urlTime = getTimeFromURL();
|
||||
if (urlTime !== null && Number.isFinite(audio.duration)) {
|
||||
audio.currentTime = Math.min(urlTime, audio.duration - 0.1);
|
||||
}
|
||||
});
|
||||
|
||||
if (configToggle && configPanel) {
|
||||
configToggle.addEventListener("click", () => {
|
||||
configPanel.classList.toggle("open");
|
||||
|
||||
@@ -57,7 +57,7 @@ const CONFIG = {
|
||||
// ===================
|
||||
|
||||
transcript: {
|
||||
rows: 6, // Number of transcript lines shown (2-12)
|
||||
rows: 2, // Number of transcript lines shown (2-12)
|
||||
opacity: 0.25, // Transcript panel opacity (0.2-1.0)
|
||||
size: "medium", // Text size: "small", "medium", "large"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user