mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-28 10:31:20 -08:00
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:
commit
0896841d23
538 files changed
+379715
No files matched your search
@@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* artplayer-plugin-danmuku-mask.js v1.1.0
|
||||
* Github: https://github.com/zhw2590582/ArtPlayer
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(t.artplayerPluginDanmukuMask=t(),define(function(){return t.artplayerPluginDanmukuMask})):(e="undefined"!==typeof globalThis?globalThis:e||self).artplayerPluginDanmukuMask=t()}(this,function(){"use strict";function e(e,t){for(var n=0;n<t.length;n++){const r=t[n];if("string"!==typeof r&&!Array.isArray(r))for(const t in r)if("default"!==t&&!(t in e)){const n=Object.getOwnPropertyDescriptor(r,t);n&&Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>r[t]})}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}class t{constructor(e,t){this.backend=e,this.dataMover=t,this.data=new WeakMap,this.dataIdsCount=0}get(e){return this.data.has(e)||this.dataMover.moveData(this.backend,e),this.data.get(e)}set(e,t){this.dataIdsCount++,this.data.set(e,t)}has(e){return this.data.has(e)}delete(e){return this.dataIdsCount--,this.data.delete(e)}numDataIds(){return this.dataIdsCount}}class n{refCount(e){return r("refCount")}incRef(e){return r("incRef")}timerAvailable(){return!0}time(e){return r("time")}read(e){return r("read")}readSync(e){return r("readSync")}readToGPU(e,t){return r("readToGPU")}numDataIds(){return r("numDataIds")}disposeData(e,t){return r("disposeData")}write(e,t,n){return r("write")}move(e,t,n,a,s){return r("move")}createTensorFromGPUData(e,t,n){return r("createTensorFromGPUData")}memory(){return r("memory")}floatPrecision(){return r("floatPrecision")}epsilon(){return 32===this.floatPrecision()?1e-7:1e-4}dispose(){return r("dispose")}}function r(e){throw new Error(`'${e}' not yet implemented or not found in the registry. This kernel may not be supported by the tfjs backend you have chosen`)}function a(e,t,n){return Math.max(e,Math.min(t,n))}function s(e){return e%2===0?e:e+1}function o(e,t,n){const r=e[t];e[t]=e[n],e[n]=r}function i(e,t){if(!e)throw new Error("string"===typeof t?t:t())}function u(e,t,n=""){i(d(e,t),()=>n+` Shapes ${e} and ${t} must match`)}function l(e){i(null!=e,()=>"The input to the tensor constructor must be a non-null value.")}function c(e){if(0===e.length)return 1;let t=e[0];for(let n=1;n<e.length;n++)t*=e[n];return t}function d(e,t){if(e===t)return!0;if(null==e||null==t)return!1;if(e.length!==t.length)return!1;for(let n=0;n<e.length;n++)if(e[n]!==t[n])return!1;return!0}function p(e){return e%1===0}function h(e){const t=Math.ceil(Math.sqrt(e));return[t,Math.ceil(e/t)]}function f(e,t){return t<=e.length?e:e+" ".repeat(t-e.length)}function m(e,t=e=>0,n,r){return new Promise((a,s)=>{let o=0;const i=()=>{if(e())return void a();o++;const u=t(o);null!=n&&o>=n?s():null!=r?r(i,u):setTimeout(i,u)};i()})}function g(e,t){let n=1,r=-1;for(let s=0;s<e.length;++s)if(e[s]>=0)n*=e[s];else if(-1===e[s]){if(-1!==r)throw Error(`Shapes can only have 1 implicit size. Found -1 at dim ${r} and dim ${s}`);r=s}else if(e[s]<0)throw Error(`Shapes can not be < 0. Found ${e[s]} at dim ${s}`);if(-1===r){if(t>0&&t!==n)throw Error(`Size(${t}) must match the product of shape ${e}`);return e}if(0===n)throw Error(`Cannot infer the missing size in [${e}] when there are 0 elements`);if(t%n!==0)throw Error(`The implicit shape can't be a fractional number. Got ${t} / ${n}`);const a=e.slice();return a[r]=t/n,a}function y(e,t){const n=t.length;return i((e=null==e?t.map((e,t)=>t):[].concat(e)).every(e=>e>=-n&&e<n),()=>`All values in axis param must be in range [-${n}, ${n}) but got axis ${e}`),i(e.every(e=>p(e)),()=>`All values in axis param must be integers but got axis ${e}`),e.map(e=>e<0?n+e:e)}function b(e,t){const n=[],r=[],a=null!=t&&Array.isArray(t)&&0===t.length,s=null==t||a?null:y(t,e).sort();let o=0;for(let i=0;i<e.length;++i){if(null!=s){if(s[o]===i&&1!==e[i])throw new Error(`Can't squeeze axis ${i} since its dim '${e[i]}' is not 1`);(null==s[o]||s[o]>i)&&1===e[i]&&(n.push(e[i]),r.push(i)),s[o]<=i&&o++}1!==e[i]&&(n.push(e[i]),r.push(i))}return{newShape:n,keptDims:r}}function x(e,t){return v(e,t)}function v(e,t){let n=null;if(null==e||"float32"===e)n=new Float32Array(t);else if("int32"===e)n=new Int32Array(t);else if("bool"===e)n=new Uint8Array(t);else{if("string"!==e)throw new Error(`Unknown data type ${e}`);n=new Array(t)}return n}function w(e,t){return"complex64"!==t&&(("float32"!==t||"complex64"===e)&&(("int32"!==t||"float32"===e||"complex64"===e)&&("bool"!==t||"bool"!==e)))}function k(e){if("float32"===e||"int32"===e)return 4;if("complex64"===e)return 8;if("bool"===e)return 1;throw new Error(`Unknown dtype ${e}`)}function I(e){return"string"===typeof e||e instanceof String}function N(e){return Array.isArray(e)?N(e[0]):e instanceof Float32Array?"float32":e instanceof Int32Array||e instanceof Uint8Array||e instanceof Uint8ClampedArray?"int32":"number"===typeof e?"float32":I(e)?"string":function(e){return"boolean"===typeof e}(e)?"bool":"float32"}function S(e){return!!(e&&e.constructor&&e.call&&e.apply)}function T(e,t){for(let n=t;n<e;++n)if(e%n===0)return nLine truncated
|
||||
Reference in new issue
Block a user