feat: batch video upscaling with folder upload, directory save, and result comparison (#1051)

* 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>
This commit is contained in:
糖猫猫andrionzhao authored and GitHub committed 2026-09-10 17:19:38 +08:00
commit 0896841d23
538 files changed
+379715

No files matched your search

+1
View File
@@ -0,0 +1 @@
if(!self.define){let s,a={};const e=(e,i)=>(e=new URL(e+".js",i).href,a[e]||new Promise((a=>{if("document"in self){const s=document.createElement("script");s.src=e,s.onload=a,document.head.appendChild(s)}else s=e,importScripts(e),a()})).then((()=>{let s=a[e];if(!s)throw new Error(`Module ${e} didn’t register its module`);return s})));self.define=(i,b)=>{const l=s||("document"in self?document.currentScript.src:"")||location.href;if(a[l])return;let c={};const r=s=>e(s,l),n={module:{uri:l},exports:c,require:r};a[l]=Promise.all(i.map((s=>n[s]||r(s)))).then((s=>(b(...s),c)))}}define(["./workbox-9a84fccb"],(function(s){"use strict";self.addEventListener("message",(s=>{s.data&&"SKIP_WAITING"===s.data.type&&self.skipWaiting()})),s.precacheAndRoute([{url:"assets/@mediapipe/selfie_segmentation/selfie_segmentation_solution_simd_wasm_bin.js",revision:"8351a2228d0d2de9dc9cc041db580385"},{url:"assets/@mediapipe/selfie_segmentation/selfie_segmentation_solution_wasm_bin.js",revision:"e9e7ba7caece4fde78c2b44cd9053f7a"},{url:"assets/@mediapipe/selfie_segmentation/selfie_segmentation.js",revision:"e946199ccd7a913f671a58b0a1e14ff4"},{url:"assets/css/normalize.css",revision:"934e11a04762ee86e8181a50912a0635"},{url:"assets/css/style.css",revision:"a2219dffa3ca43ad226937ba2025f65e"},{url:"assets/example/ads.js",revision:"25341ac4a88a4ac42eb267dbd3067b29"},{url:"assets/example/ambilight.js",revision:"a3334b053f3ecce5e7293dd9ca4811f8"},{url:"assets/example/auto.thumbnail.js",revision:"f8d7553529a54d89dc8f481da63f90da"},{url:"assets/example/canvas.js",revision:"229aac54de939baaef8e67cb92536cf4"},{url:"assets/example/chapter.js",revision:"479c0841ed154948f0cc46fd92daa7eb"},{url:"assets/example/chromecast.js",revision:"80d4535179ea849fddcfde5d470e8ed0"},{url:"assets/example/danmuku.js",revision:"648e20547b40efcd922496039a8a2500"},{url:"assets/example/danmuku.mask.js",revision:"00d1fa9b6634a072bdad31a3aa9b2563"},{url:"assets/example/dash.control.js",revision:"54c5a2acdc065c2b8354f4896312ab1d"},{url:"assets/example/dash.js",revision:"fc57b1cce558540eac7d35c480e36ab5"},{url:"assets/example/flv.js",revision:"4f05c5655380ccf4a40e91505035603b"},{url:"assets/example/hls.control.js",revision:"126716f9f595abd5402a54b49c886aac"},{url:"assets/example/hls.js",revision:"85039853d0f759dd63f00d4a9d5b22ce"},{url:"assets/example/iframe.js",revision:"e7d004c0a155efe99533903d5af7278c"},{url:"assets/example/index.js",revision:"84018fcccffe8a0b3298826e2baa7724"},{url:"assets/example/libass.js",revision:"4542dc99c7a2667b371f9d6071b36bfd"},{url:"assets/example/mobile.js",revision:"d9725aea1b9480ffe98d6109b45ca7a1"},{url:"assets/example/mpegts.js",revision:"62b285aad427dd06ea5dc02e432e067b"},{url:"assets/example/multiple.subtitles.js",revision:"e12bf313d9d66b4158cd1782e5234cc3"},{url:"assets/example/setting.test.js",revision:"4c5133ad8af576b7c6948365ec6f808b"},{url:"assets/example/thumbnail.js",revision:"dd4d5a1c0adfefb6995fc3527fd2614d"},{url:"assets/example/vast.js",revision:"3659ec5dcaa2555d454b19be6f6af400"},{url:"assets/example/vtt.thumbnail.js",revision:"e3bf1f75e62330d41d471b0a44aca24c"},{url:"assets/example/webav.js",revision:"fa78188f98b2e436cfcae64cbd6f3f54"},{url:"assets/example/webtorrent.js",revision:"0b813a8f18b3de714affa3ce176ec160"},{url:"assets/img/alipay.jpg",revision:"b01a1e3ebda10dffdf7157680ad94d06"},{url:"assets/img/danmu-off.svg",revision:"11e49cbb3a79c3c5ef21e5a1188b4aea"},{url:"assets/img/danmu-on.svg",revision:"7764c5dab2a94592877406cb79844195"},{url:"assets/img/indicator.svg",revision:"f0803fd6dad6d7f833821d6ad24182ce"},{url:"assets/img/logo.png",revision:"113f758a35e9c71b8d9c41240da8233b"},{url:"assets/img/pause.svg",revision:"525af27696c30b5bcf436dd90099ea7f"},{url:"assets/img/paypal.svg",revision:"cbe85a7e4c5ca04274a8aedda007b710"},{url:"assets/img/state.svg",revision:"2e9bac37536c46c09a38e74a480b56c6"},{url:"assets/img/subtitle.svg",revision:"99ff929bf767009e5649212e90c10392"},{url:"assets/img/wechat.jpg",revision:"f2dd1964f839781270569fe53cbae21b"},{url:"assets/js/common.js",revision:"111162390ba0d401ce94bd023d80d3b1"},{url:"assets/js/console.js",revision:"4e9a2c80679b6bbf78a4809a4a90f64a"},{url:"assets/js/JavascriptSubtitlesOctopus/subtitles-octopus-worker.js",revision:"cef7e5f6ef27a8e01d7c1bcb78cbbe4d"},{url:"assets/js/JavascriptSubtitlesOctopus/subtitles-octopus.js",revision:"a02d9bcf0639259b92b1c8ddfc45b994"},{url:"assets/js/mobile.js",revision:"47af8a8baea97d5f10a169c4ffb2c869"},{url:"assets/js/vconsole.min.js",revision:"e637889f2f4e869003f4950cce737ba6"},{url:"assets/js/vs/base/worker/workerMain.js",revision:"ff1e00116d48f10b29a7f4f0deac2cd3"},{url:"assets/js/vs/basic-languages/abap/abap.js",revision:"0d31790a1a820013ebfceaaea520086e"},{url:"assets/js/vs/basic-languages/apex/apex.js",revision:"7ed571a41e7a0f49e3813733ee9668ab"},{url:"assets/js/vs/basic-languages/azcli/azcli.js",revision:"2c95f109efa42b39efd112e1ca3f6156"},{url:"assets/js/vs/basic-languages/bat/bat.js",revision:"3a19616507d8d1f534c6155dd45075b7"},{url:Line truncated