From 4bd6998d160a488ccdf0979a873a112795b57ace Mon Sep 17 00:00:00 2001 From: codex Date: Wed, 10 Jun 2026 15:55:29 +0200 Subject: [PATCH] Show playlist video timestamps --- public/app.js | 43 ++++++++++++++++++++++++++++++++++++++++++- public/styles.css | 19 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/public/app.js b/public/app.js index 08a0dcb..a50cf85 100644 --- a/public/app.js +++ b/public/app.js @@ -190,6 +190,23 @@ function savedPositionForVideo(videoId) { return Math.max(0, Number(watchPlaylist.find((item) => item.videoId === videoId)?.position) || 0); } +function formatTime(totalSeconds) { + const seconds = Math.max(0, Math.floor(Number(totalSeconds) || 0)); + const hours = Math.floor(seconds / 3600); + const minutes = Math.floor((seconds % 3600) / 60); + const remainder = seconds % 60; + + if (hours > 0) { + return `${hours}:${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`; + } + return `${minutes}:${String(remainder).padStart(2, "0")}`; +} + +function playlistTimestamp(item) { + const position = formatTime(item.position); + return item.duration > 0 ? `${position} / ${formatTime(item.duration)}` : position; +} + function renderPlaylist() { playlistList.replaceChildren(); @@ -201,9 +218,18 @@ function renderPlaylist() { const playButton = document.createElement("button"); playButton.type = "button"; playButton.className = "playlist-play"; - playButton.textContent = item.title; playButton.title = item.title; + const title = document.createElement("span"); + title.className = "playlist-title"; + title.textContent = item.title; + + const timestamp = document.createElement("span"); + timestamp.className = "playlist-timestamp"; + timestamp.textContent = playlistTimestamp(item); + + playButton.append(title, timestamp); + const deleteButton = document.createElement("button"); deleteButton.type = "button"; deleteButton.className = "playlist-delete"; @@ -216,6 +242,19 @@ function renderPlaylist() { }); } +function refreshActivePlaylistTimestamp() { + if (!currentYoutubeVideoId || !youtubeReady || !youtubePlayer) return; + + watchPlaylist = playlistWithActivePosition(); + const activeItem = watchPlaylist.find((item) => item.videoId === currentYoutubeVideoId); + const timestamp = document.querySelector( + `[data-video-id="${currentYoutubeVideoId}"] .playlist-timestamp` + ); + if (activeItem && timestamp) { + timestamp.textContent = playlistTimestamp(activeItem); + } +} + function loadYoutubeApi() { return new Promise((resolve) => { if (window.YT?.Player) { @@ -951,6 +990,8 @@ navigator.mediaDevices?.addEventListener?.("devicechange", () => { refreshDeviceLists().catch(console.error); }); +setInterval(refreshActivePlaylistTimestamp, 1000); + applyStoredWatchHeight(); watchResizeHandle.addEventListener("pointerdown", (event) => { diff --git a/public/styles.css b/public/styles.css index c7bcb0e..78ab300 100644 --- a/public/styles.css +++ b/public/styles.css @@ -311,21 +311,38 @@ button[aria-pressed="false"] { } .playlist-item button { - height: 36px; + min-height: 42px; min-width: 0; border-radius: 6px; } .playlist-play { + display: grid; + gap: 2px; overflow: hidden; background: rgba(255, 255, 255, 0.08); color: #f2f4f7; text-align: left; + font-weight: 600; +} + +.playlist-title, +.playlist-timestamp { + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +.playlist-timestamp { + color: #aeb7c3; + font-size: 11px; font-weight: 600; } +.playlist-item.is-active .playlist-timestamp { + color: rgba(255, 255, 255, 0.82); +} + .playlist-item.is-active .playlist-play { background: #1f8f5f; }