feat: add deep link support for audio position (t= param) and set default transcript rows to 2

This commit is contained in:
5shekel
2026-01-18 02:42:45 +02:00
parent 2a8d6273ee
commit 24cf7a39f7
2 changed files with 56 additions and 2 deletions

56
app.js
View File

@@ -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");

View File

@@ -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"
},