diff --git a/app/src/main/assets/h5/css/app.css b/app/src/main/assets/h5/css/app.css
index 65c6e78..fa3622f 100644
--- a/app/src/main/assets/h5/css/app.css
+++ b/app/src/main/assets/h5/css/app.css
@@ -301,6 +301,16 @@ body {
transition: width .3s linear;
}
+/* 时长文字:当前时长 / 总时长 */
+/* Time labels: current / total duration */
+.media-card .time-row {
+ display: flex;
+ justify-content: space-between;
+ font-size: 11px;
+ color: var(--text-dim);
+ margin-top: 4px;
+}
+
/* 无时长信息时的活动脉冲进度条 */
/* Indeterminate pulse when no duration is available */
.media-card .bar.indeterminate {
diff --git a/app/src/main/assets/h5/js/pages/home.js b/app/src/main/assets/h5/js/pages/home.js
index a0958f4..17fc798 100644
--- a/app/src/main/assets/h5/js/pages/home.js
+++ b/app/src/main/assets/h5/js/pages/home.js
@@ -161,6 +161,7 @@ function renderMediaCard(info) {
const playIcon = info.playing ? ICON_PAUSE : ICON_PLAY;
// title/artist 来自任意应用元数据,转义后插入,防 XSS
// title/artist come from arbitrary app metadata; escape before insert (XSS)
+ const pct = info.duration ? Math.min(100, (info.position / info.duration) * 100) : 0;
el.innerHTML = `
-
+
+
+ ${fmtDuration(info.position)}
+ ${fmtDuration(info.duration)}
+
@@ -209,20 +214,35 @@ function updateMediaProgressState(info) {
let mediaTicker = null;
function startMediaTicker() {
if (mediaTicker) return;
- mediaTicker = setInterval(() => {
+ const tick = () => {
+ if (!mediaProgressState) return;
const bar = document.querySelector('.media-card .bar');
- if (!bar || !mediaProgressState) return;
+ const cur = document.querySelector('.media-card .t-cur');
const { position, duration, playing, updatedAt } = mediaProgressState;
if (!duration) {
// 无时长信息(部分 App 不提供 duration):显示活动脉冲而非空白
// No duration (some apps omit it): show an indeterminate pulse instead of blank
- bar.classList.add('indeterminate');
+ if (bar) bar.classList.add('indeterminate');
return;
}
- bar.classList.remove('indeterminate');
const pos = playing ? position + (Date.now() - updatedAt) : position;
- bar.style.width = Math.min(100, (pos / duration) * 100) + '%';
- }, 1000);
+ if (bar) {
+ bar.classList.remove('indeterminate');
+ bar.style.width = Math.min(100, (pos / duration) * 100) + '%';
+ }
+ if (cur) cur.textContent = fmtDuration(pos);
+ };
+ tick(); // 立即执行一次,避免初始 1 秒内进度条空白
+ mediaTicker = setInterval(tick, 1000);
+}
+
+// 时长格式化:毫秒 -> m:ss(未知返回 --:--)
+// Duration format: ms -> m:ss (--:-- when unknown)
+function fmtDuration(ms) {
+ if (!ms || ms <= 0) return '--:--';
+ const s = Math.floor(ms / 1000);
+ const m = Math.floor(s / 60);
+ return m + ':' + String(s % 60).padStart(2, '0');
}
// 布局方向:默认上下(封面在上、信息在下),横条(宽>高)时左右