@splicemood/react-music-player
Version:
The music player that sync all settings between multiple tabs on a single client-side browser session
2 lines (1 loc) • 14.4 kB
JavaScript
import{useCallback as i,useEffect as y,useRef as fA,useState as r}from"react";import{useDebouncedState as aA,useLocalStorage as jA}from"@mantine/hooks";import{createContext as iA}from"react";var WA=iA(void 0);var H=0,E=1,bA=0.5,pA=100,DA=50,MA=200,LA=65536,CA=5,oA=3072;var rA;(($)=>{$[$.PlayAll=0]="PlayAll";$[$.LoopAll=1]="LoopAll";$[$.LoopCue=2]="LoopCue"})(rA||={});import{parseWebStream as uA}from"music-metadata";function lA(q){return new Promise((S,x)=>{let $=new Audio,N=()=>{$.removeEventListener("loadedmetadata",X),$.removeEventListener("error",F)},X=()=>{S($.duration),N()},F=(v)=>{x(v),N()};$.addEventListener("loadedmetadata",X),$.addEventListener("error",F),$.src=q})}var sA=({src:q,duration:S})=>{if(S)return S;return new Promise(async(x,$)=>{try{let N=await fetch(q,{headers:{Range:"bytes=0-"+oA}});if(!N.ok)$(`HTTP error! status: ${N.status}`);if(N.headers.get("Accept-Ranges")!=="bytes")$("Server does not support range requests.");let F=N.body;if(F===null){$("webStream is null");return}let v=await uA(F,"audio/mpeg");if(v.format.duration)x(v.format.duration);else{let I=await lA(q);x(I)}}catch(N){$(N)}})},m=(q)=>Promise.all(q.map(sA)).catch((S)=>{console.error("Errors occurred while fetching durations:",S)}),EA=(q)=>{return Math.round(q)/pA};import{jsx as tA}from"react/jsx-runtime";var qA=window.localStorage,F0=({children:q})=>{let[S,x]=r(!1),[$,N]=aA(void 0,MA),[X,F]=r([]),[v,I]=r([]),Y=qA.getItem("player-playlist-id"),[b,O]=jA({key:"player-playlist-id",defaultValue:Y||void 0}),Z=qA.getItem("player-current-track-index"),p=Z?Number(Z):H,[_,Q]=r(p),[V,U]=r(!1),[KA,j]=r(H),[s,XA]=jA({key:"player-updated-time",defaultValue:0,getInitialValueInEffect:!0}),[o,FA]=jA({key:"player-volume",defaultValue:bA,getInitialValueInEffect:!0}),[k,hA]=r(DA),[gA,a]=r(H),[D,f]=r(H),[YA,w]=jA({key:"player-muted",defaultValue:!1}),[P,ZA]=jA({key:"player-shuffle",defaultValue:!1}),[R,xA]=jA({key:"player-repeat",defaultValue:0}),[t,B]=jA({key:"player-shuffle-queue",defaultValue:[]}),A=fA(new Audio),M=fA(null),_A=i((G,J)=>{if(M.current)M.current.postMessage({type:G,value:J})},[M.current]);y(()=>{_A("UPDATE_TRACK",_)},[_]),y(()=>{if(V)if(!P)B([]);else B((G)=>{return[_,...G]})},[P]),y(()=>{let G=Math.round(o*100);hA(G)},[o]),y(()=>{if(A.current!==null&&S){let G=A.current.currentTime;if(Math.abs(G-s)>1)A.current.currentTime=s,j(s)}},[s]),y(()=>{if(A.current&&X.length>H){let G=X[_];A.current.src=G?.src,A.current.currentTime=H}},[X,_]),y(()=>{if(A.current&&X.length>H){let G=qA.getItem("player-current-track-index"),J=G&&Number(G)===_;if(!J)qA.setItem("player-current-track-index",String(_));if(V)_A("PAUSE_PLAYING"),N(!0),A.current.play().then(()=>{if(A.current&&J){let C=qA.getItem("player-sync-time");A.current.currentTime=Number(C)}N(void 0)}).catch(()=>{N(void 0)});else A.current.pause()}},[_,X,V]),y(()=>{if(A.current)A.current.volume=o,A.current.muted=YA},[o,YA]);let UA=(G)=>{let J=Array(G.length).fill(0);if(I(J),G.length>0)m(G).then((C)=>{if(Array.isArray(C))I(C)})},BA=(G)=>{m([G]).then((J)=>{if(J)I((C)=>{return[...C,J[0]]})})},NA=(G)=>{if(String(G)!==b)Q(H),O(G)};y(()=>{if(X.length>0)m(X).then((G)=>{if(Array.isArray(G))I(G)})},[X]);let e=()=>{if(A.current!==null){let G=A.current.currentTime;j(G),_A("UPDATE_TIME",G),qA.setItem("player-sync-time",String(G))}},wA=i(()=>{if(A.current!==null)f(A.current.duration)},[A.current?.duration]),d=i(()=>{if(X.length===H)return;U(!0)},[X]),T=i(()=>U(!1),[U]),OA=i(()=>U((G)=>!G),[U]),AA=i((G=!1)=>{if(X.length===H)return;let J;if(P){let n=X.map((HA,nA)=>nA).filter((HA)=>HA!==_);J=n[Math.floor(Math.random()*n.length)],B((HA)=>{return HA=HA.slice(H,LA),[J,...HA]})}else J=_+E;if(J>=X.length){if(U(G),J=H,!G)return}if(a(H),!V)U(!0);Q(J)},[X,_,P,V,Q]),GA=i(()=>{switch(R){case 0:AA();break;case 1:AA(!0);break;case 2:A.current?.play();break}},[R,AA]),QA=i(()=>{if(X.length===H||!A.current)return;if(KA>CA){A.current.currentTime=H;return}let G=H;if(P){if(t.length===E)return;B((J)=>{let[,...C]=J;return G=C[H],C})}else G=_-E;if(G<H)G=X.length-E;Q(G),a(H),U(!0)},[X,t,_,KA,XA]),PA=(G)=>Q(G),SA=(G)=>FA(G),kA=(G)=>{let J=EA(G);FA(J)},RA=()=>w(!0),TA=()=>w(!1),mA=()=>w((G)=>!G),vA=()=>ZA(!0),IA=()=>ZA(!1),yA=()=>ZA((G)=>!G),VA=()=>{xA((G)=>{if(G===0)return 1;if(G===1)return 2;if(G===2)return 0;return 0})},cA=(G)=>{F((J)=>[...J,G]),BA(G)},z=(G,J)=>{let C=J!==void 0||V;if(T(),B([H]),a(H),F(G),UA(G),C)Q(J||H),d()},K=i((G)=>{let{data:J}=G;switch(J?.type){case"PAUSE_PLAYING":U(!1);break;case"UPDATE_TIME":if(!V&&J.value!==H)j(J.value);break;case"UPDATE_TRACK":if(!V)Q(J.value);break}},[V,Q,U,_]),L=()=>{if(A.current&&A.current.buffered.length>0){let G=A.current.buffered.end(A.current.buffered.length-1);a(G/A.current.duration*100)}};y(()=>{if(A.current)A.current.addEventListener("ended",GA);return()=>{A.current?.removeEventListener("ended",GA)}},[GA]),y(()=>{if(A.current){let G=qA.getItem("player-sync-time"),J=Number(G);XA(J),A.current.currentTime=J,j(J),U(!1),A.current.preload="metadata",A.current.addEventListener("progress",L),A.current.addEventListener("timeupdate",e),A.current.addEventListener("durationchange",wA),x(!0)}if("BroadcastChannel"in window&&!M.current)M.current=new BroadcastChannel("global-audio-player"),M.current.onmessage=K;return()=>{if(A.current)A.current.pause(),A.current.removeEventListener("progress",L),A.current.removeEventListener("timeupdate",e),A.current.removeEventListener("durationchange",wA),A.current=null}},[]);let W={play:d,pause:T,togglePlayPause:OA,next:AA,previous:QA,setVolume:SA,setVolumePercent:kA,mute:RA,unmute:TA,toggleMute:mA,shuffleOn:vA,shuffleOff:IA,toggleShuffle:yA,toggleLoop:VA,addToPlaylist:cA,replacePlaylist:z,setCurrentTrack:PA,setUpdateTime:XA,setPlaylistId:NA,volume:o,volumePercent:k,bufferedPercentage:gA,currentPlaylistId:b,maxTime:D,playlist:X,durations:v,isPlaying:V,currentTime:KA,currentTrackIndex:_,repeatMode:R,isShuffled:P,isLoading:$,isMuted:YA};return tA(WA.Provider,{value:W,children:q})};import{useCallback as u,useEffect as l,useRef as dA,useState as h}from"react";import{useDebouncedState as eA}from"@mantine/hooks";import{jsx as A0}from"react/jsx-runtime";var $A=window.localStorage,L0=({children:q})=>{let[S,x]=eA(void 0,MA),[$,N]=h([]),[X,F]=h([]),[v,I]=h(""),[Y,b]=h(H),[O,Z]=h(!1),[p,_]=h(H),[Q,V]=h(0),U=$A.getItem("player-volume"),KA=U?Number(U):bA,[j,s]=h(KA),[XA,o]=h(DA),[FA,k]=h(H),[hA,gA]=h(H),a=$A.getItem("player-muted")==="true",[D,f]=h(a),YA=$A.getItem("player-shuffle")==="true",[w,P]=h(YA),ZA=$A.getItem("player-repeat"),[R,xA]=h(Number(ZA)||0),[t,B]=h([]),A=dA(new Audio),M=dA(null),_A=u((W,G)=>{if(M.current)M.current.postMessage({type:W,value:G})},[M.current]);l(()=>{if(O)if(!w)B([]);else B((W)=>{return[Y,...W]});$A.setItem("player-shuffle",String(w))},[w]),l(()=>{let W=Math.round(j*100);o(W)},[j]),l(()=>{if(A.current!==null)A.current.currentTime=Q,_(Q)},[Q]),l(()=>{if(A.current&&$.length>H){let W=$[Y];A.current.src=W?.src,A.current.currentTime=H}},[$,Y]),l(()=>{if(A.current&&$.length>H)if(O)_A("PAUSE_PLAYING"),x(!0),A.current.play().then(()=>{x(void 0)}).catch(()=>{x(void 0)});else A.current.pause()},[Y,$,O]),l(()=>{if(A.current)A.current.volume=j,A.current.muted=D},[j,D]);let UA=(W)=>{let G=Array(W.length).fill(0);if(F(G),W.length>0)m(W).then((J)=>{if(Array.isArray(J))F(J)})},BA=(W)=>{m([W]).then((G)=>{if(G)F((J)=>{return[...J,G[0]]})})},NA=u(()=>{if(A.current!==null)gA(A.current.duration)},[A.current?.duration]),e=u(()=>{if($.length===H)return;Z(!0)},[$]),wA=u(()=>Z(!1),[Z]),d=u(()=>Z((W)=>!W),[Z]),T=u((W=!1)=>{if($.length===H)return;let G;if(w){let C=$.map((n,HA)=>HA).filter((n)=>n!==Y);G=C[Math.floor(Math.random()*C.length)],B((n)=>{return n=n.slice(H,LA),[G,...n]})}else G=Y+E;if(G>=$.length){if(Z(W),G=H,!W)return}if(k(H),!O)Z(!0);b(G)},[$,Y,w,O,b]),OA=u(()=>{switch(R){case 0:T();break;case 1:T(!0);break;case 2:A.current?.play();break}},[R,T]),AA=u(()=>{if($.length===H||!A.current)return;if(p>CA){A.current.currentTime=H;return}let W=H;if(w){if(t.length===E)return;B((G)=>{let[,...J]=G;return W=J[H],J})}else W=Y-E;if(W<H)W=$.length-E;b(W),k(H),Z(!0)},[$,t,Y,p,V]),GA=(W)=>b(W),QA=(W)=>{s(W),$A.setItem("player-volume",String(W))},PA=(W)=>{let G=EA(W);QA(G)},SA=()=>f(!0),kA=()=>f(!1),RA=()=>f((W)=>!W),TA=()=>P(!0),mA=()=>P(!1),vA=()=>P((W)=>!W),IA=()=>{xA((W)=>{let G=0;if(W===0)G=1;if(W===1)G=2;if(W===2)G=0;return $A.setItem("player-repeat",String(G)),G})},yA=(W)=>{N((G)=>[...G,W]),BA(W)},VA=(W,G)=>{let J=G!==void 0||O;if(wA(),B([H]),k(H),N(W),UA(W),J)b(G||H),e()};l(()=>{$A.setItem("player-muted",String(D))},[D]);let cA=u((W)=>{let{data:G}=W;switch(G?.type){case"PAUSE_PLAYING":Z(!1);break}},[O,b,Z,Y]),z=()=>{if(A.current&&A.current.buffered.length>0){let W=A.current.buffered.end(A.current.buffered.length-1);k(W/A.current.duration*100)}},K=()=>{if(A.current!==null){let W=A.current.currentTime;_(W)}};l(()=>{if(A.current)A.current.addEventListener("ended",OA);return()=>{A.current?.removeEventListener("ended",OA)}},[OA]),l(()=>{if(A.current){if(A.current.muted=D,A.current.volume=j,Z(!1),A.current.preload="metadata","BroadcastChannel"in window&&!M.current)M.current=new BroadcastChannel("playpause-audio-player"),M.current.onmessage=cA;A.current.addEventListener("progress",z),A.current.addEventListener("timeupdate",K),A.current.addEventListener("durationchange",NA)}return()=>{if(A.current)A.current.pause(),A.current.removeEventListener("progress",z),A.current.removeEventListener("timeupdate",K),A.current.removeEventListener("durationchange",NA),A.current=null}},[]);let L={play:e,pause:wA,togglePlayPause:d,next:T,previous:AA,setVolume:QA,setVolumePercent:PA,mute:SA,unmute:kA,toggleMute:RA,shuffleOn:TA,shuffleOff:mA,toggleShuffle:vA,toggleLoop:IA,addToPlaylist:yA,replacePlaylist:VA,setCurrentTrack:GA,setUpdateTime:V,setPlaylistId:I,volume:j,volumePercent:XA,bufferedPercentage:FA,currentPlaylistId:v,maxTime:hA,playlist:$,durations:X,isPlaying:O,currentTime:p,currentTrackIndex:Y,repeatMode:R,isShuffled:w,isLoading:S,isMuted:D};return A0(WA.Provider,{value:L,children:q})};import{useCallback as zA,useEffect as c,useRef as G0,useState as g}from"react";import{useDebouncedState as H0}from"@mantine/hooks";import{jsx as W0}from"react/jsx-runtime";var JA=window.localStorage,k0=({children:q})=>{let[S,x]=H0(void 0,MA),[$,N]=g([]),[X,F]=g([]),[v,I]=g(""),[Y,b]=g(H),[O,Z]=g(!1),[p,_]=g(H),[Q,V]=g(0),U=JA.getItem("player-volume"),KA=U?Number(U):bA,[j,s]=g(KA),[XA,o]=g(DA),[FA,k]=g(H),[hA,gA]=g(H),a=JA.getItem("player-muted")==="true",[D,f]=g(a),YA=JA.getItem("player-shuffle")==="true",[w,P]=g(YA),ZA=JA.getItem("player-repeat"),[R,xA]=g(Number(ZA)||0),[t,B]=g([]),A=G0(new Audio);c(()=>{if(O)if(!w)B([]);else B((z)=>{return[Y,...z]});JA.setItem("player-shuffle",String(w))},[w]),c(()=>{let z=Math.round(j*100);o(z)},[j]),c(()=>{if(A.current!==null)A.current.currentTime=Q,_(Q)},[Q]),c(()=>{if(A.current&&$.length>H){let z=$[Y];A.current.src=z?.src,A.current.currentTime=H}},[$,Y]),c(()=>{if(A.current&&$.length>H)if(O)x(!0),A.current.play().then(()=>{x(void 0)}).catch(()=>{x(void 0)});else A.current.pause()},[Y,$,O]),c(()=>{if(A.current)A.current.volume=j,A.current.muted=D},[j,D]);let M=(z)=>{let K=Array(z.length).fill(0);if(F(K),z.length>0)m(z).then((L)=>{if(Array.isArray(L))F(L)})},_A=(z)=>{m([z]).then((K)=>{if(K)F((L)=>{return[...L,K[0]]})})};c(()=>{if($.length>0)m($).then((z)=>{if(Array.isArray(z))F(z)})},[$]);let UA=zA(()=>{if(A.current!==null){let z=A.current.currentTime;_(z)}},[_]),BA=zA(()=>{if(A.current!==null)gA(A.current.duration)},[A.current?.duration]),NA=zA(()=>{if($.length===H)return;Z(!0)},[$]),e=zA(()=>Z(!1),[Z]),wA=zA(()=>Z((z)=>!z),[Z]),d=zA((z=!1)=>{if($.length===H)return;let K;if(w){let W=$.map((G,J)=>J).filter((G)=>G!==Y);K=W[Math.floor(Math.random()*W.length)],B((G)=>{return G=G.slice(H,LA),[K,...G]})}else K=Y+E;if(K>=$.length){if(Z(z),K=H,!z)return}if(k(H),!O)Z(!0);b(K)},[$,Y,w,O,b]),T=zA(()=>{switch(R){case 0:d();break;case 1:d(!0);break;case 2:A.current?.play();break}},[R,d]),OA=zA(()=>{if($.length===H||!A.current)return;if(p>CA){A.current.currentTime=H;return}let z=H;if(w){if(t.length===E)return;B((K)=>{let[,...L]=K;return z=L[H],L})}else z=Y-E;if(z<H)z=$.length-E;b(z),k(H),Z(!0)},[$,t,Y,p,V]),AA=(z)=>b(z),GA=(z)=>{s(z),JA.setItem("player-volume",String(z))},QA=(z)=>{let K=EA(z);GA(K)},PA=()=>f(!0),SA=()=>f(!1),kA=()=>f((z)=>!z),RA=()=>P(!0),TA=()=>P(!1),mA=()=>P((z)=>!z),vA=()=>{xA((z)=>{let K=0;if(z===0)K=1;if(z===1)K=2;if(z===2)K=0;return JA.setItem("player-repeat",String(K)),K})},IA=(z)=>{N((K)=>[...K,z]),_A(z)},yA=(z,K)=>{let L=K!==void 0||O;if(e(),B([H]),k(H),N(z),M(z),L)b(K||H),NA()};c(()=>{JA.setItem("player-muted",String(D))},[D]);let VA=()=>{if(A.current&&A.current.buffered.length>0){let z=A.current.buffered.end(A.current.buffered.length-1);k(z/A.current.duration*100)}};c(()=>{if(A.current)A.current.addEventListener("ended",T);return()=>{A.current?.removeEventListener("ended",T)}},[T]),c(()=>{if(A.current)A.current.muted=D,A.current.volume=j,Z(!1),A.current.preload="metadata",A.current.addEventListener("progress",VA),A.current.addEventListener("timeupdate",UA),A.current.addEventListener("durationchange",BA);return()=>{if(A.current)A.current.pause(),A.current.removeEventListener("progress",VA),A.current.removeEventListener("timeupdate",UA),A.current.removeEventListener("durationchange",BA),A.current=null}},[]);let cA={play:NA,pause:e,togglePlayPause:wA,next:d,previous:OA,setVolume:GA,setVolumePercent:QA,mute:PA,unmute:SA,toggleMute:kA,shuffleOn:RA,shuffleOff:TA,toggleShuffle:mA,toggleLoop:vA,addToPlaylist:IA,replacePlaylist:yA,setCurrentTrack:AA,setUpdateTime:V,setPlaylistId:I,volume:j,volumePercent:XA,bufferedPercentage:FA,currentPlaylistId:v,maxTime:hA,playlist:$,durations:X,isPlaying:O,currentTime:p,currentTrackIndex:Y,repeatMode:R,isShuffled:w,isLoading:S,isMuted:D};return W0(WA.Provider,{value:cA,children:q})};import{useContext as $0}from"react";var I0=()=>{let q=$0(WA);if(q===void 0)throw new Error("useAudio must be used within an PlayerContext");return q};export{I0 as useAudio,LA as truncateBackStackQueue,E as step,EA as percentToValue,CA as offsetTimeBumpToStart,oA as metadataBytesLength,pA as maxPercentage,H as firstElement,m as fetchDuration,DA as defaultVolumePercent,bA as defaultVolume,MA as debounceLoadingState,L0 as PlayerPlayPauseSyncProvider,k0 as PlayerNoSyncProvider,F0 as PlayerFullSyncProvider,rA as LoopState};