mstf-kit
Version:
一个现代化的 JavaScript/TypeScript 工具库,提供了丰富的常用工具函数
2 lines (1 loc) • 16.5 kB
JavaScript
;Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./audio.js");function t(t={}){var n,o,l,r,a,i,s,d,u,c,h,y,p,m,f,g,v,w;const k={renderUI:null!=(n=t.renderUI)&&n,container:null!=(o=t.container)?o:document.body,width:null!=(l=t.width)?l:"100%",height:null!=(r=t.height)?r:"80px",theme:null!=(a=t.theme)?a:"default",showVisualization:null==(i=t.showVisualization)||i,visualizationType:null!=(s=t.visualizationType)?s:"bars",autoplay:null!=(d=t.autoplay)&&d,loop:null!=(u=t.loop)&&u,playbackRate:null!=(c=t.playbackRate)?c:1,volume:null!=(h=t.volume)?h:1,sampleRate:null!=(y=t.sampleRate)?y:44100,onPlay:null!=(p=t.onPlay)?p:()=>{},onPause:null!=(m=t.onPause)?m:()=>{},onEnded:null!=(f=t.onEnded)?f:()=>{},onTimeUpdate:null!=(g=t.onTimeUpdate)?g:()=>{},onVolumeChange:null!=(v=t.onVolumeChange)?v:()=>{},onError:null!=(w=t.onError)?w:e=>console.error("Audio player error:",e)};let b="idle",x=null,E=null,C=null,L=null,R=null,M=null,T=null,P=null,S=null,z=null;function U(){x=new Audio,x.volume=k.volume,x.playbackRate=k.playbackRate,x.loop=k.loop,x.addEventListener("play",A),x.addEventListener("pause",B),x.addEventListener("ended",j),x.addEventListener("timeupdate",H),x.addEventListener("volumechange",$),x.addEventListener("error",I),k.renderUI&&function(){if(!x)return;const e=function(e){return e?"string"==typeof e?document.querySelector(e):e instanceof HTMLElement?e:e&&"value"in e&&e.value instanceof HTMLElement?e.value:null:null}(k.container);if(!e)return void console.error("无法找到播放器容器元素");if(P=document.createElement("div"),P.className=`mstf-audio-player theme-${k.theme}`,P.style.width=k.width,P.style.height=k.height,P.style.position="relative",P.style.display="flex",P.style.flexDirection="column",P.style.borderRadius="12px",P.style.overflow="hidden",P.style.boxShadow="dark"===k.theme?"0 8px 24px rgba(0, 0, 0, 0.3), 0 3px 8px rgba(0, 0, 0, 0.2)":"0 8px 24px rgba(0, 0, 0, 0.07), 0 3px 8px rgba(0, 0, 0, 0.05)",P.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',"dark"===k.theme?(P.style.backgroundColor="#1a1a1a",P.style.color="#fff"):"minimal"===k.theme?(P.style.backgroundColor="transparent",P.style.boxShadow="none"):(P.style.backgroundColor="#ffffff",P.style.color="#333"),k.showVisualization){R=document.createElement("canvas"),R.style.width="100%",R.style.height="minimal"===k.theme?"40px":"60%",R.style.backgroundColor="dark"===k.theme?"#1e1e1e":"transparent",R.style.transition="all 0.3s ease",P.appendChild(R),M=R.getContext("2d");const e=()=>{if(R&&M){const{width:e,height:t}=R.getBoundingClientRect();R.width=e*window.devicePixelRatio,R.height=t*window.devicePixelRatio,M.scale(window.devicePixelRatio,window.devicePixelRatio)}};e(),window.addEventListener("resize",e)}S=document.createElement("div"),S.className="mstf-player-controls",S.style.display="flex",S.style.alignItems="center",S.style.padding="10px 16px",S.style.height="minimal"===k.theme?"36px":"48px",S.style.backgroundColor="dark"===k.theme?"#252525":"#f8f8f8",S.style.borderTop="dark"===k.theme?"1px solid #333":"1px solid #eee";const t=document.createElement("button");t.className="mstf-player-play-btn",t.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="5 3 19 12 5 21 5 3"></polygon>\n </svg>',t.style.border="none",t.style.background="dark"===k.theme?"#444":"#eee",t.style.color="dark"===k.theme?"#fff":"#333",t.style.cursor="pointer",t.style.width="36px",t.style.height="36px",t.style.borderRadius="50%",t.style.display="flex",t.style.justifyContent="center",t.style.alignItems="center",t.style.marginRight="12px",t.style.transition="all 0.2s ease",t.onmouseover=()=>{t.style.background="dark"===k.theme?"#555":"#ddd",t.style.transform="scale(1.05)"},t.onmouseout=()=>{t.style.background="dark"===k.theme?"#444":"#eee",t.style.transform="scale(1)"},t.addEventListener("click",(async()=>{(null==x?void 0:x.paused)?await N.play():N.pause()}));const n=document.createElement("div");n.className="mstf-player-time",n.style.fontSize="13px",n.style.fontWeight="500",n.style.margin="0 12px",n.style.minWidth="90px",n.style.color="dark"===k.theme?"#aaa":"#666",n.textContent="0:00 / 0:00";const o=e=>{const t=Math.floor(e/60),n=Math.floor(e%60);return`${t}:${n<10?"0"+n:n}`},l=document.createElement("div");l.className="mstf-player-progress",l.style.flex="1",l.style.height="8px",l.style.borderRadius="4px",l.style.backgroundColor="dark"===k.theme?"#444":"#e0e0e0",l.style.cursor="pointer",l.style.position="relative",l.style.overflow="hidden",l.style.transition="height 0.2s ease",l.onmouseover=()=>{l.style.height="10px"},l.onmouseout=()=>{l.style.height="8px"};const r=document.createElement("div");r.className="mstf-player-progress-bar",r.style.height="100%",r.style.width="0%",r.style.borderRadius="4px",r.style.background="dark"===k.theme?"linear-gradient(90deg, #2ecc71, #1db954)":"linear-gradient(90deg, #0070f3, #00a2ff)",r.style.transition="width 0.1s linear",l.appendChild(r);const a=document.createElement("div");a.className="mstf-player-progress-thumb",a.style.width="12px",a.style.height="12px",a.style.borderRadius="50%",a.style.backgroundColor="dark"===k.theme?"#2ecc71":"#0070f3",a.style.position="absolute",a.style.top="50%",a.style.transform="translate(-50%, -50%) scale(0)",a.style.left="0%",a.style.opacity="0",a.style.transition="transform 0.2s, opacity 0.2s",a.style.boxShadow="0 0 5px rgba(0,0,0,0.2)",l.appendChild(a),l.addEventListener("mouseover",(()=>{a.style.opacity="1",a.style.transform="translate(-50%, -50%) scale(1)"})),l.addEventListener("mousemove",(e=>{const t=l.getBoundingClientRect(),n=Math.min(Math.max(0,(e.clientX-t.left)/t.width),1);a.style.left=100*n+"%"})),l.addEventListener("mouseout",(()=>{a.style.opacity="0",a.style.transform="translate(-50%, -50%) scale(0)"})),l.addEventListener("click",(e=>{if(!x)return;const t=l.getBoundingClientRect(),n=(e.clientX-t.left)/t.width;N.seek(n*x.duration)}));const i=document.createElement("div");i.className="mstf-player-volume",i.style.display="flex",i.style.alignItems="center",i.style.margin="0 8px";const s=document.createElement("button");s.className="mstf-player-volume-btn",s.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>\n <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>\n <path d="M19.07 4.93a10 10 0 0 1 0 14.14"></path>\n </svg>',s.style.border="none",s.style.background="dark"===k.theme?"#444":"#eee",s.style.width="36px",s.style.height="36px",s.style.borderRadius="50%",s.style.display="flex",s.style.justifyContent="center",s.style.alignItems="center",s.style.cursor="pointer",s.style.color="dark"===k.theme?"#fff":"#333",s.style.transition="all 0.2s ease",s.onmouseover=()=>{s.style.background="dark"===k.theme?"#555":"#ddd",s.style.transform="scale(1.05)"},s.onmouseout=()=>{s.style.background="dark"===k.theme?"#444":"#eee",s.style.transform="scale(1)"},s.addEventListener("click",(()=>{N.toggleMute()}));const d=document.createElement("input");d.type="range",d.min="0",d.max="1",d.step="0.1",d.value="1",d.style.width="0",d.style.height="4px",d.style.margin="0",d.style.appearance="none",d.style.background="dark"===k.theme?"#555":"#ddd",d.style.borderRadius="2px",d.style.outline="none",d.style.opacity="0",d.style.transition="all 0.2s ease",d.style.cursor="pointer",d.style.setProperty("--thumb-color","dark"===k.theme?"#1ed760":"#0070f3"),d.style.setProperty("--thumb-height","12px"),d.style.setProperty("--thumb-width","12px"),d.style.setProperty("--track-color","dark"===k.theme?"#555":"#ddd");const u=document.createElement("style");u.textContent='\n .mstf-player-volume input[type="range"]::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: var(--thumb-width);\n height: var(--thumb-height);\n border-radius: 50%;\n background: var(--thumb-color);\n cursor: pointer;\n }\n .mstf-player-volume input[type="range"]::-moz-range-thumb {\n width: var(--thumb-width);\n height: var(--thumb-height);\n border-radius: 50%;\n background: var(--thumb-color);\n cursor: pointer;\n border: none;\n }\n ',document.head.appendChild(u),i.addEventListener("mouseenter",(()=>{d.style.width="60px",d.style.marginLeft="8px",d.style.opacity="1"})),i.addEventListener("mouseleave",(()=>{d.style.width="0",d.style.marginLeft="0",d.style.opacity="0"})),d.addEventListener("input",(e=>{const t=e.target,n=parseFloat(t.value);N.setVolume(n)})),i.appendChild(s),i.appendChild(d),S.appendChild(t),S.appendChild(n),S.appendChild(l),"minimal"!==k.theme&&S.appendChild(i),P.appendChild(S),e.appendChild(P),k.showVisualization&&function(){if(x)try{if(k.sampleRate)try{E=new(window.AudioContext||window.webkitAudioContext)({sampleRate:k.sampleRate}),console.info(`音频播放器使用指定采样率: ${k.sampleRate}Hz`)}catch(e){console.warn(`使用指定采样率 ${k.sampleRate}Hz 失败:`,e),console.info("回退到浏览器默认采样率"),E=new(window.AudioContext||window.webkitAudioContext)}else E=new(window.AudioContext||window.webkitAudioContext);const o=E.sampleRate;switch(console.info(`音频播放器使用的实际采样率: ${o}Hz`),C=E.createAnalyser(),k.visualizationType){case"waveform":C.fftSize=2048;break;case"bars":C.fftSize=256;break;case"circle":C.fftSize=1024}try{L=E.createMediaElementSource(x),L.connect(C),C.connect(E.destination)}catch(t){if(console.warn("音频节点连接失败,可能是采样率不匹配:",t),L)try{L.disconnect()}catch(n){}if(C)try{C.disconnect()}catch(n){}if(E)try{E.close()}catch(n){}if(E=null,L=null,C=null,t instanceof Error&&t.message&&(t.message.includes("different sample-rate")||t.message.includes("采样率")))return void console.info("已降级为基本播放模式,音频可视化功能将不可用");throw t}!function(){if(!C||!M||!R)return;const e=C.frequencyBinCount,t=new Uint8Array(e);!function e(){if(!C||!M||!R||"destroyed"===b)return;T=requestAnimationFrame(e);const n=R.width/window.devicePixelRatio,o=R.height/window.devicePixelRatio;switch(M.clearRect(0,0,n,o),k.visualizationType){case"waveform":!function(e,t,n){if(!C||!M)return;C.getByteTimeDomainData(e),M.lineWidth=2,M.strokeStyle="dark"===k.theme?"#1db954":"#007bff",M.beginPath();const o=t/e.length;let l=0;for(let r=0;r<e.length;r++){const t=e[r]/128*n/2;0===r?M.moveTo(l,t):M.lineTo(l,t),l+=o}M.lineTo(t,n/2),M.stroke()}(t,n,o);break;case"bars":!function(e,t,n){if(!C||!M)return;C.getByteFrequencyData(e);const o=t/e.length*2.5;let l=0;for(let r=0;r<e.length;r++){const t=e[r]/255*n;let a=r/e.length*360;"minimal"!==k.theme?M.fillStyle="dark"===k.theme?`hsl(${a}, 70%, 60%)`:`hsl(${a}, 70%, 50%)`:M.fillStyle="#007bff",M.fillRect(l,n-t,o,t),l+=o+1}}(t,n,o);break;case"circle":!function(e,t,n){if(!C||!M)return;C.getByteFrequencyData(e);const o=t/2,l=n/2,r=Math.max(5,.85*Math.min(o,l));M.beginPath(),M.arc(o,l,Math.max(2,r/4),0,2*Math.PI),M.fillStyle="dark"===k.theme?"#333":"#f5f5f5",M.fill();for(let a=0;a<e.length;a++){const t=e[a]/255,n=a/e.length*2*Math.PI,i=Math.max(2,r/3),s=Math.max(i+1,i+.7*r*t),d=o+i*Math.cos(n),u=l+i*Math.sin(n),c=o+s*Math.cos(n),h=l+s*Math.sin(n);let y=a/e.length*360;M.strokeStyle="dark"===k.theme?`hsl(${y}, 70%, 60%)`:`hsl(${y}, 70%, 50%)`,M.lineWidth=2,M.beginPath(),M.moveTo(d,u),M.lineTo(c,h),M.stroke()}}(t,n,o)}}()}()}catch(o){if(console.error("初始化音频上下文失败:",o),k.onError(new Error("初始化音频可视化失败,但不影响播放功能")),L)try{L.disconnect()}catch(n){}if(C)try{C.disconnect()}catch(n){}if(E)try{E.close()}catch(n){}E=null,L=null,C=null}}();const c=()=>{if(!x)return;const e=x.currentTime/(x.duration||1);r.style.width=100*e+"%",a.style.left=100*e+"%",n.textContent=`${o(x.currentTime)} / ${o(x.duration||0)}`,(null==x?void 0:x.paused)?t.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="5 3 19 12 5 21 5 3"></polygon>\n </svg>':t.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <rect x="6" y="4" width="4" height="16"></rect>\n <rect x="14" y="4" width="4" height="16"></rect>\n </svg>',x.muted||0===x.volume?s.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>\n <line x1="23" y1="9" x2="17" y2="15"></line>\n <line x1="17" y1="9" x2="23" y2="15"></line>\n </svg>':x.volume<.5?s.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>\n <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>\n </svg>':s.innerHTML='<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">\n <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon>\n <path d="M15.54 8.46a5 5 0 0 1 0 7.07"></path>\n <path d="M19.07 4.93a10 10 0 0 1 0 14.14"></path>\n </svg>',d.value=x.muted?"0":x.volume.toString()};x.addEventListener("timeupdate",c),x.addEventListener("play",c),x.addEventListener("pause",c),x.addEventListener("volumechange",c),c()}()}function A(){b="playing",k.onPlay(),k.renderUI&&k.showVisualization&&"suspended"===(null==E?void 0:E.state)&&E.resume()}function B(){b="paused",k.onPause()}function j(){b="ended",k.onEnded()}function H(){x&&k.onTimeUpdate(x.currentTime,x.duration)}function $(){x&&k.onVolumeChange(x.volume,x.muted)}function I(){b="error",k.onError(new Error("音频播放错误"))}const N={async load(t){if(x||U(),!x)throw new Error("音频元素初始化失败");z&&z.startsWith("blob:")&&URL.revokeObjectURL(z),b="loading";try{if("string"==typeof t)if(t.startsWith("data:audio")){const n=t.split(",")[0].split(":")[1].split(";")[0],o=t.split(",")[1],l=e.base64ToAudioBlob(o,n);z=URL.createObjectURL(l)}else z=t;else z=URL.createObjectURL(t);if(x.src=z,await x.load(),k.autoplay)try{await N.play()}catch(n){console.warn("自动播放失败,可能需要用户交互:",n)}b="idle"}catch(n){throw b="error",k.onError(n instanceof Error?n:new Error("加载音频失败")),n}},async play(){if(!x)throw new Error("播放器未初始化");try{await x.play()}catch(e){throw b="error",k.onError(e instanceof Error?e:new Error("播放失败")),e}},pause(){x&&x.pause()},stop(){x&&(x.pause(),x.currentTime=0,b="idle")},seek(e){x&&(x.currentTime=Math.max(0,Math.min(e,x.duration||0)))},setVolume(e){x&&(x.volume=Math.max(0,Math.min(e,1)))},setPlaybackRate(e){x&&(x.playbackRate=Math.max(.5,Math.min(e,2)))},toggleMute(){x&&(x.muted=!x.muted)},getState:()=>b,getCurrentTime:()=>(null==x?void 0:x.currentTime)||0,getDuration:()=>(null==x?void 0:x.duration)||0,getVolume:()=>(null==x?void 0:x.volume)||0,isMuted:()=>(null==x?void 0:x.muted)||!1,getProgress:()=>x&&x.duration?x.currentTime/x.duration:0,isPlaying:()=>"playing"===b,destroy(){null!==T&&(cancelAnimationFrame(T),T=null),z&&z.startsWith("blob:")&&URL.revokeObjectURL(z),x&&(x.removeEventListener("play",A),x.removeEventListener("pause",B),x.removeEventListener("ended",j),x.removeEventListener("timeupdate",H),x.removeEventListener("volumechange",$),x.removeEventListener("error",I),x.pause(),x.src="",x.load()),L&&L.disconnect(),C&&C.disconnect(),E&&E.close(),P&&P.parentNode&&P.parentNode.removeChild(P),b="destroyed",x=null,E=null,C=null,L=null,R=null,M=null,T=null,P=null,S=null,z=null}};return U(),N}exports.createAudioPlayer=t,exports.playAudio=async function(e,n={}){const o=t({autoplay:void 0===n.autoplay||n.autoplay,loop:n.loop||!1,volume:n.volume||1,onEnded:n.onEnded});if(await o.load(e),!1!==n.autoplay)try{await o.play()}catch(l){console.warn("自动播放失败,可能需要用户交互:",l)}return o};