mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
8 lines
11 KiB
JavaScript
8 lines
11 KiB
JavaScript
/*!
|
|
* artplayer-tool-thumbnail.js v4.4.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?(e.ArtplayerToolThumbnail=t(),define(function(){return e.ArtplayerToolThumbnail})):(e="undefined"!==typeof globalThis?globalThis:e||self).ArtplayerToolThumbnail=t()}(this,function(){"use strict";class e{on(e,t,n){const r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this}once(e,t,n){const r=this;function o(...i){r.off(e,o),t.apply(n,i)}return o._=t,this.on(e,o,n)}emit(e,...t){const n=((this.e||(this.e={}))[e]||[]).slice();for(let r=0;r<n.length;r+=1)n[r].fn.apply(n[r].ctx,t);return this}off(e,t){const n=this.e||(this.e={}),r=n[e],o=[];if(r&&t)for(let i=0,s=r.length;i<s;i+=1)r[i].fn!==t&&r[i].fn._!==t&&o.push(r[i]);return o.length?n[e]=o:delete n[e],this}}const t=new WeakMap;function n(e){return t.has(e)||t.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}),t.get(e)}function r(e){const t=new Error(`Thumbnail task cancelled: ${e}`);return t.name="AbortError",t}function o(e,t){t&&(e.delete(t),URL.revokeObjectURL(t))}function i(e){let t;for(const r of e)try{r()}catch(n){t||(t=n)}if(t)throw t}function s(e,t="destroyed"){const r=n(e);return!r.closed&&(r.closed=!0,r.epoch++,r.job?.cancel(t),!0)}function c(e){const t=n(e),r=new Set([...t.sourceUrls,e.videoUrl]),s=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));i([...t.sourceListeners.splice(0),...c,...[...r].map(e=>()=>o(t.sourceUrls,e)),...[...s].map(e=>()=>o(t.thumbnailUrls,e))])}function a(e,t,n){return Math.max(Math.min(e,Math.max(t,n)),Math.min(t,n))}function l(e,t){let s=t.epoch;const c=e.video;let l,u,d,h,p=!1,f=!1,m=()=>{};const b=[],y=new Promise((e,t)=>{l=e,u=t});y.catch(()=>{});const g=()=>!p&&!t.closed&&t.epoch===s&&t.job===h;function v(){return!p&&(p=!0,t.job===h&&(t.job=null,e.processing=!1),i([()=>clearTimeout(d),m,...b.splice(0)]),!0)}function w(t,n=!0){if(!p){try{v()}catch(r){t||(t=r)}if(n)try{e.emit("error",t?.message)}catch(o){t=o}u(t)}}function U(t){if(!g())return;f=!0,e.processing=!0;let r=0;const i=()=>{if(!g())return;if(r===t.points.length)return void function(){if(g())try{v(),e.emit("done"),l()}catch(t){let r=t;try{e.emit("error",t?.message)}catch(n){r=n}u(r)}}();const s=t.points[r];let a=!1,d=!1;const h=c.oncanplay,p=()=>{if(!(!g()||a||c.seeking||void 0!==c.readyState&&c.readyState<2)){a=!0;try{if(m(),t.context.drawImage(c,s.x,s.y,t.width,t.height),!g())return;t.canvas.toBlob(s=>{if(g()&&!d){d=!0;try{if(!s)throw new Error("Unable to create thumbnail image");const c=function(e,t,r){const i=n(e),s=URL.createObjectURL(t);if(i.thumbnailUrls.add(s),!r())return o(i.thumbnailUrls,s),null;const c=new Set([...i.thumbnailUrls,e.thumbnailUrl]);i.thumbnailUrl=s,e.thumbnailUrl=s;for(const n of c)n&&n!==s&&o(i.thumbnailUrls,n);return r()?s:null}(e,s,g);if(!c)return;e.emit("update",c,(r+1)/t.number),r++,Promise.resolve().then(i)}catch(c){w(c)}}})}catch(l){w(l)}}};m=()=>{c.oncanplay===p&&(c.oncanplay=h),c.removeEventListener("seeked",p)};try{c.oncanplay=p,c.addEventListener("seeked",p),c.currentTime=s.time,c.readyState>=2&&!c.seeking&&Promise.resolve().then(p)}catch(f){w(f)}};Promise.resolve().then(i)}function E(n=!1){if(g()&&!f)if(clearTimeout(d),!t.loading&&c.duration){f=!0;try{const t=function(e,t){const{video:n}=t,{width:r,number:o,begin:i,end:s}=e.option,c=n.videoHeight/n.videoWidth*r,l=(n,r)=>{try{e.errorHandle(n,r)}catch(o){throw t.reported=o,t.wasReported=!0,o}};e.option.height=c,e.option.begin=a(i,0,n.duration),e.option.end=a(s||n.duration,i,n.duration),l(e.option.end>e.option.begin,"End time must be greater than the start time"),l(Number.isFinite(n.duration),"Video duration must be finite"),e.duration=e.option.end-e.option.begin,e.density=o/e.duration,l(e.file&&n,"Please select the video file first"),l(!e.processing,"There is currently a task in progress, please wait a moment..."),l(e.density<=1,`The preview density cannot be greater than 1, but got ${e.density}`);const u=e.creatScreenshotDate(),d=e.creatCanvas(),h=d.getContext("2d");return e.emit("canvas",d),{width:r,height:c,number:o,points:u,canvas:d,context:h}}(e,h);U(t)}catch(r){if(w(r,!n&&!(h.wasReported&&r===h.reported)),n)throw r}}else d=setTimeout(()=>E(),1e3),g()||clearTimeout(d)}h={video:c,promise:y,ready:E,fail:w,cancel(e){w(r(e),!1)},adopt:t=>!p&&!f&&c===e.video&&(s=t,!0)},t.job=h;try{for(const[e,t]of[["error",()=>{g()&&w(new Error(`Unable to load video: media error ${c.error?.code||0}`))}],["loadedmetadata",()=>E()],["durationchange",()=>E()]])if(b.push(()=>c.removeEventListener(e,t)),c.addEventListener(e,t),!g())return i([()=>c.removeEventListener(e,t),...b.splice(0)]),y;c.error?w(new Error(`Unable to load video: media error ${c.error.code}`)):E(!0)}catch(j){throw w(j,!1),j}return y}const u=new WeakMap;function d(e){let t;for(const[r,o]of e.listeners.splice(0))try{e.input.removeEventListener(r,o)}catch(n){t||(t=n)}if(e.wrapper){try{e.input.parentNode&&e.input.parentNode.removeChild(e.input)}catch(n){t||(t=n)}"relative"===e.wrapper.style.position&&(e.wrapper.style.position=e.position)}if(t)throw t}function h(e,t){try{for(const[n,r]of t)e.listeners.push([n,r]),e.input.addEventListener(n,r)}catch(n){try{d(e)}catch{}throw n}}function p(e){const t=u.get(e);t&&(u.delete(e),d(t))}class f extends e{constructor(e={}){super(),this.processing=!1,this.option={};try{this.setup(Object.assign({},f.DEFAULTS,e)),this.video=f.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 n=u.get(e);n.callbacks=[["change",e.inputChange],["dragover",t],["drop",e.ondrop]],h(n,n.callbacks)}(this,f.ondragover)}catch(t){s(this,"construction failed");try{p(this)}catch{}try{c(this)}catch{}throw t}}static get DEFAULTS(){return{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||function(e,t){const o=n(e),i=++o.inputEpoch,s=()=>!o.closed&&o.inputEpoch===i,c=Object.assign({},e.option,t),l=c.fileInput;e.errorHandle(l instanceof Element,"The 'fileInput' is not a Element");for(const n of["number","width","column","begin","end"])e.errorHandle("number"===typeof c[n],`The '${n}' is not a number`);if(c.number=a(c.number,10,1e3),c.width=a(c.width,10,1e3),c.column=a(c.column,1,1e3),!s())throw r("input setup superseded");const p=u.get(e);if(p&&(l===p.input||l===p.wrapper))return c.fileInput=p.input,e.option=c,c;const f={input:l,wrapper:null,position:"",listeners:[],callbacks:p?.callbacks||null};try{if("INPUT"===l.tagName&&"file"===l.type||(f.wrapper=l,f.position=l.style.position,f.input=document.createElement("input"),f.input.type="file",Object.assign(f.input.style,{position:"absolute",width:"100%",height:"100%",left:"0",top:"0",right:"0",bottom:"0",opacity:"0"}),l.style.position="relative",l.appendChild(f.input)),!s())throw r("input setup superseded");if(f.callbacks&&h(f,f.callbacks),!s())throw r("input setup superseded")}catch(m){try{d(f)}catch{}throw m}c.fileInput=f.input,u.set(e,f),e.option=c,p&&d(p)}(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 t=this.option.fileInput.files[0];this.loadVideo(t),e.target.value=""}loadVideo(e){!function(e,t){const r=n(e);if(!t||r.closed)return;const s=r.epoch,c=e.video,a=c.canPlayType(t.type);if(e.errorHandle("maybe"===a||"probably"===a,`Playback of this file format is not supported: ${t.type}`),r.closed||r.epoch!==s)return;const l=URL.createObjectURL(t);if(r.sourceUrls.add(l),r.closed||r.epoch!==s)return void o(r.sourceUrls,l);const u=++r.epoch;!r.sourceUrl&&!e.file&&r.job?.adopt(u)||r.job?.cancel("source changed"),r.loading=!0;const d=()=>!r.closed&&r.epoch===u,h=r.sourceListeners,p=[];r.sourceListeners=p;let f=()=>{},m=!1;const b=()=>{if(!d()||m||c.currentSrc&&c.currentSrc!==l)return;m=!0,r.loading=!1;const t=new Error(`Unable to load video: media error ${c.error?.code||0}`);r.job?r.job.fail(t):e.emit("error",t.message)},y=()=>{!d()||c.currentSrc&&c.currentSrc!==l||(r.loading=!1,r.job?.ready())};try{if(i(h.splice(0)),!d())return void o(r.sourceUrls,l);for(const[e,t]of[["error",b],["loadedmetadata",y]])if(f=()=>c.removeEventListener(e,t),p.push(f),c.addEventListener(e,t),!d())return i([f,...p.splice(0)]),void o(r.sourceUrls,l);if(e.videoUrl=l,e.file=t,e.emit("file",t),!d())return void o(r.sourceUrls,l);if(c.src=l,!d())return void o(r.sourceUrls,l);r.sourceUrl=l;for(const e of[...r.sourceUrls])e!==l&&o(r.sourceUrls,e);d()&&e.emit("video",c)}catch(g){d()&&(r.loading=!1,r.job?.cancel("source setup failed"));try{i([f,...p.splice(0)])}catch{}throw o(r.sourceUrls,l),g}}(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..."),l(e,t)}(this)}creatScreenshotDate(){return function(e,t){const{number:n,width:r,height:o,column:i,begin:s}=e,c=t/n,a=[s+c];for(;a.length<n;){const e=a[a.length-1];a.push(e+c)}return a.map((e,t)=>({time:e-c/2,x:t%i*r,y:Math.floor(t/i)*o}))}(this.option,this.duration)}creatCanvas(){return function(e){const{number:t,width:n,height:r,column:o}=e,i=document.createElement("canvas"),s=i.getContext("2d");return i.width=n*o,i.height=Math.ceil(t/o)*r+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: ${n}, Height: ${r}, Column: ${o}`,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 n=document.createElement("a"),r=`${function(e){const t=e.split(".");return t.pop(),t.join(".")}(e.name)}.png`;n.download=r,n.href=t;try{document.body.appendChild(n),n.click()}finally{n.parentNode&&n.parentNode.removeChild(n)}return r}(this.file,this.thumbnailUrl);return this.emit("download",e),this}errorHandle(e,t){if(!e)throw this.emit("error",t),new Error(t)}destroy(){s(this)&&i([()=>p(this),()=>c(this),()=>this.emit("destroy")])}}return f});
|