fix: refine video UI loading and playback behavior

This commit is contained in:
nianzhibai
2026-06-14 16:59:41 +08:00
parent 7e5e67697e
commit aa856db1f6
6 changed files with 87 additions and 9 deletions
+2 -2
View File
@@ -632,7 +632,7 @@ export function DrivesPage() {
</button>
<button
type="button"
className="admin-btn is-stop"
className="admin-btn is-primary"
onClick={() => handleStopDriveTasks(d)}
disabled={!!stoppingDriveId}
title="停止此网盘当前的扫描、封面、预览视频和视频指纹生成任务。"
@@ -642,7 +642,7 @@ export function DrivesPage() {
</button>
</div>
{d.kind !== "spider91" && (
<button type="button" className="admin-btn" onClick={() => openEdit(d)}>
<button type="button" className="admin-btn is-primary" onClick={() => openEdit(d)}>
</button>
)}
+3
View File
@@ -92,8 +92,11 @@ const LONG_PRESS_MS = 400;
const FAST_RATE = 2;
/** 默认倍速。 */
const NORMAL_RATE = 1;
/** ArtPlayer 内部播放失败自动重连次数。 */
const ARTPLAYER_RECONNECT_TIME_MAX = 3;
Artplayer.FAST_FORWARD_VALUE = FAST_RATE;
Artplayer.RECONNECT_TIME_MAX = ARTPLAYER_RECONNECT_TIME_MAX;
const DEFAULT_SETTINGS: PlayerSettings = {
volume: 0.7,
+24 -7
View File
@@ -492,10 +492,15 @@
}
.skeleton-card {
--skeleton-card-bg: var(--bg-surface);
--skeleton-card-border: var(--border-subtle);
--skeleton-shimmer-base: rgba(255, 255, 255, 0.03);
--skeleton-shimmer-highlight: rgba(255, 255, 255, 0.08);
position: relative;
aspect-ratio: 16 / 12.5;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
background: var(--skeleton-card-bg);
border: 1px solid var(--skeleton-card-border);
border-radius: var(--radius-md);
padding: 8px;
box-sizing: border-box;
@@ -504,6 +509,18 @@
flex-direction: column;
}
:root[data-theme="pink"] .skeleton-card {
--skeleton-card-border: rgba(255, 91, 138, 0.18);
--skeleton-shimmer-base: rgba(255, 91, 138, 0.12);
--skeleton-shimmer-highlight: rgba(255, 91, 138, 0.26);
}
:root[data-theme="sky"] .skeleton-card {
--skeleton-card-border: rgba(60, 100, 170, 0.18);
--skeleton-shimmer-base: rgba(60, 100, 170, 0.13);
--skeleton-shimmer-highlight: rgba(60, 100, 170, 0.26);
}
/* Skeleton image thumbnail area */
.skeleton-card::before {
content: "";
@@ -513,9 +530,9 @@
border-radius: var(--radius-sm);
background: linear-gradient(
90deg,
rgba(255, 255, 255, 0.03) 25%,
rgba(255, 255, 255, 0.08) 50%,
rgba(255, 255, 255, 0.03) 75%
var(--skeleton-shimmer-base) 25%,
var(--skeleton-shimmer-highlight) 50%,
var(--skeleton-shimmer-base) 75%
);
background-size: 200% 100%;
animation: skeleton-shimmer 1.6s ease-in-out infinite;
@@ -529,8 +546,8 @@
width: 100%;
height: 28px;
background:
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 25%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.03) 75%) 0 0 / 70% 12px no-repeat,
linear-gradient(90deg, rgba(255, 255, 255, 0.03) 25%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0.03) 75%) 0 20px / 42% 8px no-repeat;
linear-gradient(90deg, var(--skeleton-shimmer-base) 25%, var(--skeleton-shimmer-highlight) 50%, var(--skeleton-shimmer-base) 75%) 0 0 / 70% 12px no-repeat,
linear-gradient(90deg, var(--skeleton-shimmer-base) 25%, var(--skeleton-shimmer-highlight) 50%, var(--skeleton-shimmer-base) 75%) 0 20px / 42% 8px no-repeat;
background-size: 200% 100%;
animation: skeleton-shimmer 1.6s ease-in-out infinite;
}
+15
View File
@@ -304,6 +304,21 @@ test("drive management exposes stop task controls", () => {
assert.match(drivesPageSource, /停止所有网盘任务/);
});
test("drive detail primary actions use the rescan button color", () => {
assert.match(
drivesPageSource,
/className="admin-btn is-primary"\s+onClick=\{\(\) => handleRescan\(d\)\}/
);
assert.match(
drivesPageSource,
/className="admin-btn is-primary"\s+onClick=\{\(\) => handleStopDriveTasks\(d\)\}/
);
assert.match(
drivesPageSource,
/className="admin-btn is-primary"\s+onClick=\{\(\) => openEdit\(d\)\}/
);
});
test("drive rescan reports busy storage tasks instead of queueing duplicates", () => {
assert.match(apiSource, /accepted:\s*boolean;\s*message\?:\s*string/);
assert.match(apiSource, /scanGenerationStatus\?: DriveGenerationStatus/);
+35
View File
@@ -0,0 +1,35 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const videoCardCss = readFileSync(
new URL("../src/styles/video-card.css", import.meta.url),
"utf8"
);
function ruleBody(css: string, selector: string): string {
const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const match = css.match(new RegExp(`${escapedSelector}\\s*\\{([^}]*)\\}`));
assert.ok(match, `Expected CSS rule for ${selector}`);
return match[1];
}
test("home video skeleton uses theme-aware non-white shimmer colors", () => {
const skeleton = ruleBody(videoCardCss, ".skeleton-card");
const pink = ruleBody(videoCardCss, ':root[data-theme="pink"] .skeleton-card');
const sky = ruleBody(videoCardCss, ':root[data-theme="sky"] .skeleton-card');
const thumb = ruleBody(videoCardCss, ".skeleton-card::before");
const text = ruleBody(videoCardCss, ".skeleton-card::after");
assert.match(skeleton, /--skeleton-shimmer-base\s*:/);
assert.match(skeleton, /--skeleton-shimmer-highlight\s*:/);
assert.match(thumb, /var\(--skeleton-shimmer-base\)/);
assert.match(thumb, /var\(--skeleton-shimmer-highlight\)/);
assert.match(text, /var\(--skeleton-shimmer-base\)/);
assert.match(text, /var\(--skeleton-shimmer-highlight\)/);
assert.match(pink, /--skeleton-shimmer-base\s*:\s*rgba\(255,\s*91,\s*138,\s*0\.12\)/);
assert.match(pink, /--skeleton-shimmer-highlight\s*:\s*rgba\(255,\s*91,\s*138,\s*0\.26\)/);
assert.match(sky, /--skeleton-shimmer-base\s*:\s*rgba\(60,\s*100,\s*170,\s*0\.13\)/);
assert.match(sky, /--skeleton-shimmer-highlight\s*:\s*rgba\(60,\s*100,\s*170,\s*0\.26\)/);
});
+8
View File
@@ -74,6 +74,14 @@ test("detail player exposes a non-persistent loop switch in ArtPlayer settings",
assert.match(playerSource, /item\.tooltip = next \? "开" : "关"/);
});
test("detail player limits ArtPlayer automatic reconnect attempts", () => {
assert.match(playerSource, /const ARTPLAYER_RECONNECT_TIME_MAX = 3;/);
assert.match(
playerSource,
/Artplayer\.RECONNECT_TIME_MAX = ARTPLAYER_RECONNECT_TIME_MAX;/
);
});
test("detail loading skeleton matches current desktop video page layout", () => {
assert.match(detailPageSource, /className="vd-layout vd-skeleton"/);
assert.match(detailPageSource, /className="vd-skeleton__summary"/);