mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
35 lines
13 KiB
JavaScript
35 lines
13 KiB
JavaScript
/*!
|
|
* artplayer-tool-thumbnail.js v5.0.0
|
|
* Github: https://github.com/zhw2590582/ArtPlayer
|
|
* (c) 2017-2026 Harvey Zhao
|
|
* Released under the MIT License.
|
|
*
|
|
* ArtPlayer thumbnail tool bundled third-party notices
|
|
*
|
|
* tiny-emitter - reference 2.1.0, locally adapted to a TypeScript class
|
|
* https://github.com/scottcorgan/tiny-emitter/tree/2.1.0
|
|
*
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2017 Scott Corgan
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
!function(e,t){"object"===typeof exports&&"undefined"!==typeof module?module.exports=t():"function"==typeof define&&define.amd?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";const e=(e,t)=>Object.prototype.hasOwnProperty.call(e,t);class t{on(t,o,n){const r=this.e||(this.e={});let i=e(r,t)?r[t]:void 0;return i||(i=[],Object.defineProperty(r,t,{value:i,enumerable:!0,configurable:!0,writable:!0})),i.push({fn:o,ctx:n}),this}once(e,t,o){const n=this,r=t;let i=!1;function s(...t){i||(i=!0,n.off(e,s),r.apply(o,t))}return s._=t,this.on(e,s,o)}emit(t,...o){const n=this.e||(this.e={}),r=(e(n,t)&&n[t]||[]).slice();for(let e=0;e<r.length;e+=1)r[e].fn.apply(r[e].ctx,o);return this}off(t,o){const n=this.e||(this.e={}),r=e(n,t)?n[t]:void 0,i=[];if(r&&o)for(let e=0,s=r.length;e<s;e+=1)r[e].fn!==o&&r[e].fn._!==o&&i.push(r[e]);return i.length?n[t]=i:delete n[t],this}}const o=new WeakMap;function n(e){return o.has(e)||o.set(e,{closed:!1,epoch:0,inputEpoch:0,job:null,video:null,sourceUrl:null,thumbnailUrl:null,sourceUrls:new Set,thumbnailUrls:new Set,sourceListeners:[],loading:!1}),o.get(e)}function r(e){const t=new Error(`Thumbnail task cancelled: ${e}`);return t.name="AbortError",t}function i(e,t){t&&(e.delete(t),URL.revokeObjectURL(t))}function s(e){let t,o=!1;for(const r of e)try{r()}catch(n){o||(o=!0,t=n)}if(o)throw t}function c(e,t="destroyed"){const o=n(e);return!o.closed&&(o.closed=!0,o.epoch++,o.job?.cancel(t),!0)}function a(e){const t=n(e),o=new Set([...t.sourceUrls,e.videoUrl]),r=new Set([...t.thumbnailUrls,e.thumbnailUrl]),c=[];for(const n of new Set([t.video,e.video]))n&&c.push(()=>n.pause?.(),()=>n.removeAttribute?.("src"),()=>n.load?.(),()=>n.parentNode?.removeChild(n));s([...t.sourceListeners.splice(0),...c,...[...o].map(e=>()=>i(t.sourceUrls,e)),...[...r].map(e=>()=>i(t.thumbnailUrls,e))])}function l(e,t,o){return Math.max(Math.min(e,Math.max(t,o)),Math.min(t,o))}function u(e){const t="workspace-4.4"===e.compatibility;return{delay:t?null:e.delay,aspectHeight:t,resetInput:t}}function d(e,t){let o=t.epoch;const c=e.video;let a,d,h,p,f=!1,m=!1,b=()=>{};const y=[],g=new Promise((e,t)=>{a=e,d=t});g.catch(()=>{});const v=()=>!f&&!t.closed&&t.epoch===o&&t.job===p;function w(){return!f&&(f=!0,t.job===p&&(t.job=null,e.processing=!1),s([()=>clearTimeout(h),b,...y.splice(0)]),!0)}function U(t,o=!0){if(!f){try{w()}catch(n){t||(t=n)}if(o)try{e.emit("error",t?.message)}catch(r){t=r}d(t)}}function E(){if(v())try{w(),e.emit("done"),a()}catch(t){let n=t;try{e.emit("error",t?.message)}catch(o){n=o}d(n)}}function T(t){if(!v())return;m=!0,e.processing=!0;let o=0;const r=()=>{if(!v())return;if(o===t.points.length)return void(null!==t.delay?h=setTimeout(E,2*Number(t.delay)):E());const s=t.points[o];let a=!1,l=!1,u=null===t.delay;const d=c.oncanplay,p=()=>{if(!(!v()||!u||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(b(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!v())return;t.canvas.toBlob(s=>{if(v()&&!l){l=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,o){const r=n(e),s=URL.createObjectURL(t);if(r.thumbnailUrls.add(s),!o())return i(r.thumbnailUrls,s),null;const c=new Set([...r.thumbnailUrls,e.thumbnailUrl]);r.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&i(r.thumbnailUrls,n);return o()?s:null}(e,s,v);if(!c)return;e.emit("update",c,(o+1)/t.number),o++,Promise.resolve().then(r)}catch(c){U(c)}}})}catch(d){U(d)}}};b=()=>{c.oncanplay===p&&(c.oncanplay=d),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,null!==t.delay?(h=setTimeout(()=>{u=!0,p()},t.delay),v()||clearTimeout(h)):c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){U(f)}};Promise.resolve().then(r)}function j(o=!1){if(v()&&!m)if(clearTimeout(h),!t.loading&&c.duration){m=!0;try{const t=function(e,t){const{video:o}=t,{width:n,number:r,begin:i,end:s}=e.option,c=u(e.option),a=c.aspectHeight?o.videoHeight/o.videoWidth*n:e.option.height,d=(o,n)=>{try{e.errorHandle(o,n)}catch(r){throw t.reported=r,t.wasReported=!0,r}};e.option.height=a,e.option.begin=l(i,0,o.duration),e.option.end=l(s||o.duration,i,o.duration),d(e.option.end>e.option.begin,"End time must be greater than the start time"),d(Number.isFinite(o.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=r/e.duration,d(e.file&&o,"Please select the video file first"),d(!e.processing,"There is currently a task in progress, please wait a moment..."),d(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const h=e.creatScreenshotDate(),p=e.creatCanvas(),f=p.getContext("2d");return e.emit("canvas",p),{width:n,height:a,number:r,points:h,canvas:p,context:f,delay:c.delay}}(e,p);T(t)}catch(n){if(U(n,!o&&!(p.wasReported&&n===p.reported)),o)throw n}}else h=setTimeout(()=>j(),1e3),v()||clearTimeout(h)}p={video:c,promise:g,ready:j,fail:U,cancel(e){U(r(e),!1)},adopt:t=>!f&&!m&&c===e.video&&(o=t,!0)},t.job=p;try{for(const[e,t]of[["error",()=>{v()&&U(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>j()],["durationchange",()=>j()]])if(y.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!v())return s([()=>c.removeEventListener(e,t),...y.splice(0)]),g;c.error?U(new Error(`Unable to load video: media error ${c.error.code}`)):j(!0)}catch(k){throw U(k,!1),k}return g}const h=new WeakMap;function p(e){let t;for(const[n,r]of e.listeners.splice(0))try{e.input.removeEventListener(n,r)}catch(o){t||(t=o)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(o){t||(t=o)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function f(e,t){try{for(const[o,n]of t)e.listeners.push([o,n]),e.input.addEventListener(o,n)}catch(o){try{p(e)}catch{}throw o}}function m(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),a=c.fileInput;if(e.errorHandle(a instanceof Element,"The 'fileInput' is not a Element"),function(e,t){t(void 0===e.compatibility||"published-3.5"===e.compatibility||"workspace-4.4"===e.compatibility,"The compatibility option must be published-3.5 or workspace-4.4");const o=u(e).aspectHeight,n=o?["number","width","column","begin","end"]:["delay","number","width","height","column","begin","end"];for(const r of n)t("number"===typeof e[r],`The '${r}' is not a number`);e.number=l(e.number,10,1e3),e.width=l(e.width,10,1e3),e.column=l(e.column,1,1e3),o||(e.delay=l(Number(e.delay),10,1e3),e.height=l(e.height,10,1e3))}(c,(t,o)=>e.errorHandle(t,o)),!s())throw r("input setup superseded");const d=h.get(e);if(d&&(a===d.input||a===d.wrapper))return c.fileInput=d.input,e.option=c,c;const m={input:a,wrapper:null,position:"",listeners:[],callbacks:d?.callbacks||null};try{if("INPUT"===a.tagName&&"file"===a.type||(m.wrapper=a,m.position=a.style.position,m.input=document.createElement("input"),m.input.type="file",Object.assign(m.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),a.style.position="relative",a.appendChild(m.input)),!s())throw r("input setup superseded");if(m.callbacks&&f(m,m.callbacks),!s())throw r("input setup superseded")}catch(b){try{p(m)}catch{}throw b}return c.fileInput=m.input,h.set(e,m),e.option=c,d&&p(d),c}function b(e){const t=h.get(e);t&&(h.delete(e),p(t))}class y extends t{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},y.DEFAULTS,e)),this.video=y.creatVideo(),n(this).video=this.video,this.duration=0,this.inputChange=this.inputChange.bind(this),this.ondrop=this.ondrop.bind(this),function(e,t){const o=h.get(e);o.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],f(o,o.callbacks)}(this,y.ondragover)}catch(t){c(this,"construction failed");try{b(this)}catch{}try{a(this)}catch{}throw t}}static get DEFAULTS(){return{delay:300,number:60,width:160,height:90,column:10,begin:0,end:Number.NaN}}static ondragover(e){e.preventDefault()}ondrop(e){if(n(this).closed)return;e.preventDefault();const t=e.dataTransfer.files[0];this.loadVideo(t)}setup(e={}){return n(this).closed||m(this,e),this}static creatVideo(){const e=document.createElement("video");e.style.position="absolute",e.style.top="-9999px",e.style.left="-9999px",e.muted=!0,e.controls=!0;try{document.body.appendChild(e)}catch(t){throw e.parentNode&&e.parentNode.removeChild(e),t}return e}inputChange(e){if(n(this).closed)return;const{resetInput:t}=u(this.option),o=this.option.fileInput.files[0];this.loadVideo(o),t&&(e.target.value="")}loadVideo(e){!function(e,t){const o=n(e);if(!t||o.closed)return;const r=o.epoch,c=e.video,{delay:a}=u(e.option),l=c.canPlayType(t.type);if(e.errorHandle("maybe"===l||"probably"===l,`Playback of this file format is not supported: ${t.type}`),o.closed||o.epoch!==r)return;const d=URL.createObjectURL(t);if(o.sourceUrls.add(d),o.closed||o.epoch!==r)return void i(o.sourceUrls,d);const h=++o.epoch;!o.sourceUrl&&!e.file&&o.job?.adopt(h)||o.job?.cancel("source changed"),o.loading=!0;const p=()=>!o.closed&&o.epoch===h,f=o.sourceListeners,m=[];o.sourceListeners=m;let b,y=()=>{};const g=()=>clearTimeout(b);m.push(g);let v=!1;const w=()=>{if(!p()||v||c.currentSrc&&c.currentSrc!==d)return;v=!0,g(),o.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);o.job?o.job.fail(t):e.emit("error",t.message)},U=()=>{!p()||c.currentSrc&&c.currentSrc!==d||(o.loading=!1,o.job?.ready())};try{if(s(f.splice(0)),!p())return void i(o.sourceUrls,d);for(const[e,t]of[["error",w],["loadedmetadata",U]])if(y=()=>c.removeEventListener(e,t),m.push(y),c.addEventListener(e,t),!p())return s([y,...m.splice(0)]),void i(o.sourceUrls,d);if(e.videoUrl=d,e.file=t,e.emit("file",t),!p())return void i(o.sourceUrls,d);if(c.src=d,!p())return void i(o.sourceUrls,d);o.sourceUrl=d;for(const e of[...o.sourceUrls])e!==d&&i(o.sourceUrls,e);p()&&!v&&(null!==a?(b=setTimeout(()=>{g(),p()&&!v&&e.emit("video",c)},a),p()&&!v||g()):e.emit("video",c))}catch(E){p()&&(o.loading=!1,o.job?.cancel("source setup failed"));try{s([y,...m.splice(0)])}catch{}throw i(o.sourceUrls,d),E}}(this,e)}start(){return function(e){const t=n(e);if(t.closed){const e=Promise.reject(r("destroyed"));return e.catch(()=>{}),e}return e.errorHandle(!t.job,"There is currently a task in progress, please wait a moment..."),d(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:o,width:n,height:r,column:i,begin:s}=e,c=t/o,a=[s+c];for(;a.length<o;){const e=a[a.length-1];a.push(e+c)}return a.map((e,t)=>({time:e-c/2,x:t%i*n,y:Math.floor(t/i)*r}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:o,height:n,column:r}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=o*r,i.height=Math.ceil(t/r)*n+30,s.fillStyle="black",s.fillRect(0,0,i.width,i.height),s.font="14px Georgia",s.fillStyle="#fff",s.fillText(`From: https://artplayer.org/, Number: ${t}, Width: ${o}, Height: ${n}, Column: ${r}`,10,i.height-11),i}(this.option)}download(){if(n(this).closed)return this;this.errorHandle(this.file&&this.thumbnailUrl,"Download does not seem to be ready, please create preview first"),this.errorHandle(!this.processing,"There is currently a task in progress, please wait a moment...");const e=function(e,t){const o=document.createElement("a"),n=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;o.download=n,o.href=t;try{document.body.appendChild(o),o.click()}finally{o.parentNode&&o.parentNode.removeChild(o)}return n}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){c(this)&&s([()=>b(this),()=>a(this),()=>this.emit("destroy")])}}return y});
|