mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
* feat: support batch video upscaling with folder upload and directory save - Add multi-file selection and entire folder upload (webkitdirectory) - Support drag & drop of both files and folders via FileSystemEntry API - Add save directory picker using File System Access API (showDirectoryPicker) - Process videos sequentially with per-file progress and status tracking - Add right-side result list with click-to-compare functionality - Add per-video duration and total batch duration display - Add retry button for failed items with error detail display - Remove auto-play on comparison viewer load - Fix worker error handling: handle "error" command to reject stale promises - Fix event listener accumulation in video comparison (AbortController) - Fix XSS risk by escaping HTML in all innerHTML injections - Fix URL revocation timing in download to prevent large file truncation - Extract writeToSaveDirectory and truncateName helpers to reduce duplication - Add Usage Guide panel explaining single/multiple/folder upload options Files changed: docs/upscaler/index.html (+1110/-181) docs/upscaler/upscaler.js (+110) * fix: prevent auto-play and resource consumption during upscaling - Set preload="metadata" on preview and comparison video elements - Explicitly pause preview video after setting src in showVideoPreview - Remove preview video seek sync from batch processing progress callbacks (was causing continuous decode during upscaling, wasting GPU resources) - Remove preview video seek sync from retry progress callback - All videos now require manual play; no background resource usage * fix: improve upscaled video quality with higher bitrate and codec support - Raise base bitrate from 2.5 Mbps (25e5) to 8 Mbps (8e6) for standard and firefox processors, preventing quality loss when source videos have high bitrates (e.g. 720p @ 16.7 Mbps was producing 1440p @ 10 Mbps, smaller than the original file) - Double the bitrate for mediabunny processor (X.n1 * 2) to account for the 4x pixel increase from 2x upscaling - Add H.264 Level 5.1 (avc1.640033) and Level 5.2 (avc1.640034) codecs to the fallback lists, since Level 4.0 (avc1.640028, ~24.6 Mbps) cannot support the higher bitrates for 1440p+ outputs - Add latencyMode: "quality" to VideoEncoder configs for both standard and firefox processors, prioritizing encoding quality over latency (suitable for offline processing) * feat: add video data comparison panel for original vs upscaled metrics - Add a comparison data table below the file info bar showing 6 metrics: Resolution, File Size, Duration, Est. Bitrate, Pixels, Process Time - Display per-metric change percentage with color-coded indicators: green (↑) for improvements, red (↓) for regressions, gray for neutral - Estimated bitrate calculated from file size × 8 / video duration - Table renders after both original and upscaled video metadata loads - Auto-hide panel on reset * fix: make video data comparison panel full-width and responsive - Move comparison data table from inside the left column to the full-width area below the left/right layout, so it is no longer constrained by the comparison viewer column width - Remove table-fixed colgroup and overflow-x-auto that caused horizontal scrolling on smaller screens - Table now uses w-full auto-layout, displaying all 4 columns (Metric, Original, Upscaled, Change) without requiring horizontal scrolling --------- Co-authored-by: rionzhao <rionzhao@tencent.com>
393 lines
8.4 KiB
CSS
393 lines
8.4 KiB
CSS
@charset "utf-8";
|
|
|
|
:root {
|
|
--mocha-color: #000;
|
|
--mocha-bg-color: #fff;
|
|
--mocha-pass-icon-color: #00d6b2;
|
|
--mocha-pass-color: #fff;
|
|
--mocha-pass-shadow-color: rgba(0,0,0,.2);
|
|
--mocha-pass-mediump-color: #c09853;
|
|
--mocha-pass-slow-color: #b94a48;
|
|
--mocha-test-pending-color: #0b97c4;
|
|
--mocha-test-pending-icon-color: #0b97c4;
|
|
--mocha-test-fail-color: #c00;
|
|
--mocha-test-fail-icon-color: #c00;
|
|
--mocha-test-fail-pre-color: #000;
|
|
--mocha-test-fail-pre-error-color: #c00;
|
|
--mocha-test-html-error-color: #000;
|
|
--mocha-box-shadow-color: #eee;
|
|
--mocha-box-bottom-color: #ddd;
|
|
--mocha-test-replay-color: #000;
|
|
--mocha-test-replay-bg-color: #eee;
|
|
--mocha-stats-color: #888;
|
|
--mocha-stats-em-color: #000;
|
|
--mocha-stats-hover-color: #eee;
|
|
--mocha-error-color: #c00;
|
|
|
|
--mocha-code-comment: #ddd;
|
|
--mocha-code-init: #2f6fad;
|
|
--mocha-code-string: #5890ad;
|
|
--mocha-code-keyword: #8a6343;
|
|
--mocha-code-number: #2f6fad;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--mocha-color: #fff;
|
|
--mocha-bg-color: #222;
|
|
--mocha-pass-icon-color: #00d6b2;
|
|
--mocha-pass-color: #222;
|
|
--mocha-pass-shadow-color: rgba(255,255,255,.2);
|
|
--mocha-pass-mediump-color: #f1be67;
|
|
--mocha-pass-slow-color: #f49896;
|
|
--mocha-test-pending-color: #0b97c4;
|
|
--mocha-test-pending-icon-color: #0b97c4;
|
|
--mocha-test-fail-color: #f44;
|
|
--mocha-test-fail-icon-color: #f44;
|
|
--mocha-test-fail-pre-color: #fff;
|
|
--mocha-test-fail-pre-error-color: #f44;
|
|
--mocha-test-html-error-color: #fff;
|
|
--mocha-box-shadow-color: #444;
|
|
--mocha-box-bottom-color: #555;
|
|
--mocha-test-replay-color: #fff;
|
|
--mocha-test-replay-bg-color: #444;
|
|
--mocha-stats-color: #aaa;
|
|
--mocha-stats-em-color: #fff;
|
|
--mocha-stats-hover-color: #444;
|
|
--mocha-error-color: #f44;
|
|
|
|
--mocha-code-comment: #ddd;
|
|
--mocha-code-init: #9cc7f1;
|
|
--mocha-code-string: #80d4ff;
|
|
--mocha-code-keyword: #e3a470;
|
|
--mocha-code-number: #4ca7ff;
|
|
}
|
|
}
|
|
|
|
body {
|
|
margin:0;
|
|
background-color: var(--mocha-bg-color);
|
|
color: var(--mocha-color);
|
|
}
|
|
|
|
#mocha {
|
|
font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
|
margin: 60px 50px;
|
|
}
|
|
|
|
#mocha ul,
|
|
#mocha li {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
#mocha ul {
|
|
list-style: none;
|
|
}
|
|
|
|
#mocha h1,
|
|
#mocha h2 {
|
|
margin: 0;
|
|
}
|
|
|
|
#mocha h1 {
|
|
margin-top: 15px;
|
|
font-size: 1em;
|
|
font-weight: 200;
|
|
}
|
|
|
|
#mocha h1 a {
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
|
|
#mocha h1 a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
|
|
#mocha .suite .suite h1 {
|
|
margin-top: 0;
|
|
font-size: .8em;
|
|
}
|
|
|
|
#mocha .hidden {
|
|
display: none;
|
|
}
|
|
|
|
#mocha h2 {
|
|
font-size: 12px;
|
|
font-weight: normal;
|
|
cursor: pointer;
|
|
}
|
|
|
|
#mocha .suite {
|
|
margin-left: 15px;
|
|
}
|
|
|
|
#mocha .test {
|
|
margin-left: 15px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#mocha .test.pending:hover h2::after {
|
|
content: '(pending)';
|
|
font-family: arial, sans-serif;
|
|
}
|
|
|
|
#mocha .test.pass.medium .duration {
|
|
background: var(--mocha-pass-mediump-color);
|
|
}
|
|
|
|
#mocha .test.pass.slow .duration {
|
|
background: var(--mocha-pass-slow-color);
|
|
}
|
|
|
|
#mocha .test.pass::before {
|
|
content: '✓';
|
|
font-size: 12px;
|
|
display: block;
|
|
float: left;
|
|
margin-right: 5px;
|
|
color: var(--mocha-pass-icon-color);
|
|
}
|
|
|
|
#mocha .test.pass .duration {
|
|
font-size: 9px;
|
|
margin-left: 5px;
|
|
padding: 2px 5px;
|
|
color: var(--mocha-pass-color);
|
|
-webkit-box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
|
-moz-box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
|
box-shadow: inset 0 1px 1px var(--mocha-pass-shadow-color);
|
|
-webkit-border-radius: 5px;
|
|
-moz-border-radius: 5px;
|
|
-ms-border-radius: 5px;
|
|
-o-border-radius: 5px;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
#mocha .test.pass.fast .duration {
|
|
display: none;
|
|
}
|
|
|
|
#mocha .test.pending {
|
|
color: var(--mocha-test-pending-color);
|
|
}
|
|
|
|
#mocha .test.pending::before {
|
|
content: '◦';
|
|
color: var(--mocha-test-pending-icon-color);
|
|
}
|
|
|
|
#mocha .test.fail {
|
|
color: var(--mocha-test-fail-color);
|
|
}
|
|
|
|
#mocha .test.fail pre {
|
|
color: var(--mocha-test-fail-pre-color);
|
|
}
|
|
|
|
#mocha .test.fail::before {
|
|
content: '✖';
|
|
font-size: 12px;
|
|
display: block;
|
|
float: left;
|
|
margin-right: 5px;
|
|
color: var(--mocha-test-fail-icon-color);
|
|
}
|
|
|
|
#mocha .test pre.error {
|
|
color: var(--mocha-test-fail-pre-error-color);
|
|
max-height: 300px;
|
|
overflow: auto;
|
|
}
|
|
|
|
#mocha .test .html-error {
|
|
overflow: auto;
|
|
color: var(--mocha-test-html-error-color);
|
|
display: block;
|
|
float: left;
|
|
clear: left;
|
|
font: 12px/1.5 monaco, monospace;
|
|
margin: 5px;
|
|
padding: 15px;
|
|
border: 1px solid var(--mocha-box-shadow-color);
|
|
max-width: 85%; /*(1)*/
|
|
max-width: -webkit-calc(100% - 42px);
|
|
max-width: -moz-calc(100% - 42px);
|
|
max-width: calc(100% - 42px); /*(2)*/
|
|
max-height: 300px;
|
|
word-wrap: break-word;
|
|
border-bottom-color: var(--mocha-box-bottom-color);
|
|
-webkit-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
-moz-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
-webkit-border-radius: 3px;
|
|
-moz-border-radius: 3px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
#mocha .test .html-error pre.error {
|
|
border: none;
|
|
-webkit-border-radius: 0;
|
|
-moz-border-radius: 0;
|
|
border-radius: 0;
|
|
-webkit-box-shadow: 0;
|
|
-moz-box-shadow: 0;
|
|
box-shadow: 0;
|
|
padding: 0;
|
|
margin: 0;
|
|
margin-top: 18px;
|
|
max-height: none;
|
|
}
|
|
|
|
/**
|
|
* (1): approximate for browsers not supporting calc
|
|
* (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border)
|
|
* ^^ seriously
|
|
*/
|
|
#mocha .test pre {
|
|
display: block;
|
|
float: left;
|
|
clear: left;
|
|
font: 12px/1.5 monaco, monospace;
|
|
margin: 5px;
|
|
padding: 15px;
|
|
border: 1px solid var(--mocha-box-shadow-color);
|
|
max-width: 85%; /*(1)*/
|
|
max-width: -webkit-calc(100% - 42px);
|
|
max-width: -moz-calc(100% - 42px);
|
|
max-width: calc(100% - 42px); /*(2)*/
|
|
word-wrap: break-word;
|
|
border-bottom-color: var(--mocha-box-bottom-color);
|
|
-webkit-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
-moz-box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
box-shadow: 0 1px 3px var(--mocha-box-shadow-color);
|
|
-webkit-border-radius: 3px;
|
|
-moz-border-radius: 3px;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
#mocha .test h2 {
|
|
position: relative;
|
|
}
|
|
|
|
#mocha .test a.replay {
|
|
position: absolute;
|
|
top: 3px;
|
|
right: 0;
|
|
text-decoration: none;
|
|
vertical-align: middle;
|
|
display: block;
|
|
width: 15px;
|
|
height: 15px;
|
|
line-height: 15px;
|
|
text-align: center;
|
|
background: var(--mocha-test-replay-bg-color);
|
|
font-size: 15px;
|
|
-webkit-border-radius: 15px;
|
|
-moz-border-radius: 15px;
|
|
border-radius: 15px;
|
|
-webkit-transition:opacity 200ms;
|
|
-moz-transition:opacity 200ms;
|
|
-o-transition:opacity 200ms;
|
|
transition: opacity 200ms;
|
|
opacity: 0.7;
|
|
color: var(--mocha-test-replay-color);
|
|
}
|
|
|
|
#mocha .test:hover a.replay {
|
|
box-shadow: 0 0 1px inset var(--mocha-test-replay-color);
|
|
opacity: 1;
|
|
}
|
|
|
|
#mocha-report.pass .test.fail {
|
|
display: none;
|
|
}
|
|
|
|
#mocha-report.fail .test.pass {
|
|
display: none;
|
|
}
|
|
|
|
#mocha-report.pending .test.pass,
|
|
#mocha-report.pending .test.fail {
|
|
display: none;
|
|
}
|
|
#mocha-report.pending .test.pass.pending {
|
|
display: block;
|
|
}
|
|
|
|
#mocha-error {
|
|
color: var(--mocha-error-color);
|
|
font-size: 1.5em;
|
|
font-weight: 100;
|
|
letter-spacing: 1px;
|
|
}
|
|
|
|
#mocha-stats {
|
|
position: fixed;
|
|
top: 15px;
|
|
right: 10px;
|
|
font-size: 12px;
|
|
margin: 0;
|
|
color: var(--mocha-stats-color);
|
|
z-index: 1;
|
|
}
|
|
|
|
#mocha-stats .progress {
|
|
float: right;
|
|
padding-top: 0;
|
|
|
|
/**
|
|
* Set safe initial values, so mochas .progress does not inherit these
|
|
* properties from Bootstrap .progress (which causes .progress height to
|
|
* equal line height set in Bootstrap).
|
|
*/
|
|
height: auto;
|
|
-webkit-box-shadow: none;
|
|
-moz-box-shadow: none;
|
|
box-shadow: none;
|
|
background-color: initial;
|
|
}
|
|
|
|
#mocha-stats em {
|
|
color: var(--mocha-stats-em-color);
|
|
}
|
|
|
|
#mocha-stats a {
|
|
text-decoration: none;
|
|
color: inherit;
|
|
}
|
|
|
|
#mocha-stats a:hover {
|
|
border-bottom: 1px solid var(--mocha-stats-hover-color);
|
|
}
|
|
|
|
#mocha-stats li {
|
|
display: inline-block;
|
|
margin: 0 5px;
|
|
list-style: none;
|
|
padding-top: 11px;
|
|
}
|
|
|
|
#mocha-stats canvas {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
|
|
#mocha code .comment { color: var(--mocha-code-comment); }
|
|
#mocha code .init { color: var(--mocha-code-init); }
|
|
#mocha code .string { color: var(--mocha-code-string); }
|
|
#mocha code .keyword { color: var(--mocha-code-keyword); }
|
|
#mocha code .number { color: var(--mocha-code-number); }
|
|
|
|
@media screen and (max-device-width: 480px) {
|
|
#mocha {
|
|
margin: 60px 0px;
|
|
}
|
|
|
|
#mocha #stats {
|
|
position: absolute;
|
|
}
|
|
}
|