From 2aa00e162f0a0b06f86b4bd329cb9d4867a866b8 Mon Sep 17 00:00:00 2001 From: jiulinxiri Date: Wed, 29 Apr 2026 17:02:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=99=BA=E8=83=BD=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E9=9D=9E=E9=BB=91=E8=89=B2=E8=A7=86=E9=A2=91=E5=B0=81=E9=9D=A2?= =?UTF-8?q?=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++- versions.json | 3 +- 2 files changed, 87 insertions(+), 2 deletions(-) diff --git a/main.js b/main.js index 18b5000..cc498c4 100644 --- a/main.js +++ b/main.js @@ -3790,6 +3790,71 @@ var fillAudioPreviewArtwork = (app, file, cover) => { img.alt = file.name; }); }; +var waitForSeek = (video, time) => new Promise((resolve) => { + const onSeeked = () => { + video.removeEventListener("seeked", onSeeked); + resolve(); + }; + video.addEventListener("seeked", onSeeked, { once: true }); + try { + video.currentTime = time; + } catch (error) { + video.removeEventListener("seeked", onSeeked); + resolve(); + } +}); +var isCanvasFrameMostlyBlack = (ctx, width, height) => { + const sampleWidth = Math.max(8, Math.min(64, width)); + const sampleHeight = Math.max(8, Math.min(64, height)); + const imageData = ctx.getImageData(0, 0, sampleWidth, sampleHeight).data; + let brightPixels = 0; + let totalPixels = 0; + for (let i = 0; i < imageData.length; i += 4) { + const luminance = imageData[i] * 0.2126 + imageData[i + 1] * 0.7152 + imageData[i + 2] * 0.0722; + if (luminance >= 20) + brightPixels++; + totalPixels++; + } + if (!totalPixels) + return true; + return brightPixels / totalPixels < 0.02; +}; +var pickNonBlackVideoCoverTime = (video) => __async(null, null, function* () { + const duration = Number.isFinite(video.duration) ? video.duration : 0; + if (duration <= 0.1) + return 0; + const canvas = document.createElement("canvas"); + canvas.width = 64; + canvas.height = 64; + const ctx = canvas.getContext("2d", { willReadFrequently: true }); + if (!ctx) + return 0; + const checkpoints = [ + 0.02, + 0.06, + 0.12, + 0.2, + 0.35, + 0.5, + 0.7, + 0.85 + ]; + const tried = /* @__PURE__ */ new Set(); + for (const ratio of checkpoints) { + const target = Math.min(Math.max(duration * ratio, 0), Math.max(duration - 0.05, 0)); + const bucket = Math.round(target * 100); + if (tried.has(bucket)) + continue; + tried.add(bucket); + yield waitForSeek(video, target); + if (video.readyState < 2) + continue; + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + if (!isCanvasFrameMostlyBlack(ctx, canvas.width, canvas.height)) + return target; + } + return 0; +}); var appendAudioPreview = (app, figure, file, settings) => { figure.addClass("img-gallery-audio-item"); const audioCard = figure.createEl("div", { cls: "img-gallery-audio-card" }); @@ -3821,13 +3886,32 @@ var appendPreviewMedia = (app, figure, file, settings) => { "object-position": "center center", display: "block" }); + let coverTime = 0; + let coverInitialized = false; + const ensureVideoCover = () => __async(null, null, function* () { + if (coverInitialized) + return; + coverInitialized = true; + video.pause(); + const pickedTime = yield pickNonBlackVideoCoverTime(video); + coverTime = Number.isFinite(pickedTime) ? pickedTime : 0; + yield waitForSeek(video, coverTime); + video.pause(); + }); + video.addEventListener("loadedmetadata", () => { + void ensureVideoCover(); + }, { once: true }); video.addEventListener("mouseenter", () => { void video.play().catch(() => { }); }); video.addEventListener("mouseleave", () => { video.pause(); - video.currentTime = 0; + if (coverInitialized) { + void waitForSeek(video, coverTime); + } else { + video.currentTime = 0; + } }); return video; } diff --git a/versions.json b/versions.json index 18b91ee..ea3d987 100644 --- a/versions.json +++ b/versions.json @@ -2,5 +2,6 @@ "1.0.0": "0.14.6", "1.1.0": "1.1.8", "1.1.1": "1.1.8", - "2.0.0": "1.1.8" + "2.0.0": "1.1.8", + "2.0.1": "2.0.1" }