@prose-reader/enhancer-audio 1.324.0 → 1.325.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@prose-reader/core"),t=require("rxjs");var n=80,r=.035,i=()=>Array.from({length:n},()=>0),a=e=>{if(e.length===0)return i();let t=Math.max(1,Math.floor(e.length/n));return Array.from({length:n},(n,i)=>{let a=i*t,o=Math.min(e.length,a+t);if(a>=e.length||a===o)return 0;let s=0;for(let t=a;t<o;t+=1)s+=e[t]??0;let c=(s/(o-a)/255-r)/(1-r);return Math.max(0,Math.min(1,c))})},o=256,s=class{audioElement;audioContext;audioSourceNode;analyserNode;frequencyData;constructor(e){this.audioElement=e}async resumeIfNeeded(){if(!this.ensure())return!1;let e=this.audioContext;if(!e)return!1;if(e.state!==`suspended`)return!0;try{return await e.resume(),!0}catch{return!1}}readLevels(){return!this.analyserNode||!this.frequencyData?i():(this.analyserNode.getByteFrequencyData(this.frequencyData),a(this.frequencyData))}destroy(){this.audioSourceNode?.disconnect(),this.analyserNode?.disconnect(),this.audioContext?.close().catch(()=>void 0),this.audioContext=void 0,this.audioSourceNode=void 0,this.analyserNode=void 0,this.frequencyData=void 0}ensure(){if(this.audioContext&&this.analyserNode&&this.frequencyData)return!0;if(typeof window>`u`||!window.AudioContext)return!1;let e=new window.AudioContext,t=e.createMediaElementSource(this.audioElement),n=e.createAnalyser();return n.fftSize=o,n.smoothingTimeConstant=.8,t.connect(n),n.connect(e.destination),this.audioContext=e,this.audioSourceNode=t,this.analyserNode=n,this.frequencyData=new Uint8Array(n.frequencyBinCount),!0}},c=class extends e.ReactiveEntity{audioGraph;playbackState$=new t.BehaviorSubject({trackId:void 0,isRunning:!1,resetLevels:!1});subscriptions=new t.Subscription;constructor(e){super({levels:i(),isActive:!1,trackId:void 0}),this.audioGraph=new s(e),this.subscriptions.add(this.playbackState$.pipe((0,t.distinctUntilChanged)((e,t)=>e.trackId===t.trackId&&e.isRunning===t.isRunning&&e.resetLevels===t.resetLevels),(0,t.switchMap)(({trackId:e,isRunning:n,resetLevels:r})=>!e||!n?(0,t.of)({trackId:e,isActive:!1,...r?{levels:i()}:void 0}):(0,t.concat)((0,t.of)({trackId:e,isActive:!0,...this.value.trackId===e?void 0:{levels:i()}}),this.createLevels$().pipe((0,t.map)(t=>({trackId:e,levels:t,isActive:!0})),(0,t.defaultIfEmpty)({trackId:e,isActive:!1,levels:i()}))))).subscribe(e=>{this.mergeCompare(e)}))}start(e){e&&this.playbackState$.next({trackId:e.id,isRunning:!0,resetLevels:!1})}setTrack(e){this.playbackState$.next({trackId:e,isRunning:!1,resetLevels:!1})}reset(e){this.playbackState$.next({trackId:e,isRunning:!1,resetLevels:!0})}stop({resetLevels:e=!1}={}){this.playbackState$.next({trackId:this.playbackState$.value.trackId,isRunning:!1,resetLevels:e})}destroy(){this.stop({resetLevels:!0}),this.subscriptions.unsubscribe(),this.playbackState$.complete(),this.audioGraph.destroy(),super.destroy()}createLevels$(){return(0,t.defer)(()=>(0,t.from)(this.audioGraph.resumeIfNeeded()).pipe((0,t.switchMap)(e=>e?(0,t.animationFrames)().pipe((0,t.map)(()=>this.audioGraph.readLevels())):t.EMPTY)))}},l=class{element;canPlay$;ended$;isPlaying$;metrics$;constructor(){this.element=document.createElement(`audio`),this.element.preload=`metadata`,this.canPlay$=(0,t.fromEvent)(this.element,`canplay`).pipe((0,t.share)()),this.ended$=(0,t.fromEvent)(this.element,`ended`).pipe((0,t.share)()),this.isPlaying$=(0,t.merge)((0,t.fromEvent)(this.element,`play`).pipe((0,t.map)(()=>!0)),(0,t.fromEvent)(this.element,`pause`).pipe((0,t.map)(()=>!1))).pipe((0,t.share)()),this.metrics$=(0,t.merge)((0,t.fromEvent)(this.element,`timeupdate`),(0,t.fromEvent)(this.element,`seeking`),(0,t.fromEvent)(this.element,`seeked`),(0,t.fromEvent)(this.element,`loadedmetadata`),(0,t.fromEvent)(this.element,`durationchange`),this.canPlay$).pipe((0,t.map)(()=>({currentTime:this.element.currentTime,duration:Number.isFinite(this.element.duration)?this.element.duration:void 0})),(0,t.share)())}get paused(){return this.element.paused}get hasSource(){return this.element.hasAttribute(`src`)}play$(){return(0,t.defer)(()=>(0,t.from)(this.element.play())).pipe((0,t.retry)({count:1,delay:()=>this.canPlay$.pipe((0,t.take)(1))}))}pause(){this.element.pause()}loadSource(e){this.element.src=e,this.element.load()}unloadSource(){this.element.removeAttribute(`src`),this.element.load()}setCurrentTime(e){this.element.currentTime=e}},u=class{cachedSourceByTrackId=new Map;hasCachedSource(e){return this.cachedSourceByTrackId.has(e)}getTrackResourceUrl$=(e,n)=>{let r=this.cachedSourceByTrackId.get(e.id);return r?(0,t.of)(r.url):(0,t.from)(n.getResource()).pipe((0,t.switchMap)(n=>n instanceof URL?(this.cachedSourceByTrackId.set(e.id,{url:n.href}),(0,t.of)(n.href)):n instanceof Response?(0,t.from)(n.blob()).pipe((0,t.map)(t=>{let n=URL.createObjectURL(t);return this.cachedSourceByTrackId.set(e.id,{url:n,release:()=>{URL.revokeObjectURL(n)}}),n})):(this.cachedSourceByTrackId.set(e.id,{url:e.href}),(0,t.of)(e.href))))};releaseTrackSource(e){let t=this.cachedSourceByTrackId.get(e);t&&(this.cachedSourceByTrackId.delete(e),t.release?.())}releaseAll(){for(let e of this.cachedSourceByTrackId.keys())this.releaseTrackSource(e)}destroy(){this.releaseAll()}},d=(e,t)=>e===t||e.length===t.length&&e.every((e,n)=>e===t[n]),f=(e,t,n)=>{if(Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;let a=n?.customEqual??Object.is,o=e,s=t;for(let e of r)if(!Object.hasOwn(s,e)||!a(o[e],s[e]))return!1;return!0};function p(){let e=globalThis.__PROSE_READER_DEBUG;return e===!0||e===`true`}var m=(e,t)=>typeof e==`boolean`?{enabled:e,options:t}:{enabled:e?.enabled,options:e??t},h=e=>e.at(-1)?.color,g=e=>{if(!e.length)return;if(!e.some(e=>e.color))return[e.map(e=>e.label).join(` `)];let t=``,n=[];for(let r of e)t+=`%c${t?` ${r.label}`:r.label}`,n.push(r.color?`color: ${r.color}`:``);return[t,...n]},_=(e=[],t=p())=>{let n=t,r={enable:e=>{a(e)},namespace:(t,r,i)=>{let a=m(r,i),o=a.options?.color??h(e);return _([...e,{label:`[${t}]`,color:o}],a.enabled??n)},isEnabled:()=>n,debug:()=>{},info:()=>{},log:()=>{},groupCollapsed:()=>{},groupEnd:()=>{},getGroupArgs:t=>{let n=g(e);if(!n)return[t];let[r,...i]=n;return[`${r} ${t}`,...i]},warn:()=>{},error:()=>{}},i=t=>{if(!t){r.debug=()=>{},r.info=()=>{},r.log=()=>{},r.groupCollapsed=()=>{},r.groupEnd=()=>{},r.warn=()=>{},r.error=()=>{};return}let n=g(e)??[];r.debug=Function.prototype.bind.call(console.debug,console,...n),r.info=Function.prototype.bind.call(console.info,console,...n),r.log=Function.prototype.bind.call(console.log,console,...n),r.groupCollapsed=Function.prototype.bind.call(console.groupCollapsed,console),r.groupEnd=Function.prototype.bind.call(console.groupEnd,console),r.warn=Function.prototype.bind.call(console.warn,console,...n),r.error=Function.prototype.bind.call(console.error,console,...n)},a=e=>{n!==e&&(n=e,i(n))};return i(n),r};_();var v=new Set([`mp3`,`m4a`,`m4b`,`aac`,`ogg`,`oga`,`wav`,`flac`,`opus`]),y=e=>{let[t=``]=e.split(/[?#]/);return(t.split(`/`).at(-1)??``).split(`.`).at(-1)?.toLowerCase()},b=e=>{if((e.mediaType?.split(`;`).at(0)?.trim().toLowerCase())?.startsWith(`audio/`))return!0;let t=y(e.href);return t?v.has(t):!1},x=(e,t)=>{if(t!==void 0)return e.find(e=>e.index===t)},S=(e,t)=>[x(e,t.beginSpineItemIndex),x(e,t.endSpineItemIndex)].filter((e,t,n)=>e!==void 0&&n.indexOf(e)===t);function C(n,r){let i=n.context.manifest$.pipe((0,t.map)(e=>e.spineItems.filter(b).map(e=>({id:e.id,href:e.href,index:e.index,mediaType:e.mediaType}))),(0,t.shareReplay)({bufferSize:1,refCount:!0})),a=n.pagination.state$.pipe((0,e.mapKeysTo)([`beginSpineItemIndex`,`endSpineItemIndex`]),(0,t.distinctUntilChanged)(f),(0,t.shareReplay)({bufferSize:1,refCount:!0}));return{tracks$:i,visibleTrackIds$:(0,t.combineLatest)([i,a]).pipe((0,t.map)(([e,t])=>S(e,t).map(({id:e})=>e)),(0,t.distinctUntilChanged)(d),(0,t.shareReplay)({bufferSize:1,refCount:!0})),nextTrack$:(0,t.combineLatest)([i,a,r.pipe((0,t.map)(e=>e.currentTrack),(0,t.distinctUntilChanged)())]).pipe((0,t.map)(([e,{endSpineItemIndex:t},n])=>({nextTrackInPaginationWindow:n&&t!==void 0?e.find(({index:e})=>e>n.index&&e<=t):void 0,nextTrackAfterCurrentTrack:n?e.find(({index:e})=>e>n.index):void 0})),(0,t.shareReplay)({bufferSize:1,refCount:!0}))}}var w={shouldPlay:!1,trackId:void 0},T={tracks:[],currentTrack:void 0,isPlaying:!1,isLoading:!1,hasError:!1,currentTime:0,duration:void 0},E=class extends e.ReactiveEntity{reader;audio;visualizer$;resourcesResolver=new u;visibleTrackIds$;playCommandSubject=new t.Subject;pauseCommandSubject=new t.Subject;selectCommandSubject=new t.Subject;desiredPlayback$=new t.BehaviorSubject(w);subscriptions=new t.Subscription;constructor(n,r=new l){super(T),this.reader=n,this.audio=r,this.visualizer$=new c(this.audio.element);let{tracks$:i,visibleTrackIds$:a,nextTrack$:o}=C(this.reader,this.state$);this.visibleTrackIds$=a;let s=this.visibleTrackIds$.pipe((0,t.map)(e=>e[0]),(0,t.distinctUntilChanged)(),(0,t.share)()),u=s.pipe((0,t.withLatestFrom)(this.state$),(0,t.filter)(([e,t])=>e===void 0&&(t.currentTrack?.id!==void 0||t.isLoading)),(0,t.map)(([,e])=>e.tracks)),d=s.pipe((0,t.filter)(e=>e!==void 0),(0,t.withLatestFrom)(this.desiredPlayback$),(0,t.map)(([e,{shouldPlay:t}])=>({trackId:e,options:{navigate:!1,play:t?!0:void 0}}))),f=(0,t.merge)(u,i.pipe((0,t.tap)(()=>this.resourcesResolver.releaseAll()))).pipe((0,t.tap)(e=>{this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.visualizer$.stop({resetLevels:!0}),this.unmountCurrentSource(),this.mergeCompare({tracks:e,currentTrack:void 0,isLoading:!1,isPlaying:!1,hasError:!1,currentTime:0,duration:void 0}),this.visualizer$.reset(void 0)}),(0,t.share)()),p=this.playCommandSubject.pipe((0,t.filter)(()=>!this.audio.hasSource),(0,t.map)(()=>this.state.currentTrack??this.state.tracks[0]),(0,t.filter)(e.isDefined),(0,t.map)(e=>({trackId:e.id,options:{navigate:!1,play:!0}}))),m=this.playCommandSubject.pipe((0,t.filter)(()=>this.audio.hasSource),(0,t.tap)(()=>{this.emitDesiredPlayback({shouldPlay:!0,trackId:this.state.currentTrack?.id})})),h=this.audio.ended$.pipe((0,t.withLatestFrom)(o),(0,t.switchMap)(([,{nextTrackAfterCurrentTrack:e,nextTrackInPaginationWindow:n}])=>(this.visualizer$.stop({resetLevels:!0}),n?(0,t.of)({trackId:n.id,options:{navigate:!1,play:!0}}):(e||this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.reader.navigation.goToRightOrBottomSpineItem(),t.EMPTY)))),g=(0,t.merge)(m,(0,t.merge)(this.selectCommandSubject,d,h,p).pipe((0,t.withLatestFrom)(this.state$),(0,t.filter)(([e,t])=>t.tracks.some(({id:t})=>t===e.trackId)),(0,t.map)(([e,t])=>({selectionIntent:e,state:t,isReselectionWhileLoading:t.currentTrack?.id===e.trackId&&t.isLoading})),(0,t.tap)(({selectionIntent:e,state:t,isReselectionWhileLoading:n})=>{e.options.navigate!==!1&&this.reader.navigation.navigate({spineItem:e.trackId,animation:`turn`}),n&&e.options.play!==void 0&&this.emitDesiredPlayback({shouldPlay:e.options.play,trackId:t.currentTrack?.id})}),(0,t.filter)(({isReselectionWhileLoading:e})=>!e),(0,t.switchMap)(({selectionIntent:{trackId:e,options:t}})=>this.selectTrack$({trackId:e,options:t,playbackReset$:f})))).pipe((0,t.withLatestFrom)(this.desiredPlayback$),(0,t.switchMap)(([,{shouldPlay:e,trackId:n}])=>!e||!this.audio.hasSource||this.state.currentTrack?.id!==n?t.EMPTY:(this.mergeCompare({hasError:!1}),this.audio.play$().pipe((0,t.catchError)(()=>(this.mergeCompare({hasError:!0}),t.EMPTY))))));this.subscriptions.add(f.subscribe()),this.subscriptions.add(g.subscribe()),this.subscriptions.add(this.pauseCommandSubject.subscribe(()=>{this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0})})),this.subscriptions.add(this.audio.isPlaying$.subscribe(e=>{if(this.mergeCompare({isPlaying:e}),!e){this.visualizer$.stop();return}this.state.currentTrack&&this.visualizer$.start(this.state.currentTrack)})),this.subscriptions.add(this.audio.metrics$.subscribe(({currentTime:e,duration:t})=>{this.mergeCompare({currentTime:e,duration:t})}))}get state(){return this.value}get visualizer(){return this.visualizer$.value}resetTrackSelection({currentTrack:e,isLoading:t}){this.mergeCompare({currentTrack:e,isLoading:t,isPlaying:!1,hasError:!1,currentTime:0,duration:void 0}),this.visualizer$.reset(e?.id)}emitDesiredPlayback({shouldPlay:e,trackId:t}){this.desiredPlayback$.next({shouldPlay:e,trackId:e?t:void 0}),e||this.audio.pause()}unmountCurrentSource(){if(!this.audio.hasSource)return;let e=this.state.currentTrack?.id;this.audio.paused||this.audio.pause(),this.audio.unloadSource(),e&&this.resourcesResolver.releaseTrackSource(e)}mountSource(e){this.unmountCurrentSource(),this.audio.loadSource(e)}resolveTrackSource(e){let n=this.reader.spineItemsManager.get(e.index);return n?this.resourcesResolver.getTrackResourceUrl$(e,n.resourcesHandler):t.EMPTY}selectTrack$({trackId:e,options:n,playbackReset$:r}){let i=this.state.tracks.find(({id:t})=>t===e);if(!i)return t.EMPTY;let a=this.state.currentTrack,o=n.play??(!this.audio.paused&&a!==void 0);return this.emitDesiredPlayback({shouldPlay:o,trackId:i.id}),a?.id===i.id&&this.audio.hasSource?(this.visualizer$.setTrack(i.id),o?(0,t.of)(void 0):t.EMPTY):(this.unmountCurrentSource(),this.resetTrackSelection({currentTrack:i,isLoading:!0}),this.resolveTrackSource(i).pipe((0,t.map)(e=>({source:e})),(0,t.defaultIfEmpty)({source:void 0}),(0,t.catchError)(()=>(0,t.of)({source:void 0})),(0,t.takeUntil)(r),(0,t.tap)(({source:e})=>{e?this.mountSource(e):this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.mergeCompare({isLoading:!1})}),(0,t.filter)(({source:e})=>e!==void 0),(0,t.map)(()=>void 0)))}select(e,t={}){this.selectCommandSubject.next({trackId:e,options:t})}play(){this.playCommandSubject.next()}pause(){this.pauseCommandSubject.next()}toggle(){if(this.audio.paused){this.play();return}this.pause()}setCurrentTime(e){this.mergeCompare({currentTime:e}),this.audio.setCurrentTime(e)}destroy(){this.subscriptions.unsubscribe(),this.playCommandSubject.complete(),this.pauseCommandSubject.complete(),this.selectCommandSubject.complete(),this.desiredPlayback$.complete(),this.visualizer$.destroy(),this.unmountCurrentSource(),this.resourcesResolver.destroy(),super.destroy()}},D=class extends e.DocumentRenderer{onCreateDocument(){let e=this.containerElement.ownerDocument.createElement(`div`);return e.style.cssText=`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@prose-reader/core"),t=require("rxjs");var n=80,r=.035,i=()=>Array.from({length:n},()=>0),a=e=>{if(e.length===0)return i();let t=Math.max(1,Math.floor(e.length/n));return Array.from({length:n},(n,i)=>{let a=i*t,o=Math.min(e.length,a+t);if(a>=e.length||a===o)return 0;let s=0;for(let t=a;t<o;t+=1)s+=e[t]??0;let c=(s/(o-a)/255-r)/(1-r);return Math.max(0,Math.min(1,c))})},o=256,s=class{audioElement;audioContext;audioSourceNode;analyserNode;frequencyData;constructor(e){this.audioElement=e}async resumeIfNeeded(){if(!this.ensure())return!1;let e=this.audioContext;if(!e)return!1;if(e.state!==`suspended`)return!0;try{return await e.resume(),!0}catch{return!1}}readLevels(){return!this.analyserNode||!this.frequencyData?i():(this.analyserNode.getByteFrequencyData(this.frequencyData),a(this.frequencyData))}destroy(){this.audioSourceNode?.disconnect(),this.analyserNode?.disconnect(),this.audioContext?.close().catch(()=>void 0),this.audioContext=void 0,this.audioSourceNode=void 0,this.analyserNode=void 0,this.frequencyData=void 0}ensure(){if(this.audioContext&&this.analyserNode&&this.frequencyData)return!0;if(typeof window>`u`||!window.AudioContext)return!1;let e=new window.AudioContext,t=e.createMediaElementSource(this.audioElement),n=e.createAnalyser();return n.fftSize=o,n.smoothingTimeConstant=.8,t.connect(n),n.connect(e.destination),this.audioContext=e,this.audioSourceNode=t,this.analyserNode=n,this.frequencyData=new Uint8Array(n.frequencyBinCount),!0}},c=class extends e.ReactiveEntity{audioGraph;playbackState$=new t.BehaviorSubject({trackId:void 0,isRunning:!1,resetLevels:!1});subscriptions=new t.Subscription;constructor(e){super({levels:i(),isActive:!1,trackId:void 0}),this.audioGraph=new s(e),this.subscriptions.add(this.playbackState$.pipe((0,t.distinctUntilChanged)((e,t)=>e.trackId===t.trackId&&e.isRunning===t.isRunning&&e.resetLevels===t.resetLevels),(0,t.switchMap)(({trackId:e,isRunning:n,resetLevels:r})=>!e||!n?(0,t.of)({trackId:e,isActive:!1,...r?{levels:i()}:void 0}):(0,t.concat)((0,t.of)({trackId:e,isActive:!0,...this.value.trackId===e?void 0:{levels:i()}}),this.createLevels$().pipe((0,t.map)(t=>({trackId:e,levels:t,isActive:!0})),(0,t.defaultIfEmpty)({trackId:e,isActive:!1,levels:i()}))))).subscribe(e=>{this.mergeCompare(e)}))}start(e){e&&this.playbackState$.next({trackId:e.id,isRunning:!0,resetLevels:!1})}setTrack(e){this.playbackState$.next({trackId:e,isRunning:!1,resetLevels:!1})}reset(e){this.playbackState$.next({trackId:e,isRunning:!1,resetLevels:!0})}stop({resetLevels:e=!1}={}){this.playbackState$.next({trackId:this.playbackState$.value.trackId,isRunning:!1,resetLevels:e})}destroy(){this.stop({resetLevels:!0}),this.subscriptions.unsubscribe(),this.playbackState$.complete(),this.audioGraph.destroy(),super.destroy()}createLevels$(){return(0,t.defer)(()=>(0,t.from)(this.audioGraph.resumeIfNeeded()).pipe((0,t.switchMap)(e=>e?(0,t.animationFrames)().pipe((0,t.map)(()=>this.audioGraph.readLevels())):t.EMPTY)))}},l=class{element;canPlay$;ended$;isPlaying$;metrics$;constructor(){this.element=document.createElement(`audio`),this.element.preload=`metadata`,this.canPlay$=(0,t.fromEvent)(this.element,`canplay`).pipe((0,t.share)()),this.ended$=(0,t.fromEvent)(this.element,`ended`).pipe((0,t.share)()),this.isPlaying$=(0,t.merge)((0,t.fromEvent)(this.element,`play`).pipe((0,t.map)(()=>!0)),(0,t.fromEvent)(this.element,`pause`).pipe((0,t.map)(()=>!1))).pipe((0,t.share)()),this.metrics$=(0,t.merge)((0,t.fromEvent)(this.element,`timeupdate`),(0,t.fromEvent)(this.element,`seeking`),(0,t.fromEvent)(this.element,`seeked`),(0,t.fromEvent)(this.element,`loadedmetadata`),(0,t.fromEvent)(this.element,`durationchange`),this.canPlay$).pipe((0,t.map)(()=>({currentTime:this.element.currentTime,duration:Number.isFinite(this.element.duration)?this.element.duration:void 0})),(0,t.share)())}get paused(){return this.element.paused}get hasSource(){return this.element.hasAttribute(`src`)}play$(){return(0,t.defer)(()=>(0,t.from)(this.element.play())).pipe((0,t.retry)({count:1,delay:()=>this.canPlay$.pipe((0,t.take)(1))}))}pause(){this.element.pause()}loadSource(e){this.element.src=e,this.element.load()}unloadSource(){this.element.removeAttribute(`src`),this.element.load()}setCurrentTime(e){this.element.currentTime=e}},u=class{cachedSourceByTrackId=new Map;hasCachedSource(e){return this.cachedSourceByTrackId.has(e)}getTrackResourceUrl$=(e,n)=>{let r=this.cachedSourceByTrackId.get(e.id);return r?(0,t.of)(r.url):(0,t.from)(n.getResource()).pipe((0,t.switchMap)(n=>n instanceof URL?(this.cachedSourceByTrackId.set(e.id,{url:n.href}),(0,t.of)(n.href)):n instanceof Response?(0,t.from)(n.blob()).pipe((0,t.map)(t=>{let n=URL.createObjectURL(t);return this.cachedSourceByTrackId.set(e.id,{url:n,release:()=>{URL.revokeObjectURL(n)}}),n})):(this.cachedSourceByTrackId.set(e.id,{url:e.href}),(0,t.of)(e.href))))};releaseTrackSource(e){let t=this.cachedSourceByTrackId.get(e);t&&(this.cachedSourceByTrackId.delete(e),t.release?.())}releaseAll(){for(let e of this.cachedSourceByTrackId.keys())this.releaseTrackSource(e)}destroy(){this.releaseAll()}},d=(e,t)=>e===t||e.length===t.length&&e.every((e,n)=>e===t[n]),f=(e,t,n)=>{if(Object.is(e,t))return!0;if(typeof e!=`object`||!e||typeof t!=`object`||!t)return!1;let r=Object.keys(e),i=Object.keys(t);if(r.length!==i.length)return!1;let a=n?.customEqual??Object.is,o=e,s=t;for(let e of r)if(!Object.hasOwn(s,e)||!a(o[e],s[e]))return!1;return!0};function p(){let e=globalThis.__PROSE_READER_DEBUG;return e===!0||e===`true`}var m=(e,t)=>typeof e==`boolean`?{enabled:e,options:t}:{enabled:e?.enabled,options:e??t},h=e=>e.at(-1)?.color,g=e=>{if(!e.length)return;if(!e.some(e=>e.color))return[e.map(e=>e.label).join(` `)];let t=``,n=[];for(let r of e)t+=`%c${t?` ${r.label}`:r.label}`,n.push(r.color?`color: ${r.color}`:``);return[t,...n]},_=(e=[],t=p())=>{let n=t,r={enable:e=>{a(e)},namespace:(t,r,i)=>{let a=m(r,i),o=a.options?.color??h(e);return _([...e,{label:`[${t}]`,color:o}],a.enabled??n)},isEnabled:()=>n,debug:()=>{},info:()=>{},log:()=>{},groupCollapsed:()=>{},groupEnd:()=>{},getGroupArgs:t=>{let n=g(e);if(!n)return[t];let[r,...i]=n;return[`${r} ${t}`,...i]},warn:()=>{},error:()=>{}},i=t=>{if(!t){r.debug=()=>{},r.info=()=>{},r.log=()=>{},r.groupCollapsed=()=>{},r.groupEnd=()=>{},r.warn=()=>{},r.error=()=>{};return}let n=g(e)??[];r.debug=Function.prototype.bind.call(console.debug,console,...n),r.info=Function.prototype.bind.call(console.info,console,...n),r.log=Function.prototype.bind.call(console.log,console,...n),r.groupCollapsed=Function.prototype.bind.call(console.groupCollapsed,console),r.groupEnd=Function.prototype.bind.call(console.groupEnd,console),r.warn=Function.prototype.bind.call(console.warn,console,...n),r.error=Function.prototype.bind.call(console.error,console,...n)},a=e=>{n!==e&&(n=e,i(n))};return i(n),r};_();var v=new Set([`mp3`,`m4a`,`m4b`,`aac`,`ogg`,`oga`,`wav`,`flac`,`opus`]),y=e=>{let[t=``]=e.split(/[?#]/);return(t.split(`/`).at(-1)??``).split(`.`).at(-1)?.toLowerCase()},b=e=>{if((e.mediaType?.split(`;`).at(0)?.trim().toLowerCase())?.startsWith(`audio/`))return!0;let t=y(e.href);return t?v.has(t):!1},x=(e,t)=>{if(t!==void 0)return e.find(e=>e.index===t)},S=(e,t)=>[x(e,t.beginSpineItemIndex),x(e,t.endSpineItemIndex)].filter((e,t,n)=>e!==void 0&&n.indexOf(e)===t);function C(n,r){let i=(0,t.of)(n.context.manifest.spineItems.filter(b).map(e=>({id:e.id,href:e.href,index:e.index,mediaType:e.mediaType}))),a=n.pagination.state$.pipe((0,e.mapKeysTo)([`beginSpineItemIndex`,`endSpineItemIndex`]),(0,t.distinctUntilChanged)(f),(0,t.shareReplay)({bufferSize:1,refCount:!0}));return{tracks$:i,visibleTrackIds$:(0,t.combineLatest)([i,a]).pipe((0,t.map)(([e,t])=>S(e,t).map(({id:e})=>e)),(0,t.distinctUntilChanged)(d),(0,t.shareReplay)({bufferSize:1,refCount:!0})),nextTrack$:(0,t.combineLatest)([i,a,r.pipe((0,t.map)(e=>e.currentTrack),(0,t.distinctUntilChanged)())]).pipe((0,t.map)(([e,{endSpineItemIndex:t},n])=>({nextTrackInPaginationWindow:n&&t!==void 0?e.find(({index:e})=>e>n.index&&e<=t):void 0,nextTrackAfterCurrentTrack:n?e.find(({index:e})=>e>n.index):void 0})),(0,t.shareReplay)({bufferSize:1,refCount:!0}))}}var w={shouldPlay:!1,trackId:void 0},T={tracks:[],currentTrack:void 0,isPlaying:!1,isLoading:!1,hasError:!1,currentTime:0,duration:void 0},E=class extends e.ReactiveEntity{reader;audio;visualizer$;resourcesResolver=new u;visibleTrackIds$;playCommandSubject=new t.Subject;pauseCommandSubject=new t.Subject;selectCommandSubject=new t.Subject;desiredPlayback$=new t.BehaviorSubject(w);subscriptions=new t.Subscription;constructor(n,r=new l){super(T),this.reader=n,this.audio=r,this.visualizer$=new c(this.audio.element);let{tracks$:i,visibleTrackIds$:a,nextTrack$:o}=C(this.reader,this.state$);this.visibleTrackIds$=a;let s=this.visibleTrackIds$.pipe((0,t.map)(e=>e[0]),(0,t.distinctUntilChanged)(),(0,t.share)()),u=s.pipe((0,t.withLatestFrom)(this.state$),(0,t.filter)(([e,t])=>e===void 0&&(t.currentTrack?.id!==void 0||t.isLoading)),(0,t.map)(([,e])=>e.tracks)),d=s.pipe((0,t.filter)(e=>e!==void 0),(0,t.withLatestFrom)(this.desiredPlayback$),(0,t.map)(([e,{shouldPlay:t}])=>({trackId:e,options:{navigate:!1,play:t?!0:void 0}}))),f=(0,t.merge)(u,i.pipe((0,t.tap)(()=>this.resourcesResolver.releaseAll()))).pipe((0,t.tap)(e=>{this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.visualizer$.stop({resetLevels:!0}),this.unmountCurrentSource(),this.mergeCompare({tracks:e,currentTrack:void 0,isLoading:!1,isPlaying:!1,hasError:!1,currentTime:0,duration:void 0}),this.visualizer$.reset(void 0)}),(0,t.share)()),p=this.playCommandSubject.pipe((0,t.filter)(()=>!this.audio.hasSource),(0,t.map)(()=>this.state.currentTrack??this.state.tracks[0]),(0,t.filter)(e.isDefined),(0,t.map)(e=>({trackId:e.id,options:{navigate:!1,play:!0}}))),m=this.playCommandSubject.pipe((0,t.filter)(()=>this.audio.hasSource),(0,t.tap)(()=>{this.emitDesiredPlayback({shouldPlay:!0,trackId:this.state.currentTrack?.id})})),h=this.audio.ended$.pipe((0,t.withLatestFrom)(o),(0,t.switchMap)(([,{nextTrackAfterCurrentTrack:e,nextTrackInPaginationWindow:n}])=>(this.visualizer$.stop({resetLevels:!0}),n?(0,t.of)({trackId:n.id,options:{navigate:!1,play:!0}}):(e||this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.reader.navigation.goToRightOrBottomSpineItem(),t.EMPTY)))),g=(0,t.merge)(m,(0,t.merge)(this.selectCommandSubject,d,h,p).pipe((0,t.withLatestFrom)(this.state$),(0,t.filter)(([e,t])=>t.tracks.some(({id:t})=>t===e.trackId)),(0,t.map)(([e,t])=>({selectionIntent:e,state:t,isReselectionWhileLoading:t.currentTrack?.id===e.trackId&&t.isLoading})),(0,t.tap)(({selectionIntent:e,state:t,isReselectionWhileLoading:n})=>{e.options.navigate!==!1&&this.reader.navigation.navigate({spineItem:e.trackId,animation:`turn`}),n&&e.options.play!==void 0&&this.emitDesiredPlayback({shouldPlay:e.options.play,trackId:t.currentTrack?.id})}),(0,t.filter)(({isReselectionWhileLoading:e})=>!e),(0,t.switchMap)(({selectionIntent:{trackId:e,options:t}})=>this.selectTrack$({trackId:e,options:t,playbackReset$:f})))).pipe((0,t.withLatestFrom)(this.desiredPlayback$),(0,t.switchMap)(([,{shouldPlay:e,trackId:n}])=>!e||!this.audio.hasSource||this.state.currentTrack?.id!==n?t.EMPTY:(this.mergeCompare({hasError:!1}),this.audio.play$().pipe((0,t.catchError)(()=>(this.mergeCompare({hasError:!0}),t.EMPTY))))));this.subscriptions.add(f.subscribe()),this.subscriptions.add(g.subscribe()),this.subscriptions.add(this.pauseCommandSubject.subscribe(()=>{this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0})})),this.subscriptions.add(this.audio.isPlaying$.subscribe(e=>{if(this.mergeCompare({isPlaying:e}),!e){this.visualizer$.stop();return}this.state.currentTrack&&this.visualizer$.start(this.state.currentTrack)})),this.subscriptions.add(this.audio.metrics$.subscribe(({currentTime:e,duration:t})=>{this.mergeCompare({currentTime:e,duration:t})}))}get state(){return this.value}get visualizer(){return this.visualizer$.value}resetTrackSelection({currentTrack:e,isLoading:t}){this.mergeCompare({currentTrack:e,isLoading:t,isPlaying:!1,hasError:!1,currentTime:0,duration:void 0}),this.visualizer$.reset(e?.id)}emitDesiredPlayback({shouldPlay:e,trackId:t}){this.desiredPlayback$.next({shouldPlay:e,trackId:e?t:void 0}),e||this.audio.pause()}unmountCurrentSource(){if(!this.audio.hasSource)return;let e=this.state.currentTrack?.id;this.audio.paused||this.audio.pause(),this.audio.unloadSource(),e&&this.resourcesResolver.releaseTrackSource(e)}mountSource(e){this.unmountCurrentSource(),this.audio.loadSource(e)}resolveTrackSource(e){let n=this.reader.spineItemsManager.get(e.index);return n?this.resourcesResolver.getTrackResourceUrl$(e,n.resourcesHandler):t.EMPTY}selectTrack$({trackId:e,options:n,playbackReset$:r}){let i=this.state.tracks.find(({id:t})=>t===e);if(!i)return t.EMPTY;let a=this.state.currentTrack,o=n.play??(!this.audio.paused&&a!==void 0);return this.emitDesiredPlayback({shouldPlay:o,trackId:i.id}),a?.id===i.id&&this.audio.hasSource?(this.visualizer$.setTrack(i.id),o?(0,t.of)(void 0):t.EMPTY):(this.unmountCurrentSource(),this.resetTrackSelection({currentTrack:i,isLoading:!0}),this.resolveTrackSource(i).pipe((0,t.map)(e=>({source:e})),(0,t.defaultIfEmpty)({source:void 0}),(0,t.catchError)(()=>(0,t.of)({source:void 0})),(0,t.takeUntil)(r),(0,t.tap)(({source:e})=>{e?this.mountSource(e):this.emitDesiredPlayback({shouldPlay:!1,trackId:void 0}),this.mergeCompare({isLoading:!1})}),(0,t.filter)(({source:e})=>e!==void 0),(0,t.map)(()=>void 0)))}select(e,t={}){this.selectCommandSubject.next({trackId:e,options:t})}play(){this.playCommandSubject.next()}pause(){this.pauseCommandSubject.next()}toggle(){if(this.audio.paused){this.play();return}this.pause()}setCurrentTime(e){this.mergeCompare({currentTime:e}),this.audio.setCurrentTime(e)}destroy(){this.subscriptions.unsubscribe(),this.playCommandSubject.complete(),this.pauseCommandSubject.complete(),this.selectCommandSubject.complete(),this.desiredPlayback$.complete(),this.visualizer$.destroy(),this.unmountCurrentSource(),this.resourcesResolver.destroy(),super.destroy()}},D=class extends e.DocumentRenderer{onCreateDocument(){let e=this.containerElement.ownerDocument.createElement(`div`);return e.style.cssText=`
2
2
  box-sizing: border-box;
3
3
  width: 100%;
4
4
  height: 100%;
5
5
  display: block;
6
- `,e.setAttribute(`data-prose-reader-audio-page`,this.item.id),this.setDocumentContainer(e),(0,t.of)(e)}onLoadDocument(){return this.attach(),t.EMPTY}onUnload(){return this.detach(),t.EMPTY}onLayout(){let{width:n,height:r}=this.viewport.pageSize,i=this.documentContainer;return i&&((0,e.setStylePropertyIfChanged)(i.style,`width`,`${n}px`),(0,e.setStylePropertyIfChanged)(i.style,`height`,`${r}px`)),(0,t.of)({width:n,height:r})}onRenderHeadless(){return t.EMPTY}getDocumentFrame(){}get renditionLayout(){return`pre-paginated`}},O=e=>t=>{let n=e({...t,getRenderer(e){let n=t.getRenderer?.(e);if(n)return n;if(b(e))return e=>new D(e)}}),r=new E(n),i=()=>{r.destroy(),n.destroy()};return{...n,__PROSE_READER_ENHANCER_AUDIO:!0,destroy:i,audio:{state$:r.state$,visualizer$:r.visualizer$,visibleTrackIds$:r.visibleTrackIds$,get state(){return r.state},get visualizer(){return r.visualizer},isAudioRenderer:e=>e instanceof D,play:()=>r.play(),pause:()=>r.pause(),toggle:()=>r.toggle(),setCurrentTime:e=>r.setCurrentTime(e),select:(e,t)=>r.select(e,t)}}};exports.AudioRenderer=D,exports.audioEnhancer=O,exports.isAudioSpineItem=b;
6
+ `,e.setAttribute(`data-prose-reader-audio-page`,this.item.id),this.setDocumentContainer(e),(0,t.of)(e)}onLoadDocument(){return this.attach(),t.EMPTY}onUnload(){this.detach()}onLayout(){let{width:n,height:r}=this.viewport.pageSize,i=this.documentContainer;return i&&((0,e.setStylePropertyIfChanged)(i.style,`width`,`${n}px`),(0,e.setStylePropertyIfChanged)(i.style,`height`,`${r}px`)),(0,t.of)({width:n,height:r})}onRenderHeadless(){return t.EMPTY}getDocumentFrame(){}get renditionLayout(){return`pre-paginated`}},O=e=>t=>{let n=e({...t,getRenderer(e){let n=t.getRenderer?.(e);if(n)return n;if(b(e))return e=>new D(e)}}),r=new E(n),i=()=>{r.destroy(),n.destroy()};return{...n,__PROSE_READER_ENHANCER_AUDIO:!0,destroy:i,audio:{state$:r.state$,visualizer$:r.visualizer$,visibleTrackIds$:r.visibleTrackIds$,get state(){return r.state},get visualizer(){return r.visualizer},isAudioRenderer:e=>e instanceof D,play:()=>r.play(),pause:()=>r.pause(),toggle:()=>r.toggle(),setCurrentTime:e=>r.setCurrentTime(e),select:(e,t)=>r.select(e,t)}}};exports.AudioRenderer=D,exports.audioEnhancer=O,exports.isAudioSpineItem=b;
7
7
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/visualizer/levels.ts","../src/visualizer/VisualizerAudioGraph.ts","../src/visualizer/AudioVisualizer.ts","../src/playback/AudioElementAdapter.ts","../src/playback/ResourcesResolver.ts","../../shared/dist/index.js","../src/utils.ts","../src/playback/trackStreams.ts","../src/playback/AudioController.ts","../src/renderer/AudioRenderer.ts","../src/audioEnhancer.ts"],"sourcesContent":["const AUDIO_VISUALIZER_LEVEL_COUNT = 80\nconst AUDIO_VISUALIZER_NOISE_FLOOR = 0.035\n\nexport const getIdleVisualizerLevels = () =>\n Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, () => 0)\n\nexport const getVisualizerLevels = (data: ArrayLike<number>) => {\n if (data.length === 0) return getIdleVisualizerLevels()\n\n const bucketSize = Math.max(\n 1,\n Math.floor(data.length / AUDIO_VISUALIZER_LEVEL_COUNT),\n )\n\n return Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, (_, index) => {\n const start = index * bucketSize\n const end = Math.min(data.length, start + bucketSize)\n\n if (start >= data.length || start === end) return 0\n\n let total = 0\n\n for (let cursor = start; cursor < end; cursor += 1) {\n total += data[cursor] ?? 0\n }\n\n const average = total / (end - start)\n const normalizedAverage = average / 255\n const gatedAverage =\n (normalizedAverage - AUDIO_VISUALIZER_NOISE_FLOOR) /\n (1 - AUDIO_VISUALIZER_NOISE_FLOOR)\n\n return Math.max(0, Math.min(1, gatedAverage))\n })\n}\n","import { getIdleVisualizerLevels, getVisualizerLevels } from \"./levels\"\n\nconst AUDIO_VISUALIZER_FFT_SIZE = 256\n\nexport class VisualizerAudioGraph {\n private audioContext: AudioContext | undefined\n private audioSourceNode: MediaElementAudioSourceNode | undefined\n private analyserNode: AnalyserNode | undefined\n private frequencyData: Uint8Array<ArrayBuffer> | undefined\n\n constructor(private readonly audioElement: HTMLAudioElement) {}\n\n async resumeIfNeeded() {\n if (!this.ensure()) {\n return false\n }\n\n const audioContext = this.audioContext\n\n if (!audioContext) {\n return false\n }\n\n if (audioContext.state !== `suspended`) {\n return true\n }\n\n try {\n await audioContext.resume()\n\n return true\n } catch {\n return false\n }\n }\n\n readLevels() {\n if (!this.analyserNode || !this.frequencyData) {\n return getIdleVisualizerLevels()\n }\n\n this.analyserNode.getByteFrequencyData(this.frequencyData)\n\n return getVisualizerLevels(this.frequencyData)\n }\n\n destroy() {\n this.audioSourceNode?.disconnect()\n this.analyserNode?.disconnect()\n this.audioContext?.close().catch(() => undefined)\n this.audioContext = undefined\n this.audioSourceNode = undefined\n this.analyserNode = undefined\n this.frequencyData = undefined\n }\n\n private ensure() {\n if (this.audioContext && this.analyserNode && this.frequencyData) {\n return true\n }\n\n if (typeof window === `undefined` || !window.AudioContext) {\n return false\n }\n\n const audioContext = new window.AudioContext()\n const audioSourceNode = audioContext.createMediaElementSource(\n this.audioElement,\n )\n const analyserNode = audioContext.createAnalyser()\n analyserNode.fftSize = AUDIO_VISUALIZER_FFT_SIZE\n analyserNode.smoothingTimeConstant = 0.8\n audioSourceNode.connect(analyserNode)\n analyserNode.connect(audioContext.destination)\n\n this.audioContext = audioContext\n this.audioSourceNode = audioSourceNode\n this.analyserNode = analyserNode\n this.frequencyData = new Uint8Array(analyserNode.frequencyBinCount)\n\n return true\n }\n}\n","import { ReactiveEntity } from \"@prose-reader/core\"\nimport {\n animationFrames,\n BehaviorSubject,\n concat,\n defaultIfEmpty,\n defer,\n distinctUntilChanged,\n EMPTY,\n from,\n map,\n of,\n Subscription,\n switchMap,\n} from \"rxjs\"\nimport type { AudioTrack, AudioVisualizerState } from \"../types\"\nimport { getIdleVisualizerLevels } from \"./levels\"\nimport { VisualizerAudioGraph } from \"./VisualizerAudioGraph\"\n\ntype AudioVisualizerPlaybackState = {\n trackId: string | undefined\n isRunning: boolean\n resetLevels: boolean\n}\n\nexport class AudioVisualizer extends ReactiveEntity<AudioVisualizerState> {\n private readonly audioGraph: VisualizerAudioGraph\n private readonly playbackState$ =\n new BehaviorSubject<AudioVisualizerPlaybackState>({\n trackId: undefined,\n isRunning: false,\n resetLevels: false,\n })\n private readonly subscriptions = new Subscription()\n\n constructor(audioElement: HTMLAudioElement) {\n super({\n levels: getIdleVisualizerLevels(),\n isActive: false,\n trackId: undefined,\n })\n this.audioGraph = new VisualizerAudioGraph(audioElement)\n\n this.subscriptions.add(\n this.playbackState$\n .pipe(\n distinctUntilChanged(\n (previous, next) =>\n previous.trackId === next.trackId &&\n previous.isRunning === next.isRunning &&\n previous.resetLevels === next.resetLevels,\n ),\n switchMap(({ trackId, isRunning, resetLevels }) => {\n if (!trackId || !isRunning) {\n return of({\n trackId,\n isActive: false,\n ...(resetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n })\n }\n\n const shouldResetLevels = this.value.trackId !== trackId\n\n return concat(\n of({\n trackId,\n isActive: true,\n ...(shouldResetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n }),\n this.createLevels$().pipe(\n map((levels) => ({\n trackId,\n levels,\n isActive: true,\n })),\n defaultIfEmpty({\n trackId,\n isActive: false,\n levels: getIdleVisualizerLevels(),\n }),\n ),\n )\n }),\n )\n .subscribe((value) => {\n this.mergeCompare(value)\n }),\n )\n }\n\n start(currentTrack: AudioTrack | undefined) {\n if (!currentTrack) return\n\n this.playbackState$.next({\n trackId: currentTrack.id,\n isRunning: true,\n resetLevels: false,\n })\n }\n\n setTrack(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: false,\n })\n }\n\n reset(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: true,\n })\n }\n\n stop({ resetLevels = false }: { resetLevels?: boolean } = {}) {\n this.playbackState$.next({\n trackId: this.playbackState$.value.trackId,\n isRunning: false,\n resetLevels,\n })\n }\n\n override destroy() {\n this.stop({\n resetLevels: true,\n })\n this.subscriptions.unsubscribe()\n this.playbackState$.complete()\n this.audioGraph.destroy()\n\n super.destroy()\n }\n\n private createLevels$() {\n return defer(() => {\n return from(this.audioGraph.resumeIfNeeded()).pipe(\n switchMap((isReady) =>\n isReady\n ? animationFrames().pipe(map(() => this.audioGraph.readLevels()))\n : EMPTY,\n ),\n )\n })\n }\n}\n","import {\n defer,\n from,\n fromEvent,\n map,\n merge,\n type Observable,\n retry,\n share,\n take,\n} from \"rxjs\"\n\nexport type AudioMetrics = {\n currentTime: number\n duration: number | undefined\n}\n\nexport class AudioElementAdapter {\n readonly element: HTMLAudioElement\n readonly canPlay$: Observable<Event>\n readonly ended$: Observable<Event>\n readonly isPlaying$: Observable<boolean>\n readonly metrics$: Observable<AudioMetrics>\n\n constructor() {\n this.element = document.createElement(`audio`)\n this.element.preload = `metadata`\n\n this.canPlay$ = fromEvent(this.element, `canplay`).pipe(share())\n this.ended$ = fromEvent(this.element, `ended`).pipe(share())\n\n this.isPlaying$ = merge(\n fromEvent(this.element, `play`).pipe(map(() => true)),\n fromEvent(this.element, `pause`).pipe(map(() => false)),\n ).pipe(share())\n\n this.metrics$ = merge(\n fromEvent(this.element, `timeupdate`),\n fromEvent(this.element, `seeking`),\n fromEvent(this.element, `seeked`),\n fromEvent(this.element, `loadedmetadata`),\n fromEvent(this.element, `durationchange`),\n this.canPlay$,\n ).pipe(\n map(() => ({\n currentTime: this.element.currentTime,\n duration: Number.isFinite(this.element.duration)\n ? this.element.duration\n : undefined,\n })),\n share(),\n )\n }\n\n get paused() {\n return this.element.paused\n }\n\n get hasSource() {\n return this.element.hasAttribute(`src`)\n }\n\n play$() {\n return defer(() => from(this.element.play())).pipe(\n retry({\n count: 1,\n delay: () => this.canPlay$.pipe(take(1)),\n }),\n )\n }\n\n pause() {\n this.element.pause()\n }\n\n loadSource(src: string) {\n this.element.src = src\n this.element.load()\n }\n\n unloadSource() {\n this.element.removeAttribute(`src`)\n this.element.load()\n }\n\n setCurrentTime(value: number) {\n this.element.currentTime = value\n }\n}\n","import type { ResourceHandler } from \"@prose-reader/core\"\nimport { from, map, of, switchMap } from \"rxjs\"\nimport type { AudioTrack } from \"../types\"\n\ntype CachedTrackSource = {\n url: string\n release?: () => void\n}\n\nexport class ResourcesResolver {\n private readonly cachedSourceByTrackId = new Map<string, CachedTrackSource>()\n\n public hasCachedSource(trackId: string): boolean {\n return this.cachedSourceByTrackId.has(trackId)\n }\n\n public getTrackResourceUrl$ = (\n track: AudioTrack,\n resourcesHandler: Pick<ResourceHandler, \"getResource\">,\n ) => {\n const cachedSource = this.cachedSourceByTrackId.get(track.id)\n\n if (cachedSource) {\n return of(cachedSource.url)\n }\n\n const resource$ = from(resourcesHandler.getResource())\n\n return resource$.pipe(\n switchMap((resource) => {\n if (resource instanceof URL) {\n this.cachedSourceByTrackId.set(track.id, {\n url: resource.href,\n })\n\n return of(resource.href)\n }\n\n if (resource instanceof Response) {\n return from(resource.blob()).pipe(\n map((blob) => {\n const objectUrl = URL.createObjectURL(blob)\n\n this.cachedSourceByTrackId.set(track.id, {\n url: objectUrl,\n release: () => {\n URL.revokeObjectURL(objectUrl)\n },\n })\n\n return objectUrl\n }),\n )\n }\n\n this.cachedSourceByTrackId.set(track.id, {\n url: track.href,\n })\n\n return of(track.href)\n }),\n )\n }\n\n public releaseTrackSource(trackId: string) {\n const cachedSource = this.cachedSourceByTrackId.get(trackId)\n\n if (!cachedSource) return\n\n this.cachedSourceByTrackId.delete(trackId)\n cachedSource.release?.()\n }\n\n public releaseAll() {\n for (const trackId of this.cachedSourceByTrackId.keys()) {\n this.releaseTrackSource(trackId)\n }\n }\n\n public destroy() {\n this.releaseAll()\n }\n}\n","//#region src/array.ts\nvar e = (e, t) => e === t || e.length === t.length && e.every((e, n) => e === t[n]), t = (e) => e.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction n(e) {\n\tlet t = [];\n\tif (e.length === 0) return \"\";\n\tif (typeof e[0] != \"string\") throw TypeError(`Url must be a string. Received ${e[0]}`);\n\te[0].match(/^[^/:]+:\\/*$/) && e.length > 1 && (e[0] = e.shift() + e[0]), e[0].match(/^file:\\/\\/\\//) ? e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n\tfor (let n = 0; n < e.length; n++) {\n\t\tlet r = e[n];\n\t\tif (typeof r != \"string\") throw TypeError(`Url must be a string. Received ${r}`);\n\t\tr !== \"\" && (n > 0 && (r = r.replace(/^[/]+/, \"\")), r = n < e.length - 1 ? r.replace(/[/]+$/, \"\") : r.replace(/[/]+$/, \"/\"), t.push(r));\n\t}\n\tlet n = t.join(\"/\");\n\tn = n.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n\tlet r = n.split(\"?\");\n\treturn n = r.shift() + (r.length > 0 ? \"?\" : \"\") + r.join(\"&\"), n;\n}\nfunction r(...e) {\n\treturn n(Array.from(Array.isArray(e[0]) ? e[0] : e));\n}\nfunction i(e) {\n\tlet t = e.split(\"/\");\n\treturn t.pop(), t.join(\"/\");\n}\n//#endregion\n//#region src/contentType.ts\nvar a = (e) => {\n\tswitch (t(e)) {\n\t\tcase \"png\": return \"image/png\";\n\t\tcase \"jpg\": return \"image/jpg\";\n\t\tcase \"jpeg\": return \"image/jpeg\";\n\t\tcase \"txt\": return \"text/plain\";\n\t\tcase \"webp\": return \"image/webp\";\n\t\tcase \"xhtml\": return \"application/xhtml+xml\";\n\t\tcase \"mp3\": return \"audio/mpeg\";\n\t\tcase \"m4a\":\n\t\tcase \"m4b\": return \"audio/mp4\";\n\t\tcase \"aac\": return \"audio/aac\";\n\t\tcase \"ogg\":\n\t\tcase \"oga\": return \"audio/ogg\";\n\t\tcase \"wav\": return \"audio/wav\";\n\t\tcase \"flac\": return \"audio/flac\";\n\t\tcase \"opus\": return \"audio/opus\";\n\t}\n}, o = ({ mimeType: e, uri: t }) => (e ?? a(t ?? \"\"))?.startsWith(\"application/xhtml+xml\"), s = (e) => e.startsWith(\"image/\") || e.startsWith(\"audio/\") || e.startsWith(\"video/\"), c = (e) => {\n\tif (!e.length) return;\n\tlet t = e.indexOf(\";\");\n\treturn t >= 0 ? e.substring(0, t) : e;\n}, l = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/, u = /^[A-Za-z0-9_]/, d = /^xml/i, f = /[^A-Za-z0-9_.-]+/g, p = \"_\", m = (e) => e.replace(/^_+|_+$/g, \"\"), h = (e) => m(e.trim().replaceAll(\"/\", \"_\").replace(f, \"_\")), g = (e) => {\n\tif (l.test(e) && !d.test(e)) return e;\n\tlet t = h(e), n = t && !d.test(t) ? t : `${p}${t}`;\n\treturn u.test(n) ? n : `${p}${n}`;\n}, _ = (e, t) => {\n\tlet n = g(e);\n\tif (!t.has(n)) return t.add(n), n;\n\tlet r = 2, i = `${n}-${r}`;\n\tfor (; t.has(i);) r += 1, i = `${n}-${r}`;\n\treturn t.add(i), i;\n}, v = () => {\n\tlet e = /* @__PURE__ */ new Set();\n\treturn (t) => _(t, e);\n}, y = (e) => {\n\tlet t = e.pageSpreadLeft === !0;\n\tif (t !== (e.pageSpreadRight === !0)) return t ? \"left\" : \"right\";\n}, b = (e, t, n) => {\n\tif (Object.is(e, t)) return !0;\n\tif (typeof e != \"object\" || !e || typeof t != \"object\" || !t) return !1;\n\tlet r = Object.keys(e), i = Object.keys(t);\n\tif (r.length !== i.length) return !1;\n\tlet a = n?.customEqual ?? Object.is, o = e, s = t;\n\tfor (let e of r) if (!Object.hasOwn(s, e) || !a(o[e], s[e])) return !1;\n\treturn !0;\n}, x = (e, t) => e.reduce((e, n) => {\n\tlet r = t(n);\n\treturn e[r] || (e[r] = []), e[r].push(n), e;\n}, {});\nfunction S(e, t) {\n\tlet n = { ...e };\n\tfor (let r in t) if (Object.hasOwn(t, r)) {\n\t\tlet i = t[r];\n\t\t(i !== void 0 || !(r in e)) && (n[r] = i);\n\t}\n\treturn n;\n}\n//#endregion\n//#region src/report.ts\nfunction C() {\n\tlet e = globalThis.__PROSE_READER_DEBUG;\n\treturn e === !0 || e === \"true\";\n}\nvar w = (e, t) => typeof e == \"boolean\" ? {\n\tenabled: e,\n\toptions: t\n} : {\n\tenabled: e?.enabled,\n\toptions: e ?? t\n}, T = (e) => e.at(-1)?.color, E = (e) => {\n\tif (!e.length) return;\n\tif (!e.some((e) => e.color)) return [e.map((e) => e.label).join(\" \")];\n\tlet t = \"\", n = [];\n\tfor (let r of e) t += `%c${t ? ` ${r.label}` : r.label}`, n.push(r.color ? `color: ${r.color}` : \"\");\n\treturn [t, ...n];\n}, D = (e = [], t = C()) => {\n\tlet n = t, r = {\n\t\tenable: (e) => {\n\t\t\ta(e);\n\t\t},\n\t\tnamespace: (t, r, i) => {\n\t\t\tlet a = w(r, i), o = a.options?.color ?? T(e);\n\t\t\treturn D([...e, {\n\t\t\t\tlabel: `[${t}]`,\n\t\t\t\tcolor: o\n\t\t\t}], a.enabled ?? n);\n\t\t},\n\t\tisEnabled: () => n,\n\t\tdebug: () => {},\n\t\tinfo: () => {},\n\t\tlog: () => {},\n\t\tgroupCollapsed: () => {},\n\t\tgroupEnd: () => {},\n\t\tgetGroupArgs: (t) => {\n\t\t\tlet n = E(e);\n\t\t\tif (!n) return [t];\n\t\t\tlet [r, ...i] = n;\n\t\t\treturn [`${r} ${t}`, ...i];\n\t\t},\n\t\twarn: () => {},\n\t\terror: () => {}\n\t}, i = (t) => {\n\t\tif (!t) {\n\t\t\tr.debug = () => {}, r.info = () => {}, r.log = () => {}, r.groupCollapsed = () => {}, r.groupEnd = () => {}, r.warn = () => {}, r.error = () => {};\n\t\t\treturn;\n\t\t}\n\t\tlet n = E(e) ?? [];\n\t\tr.debug = Function.prototype.bind.call(console.debug, console, ...n), r.info = Function.prototype.bind.call(console.info, console, ...n), r.log = Function.prototype.bind.call(console.log, console, ...n), r.groupCollapsed = Function.prototype.bind.call(console.groupCollapsed, console), r.groupEnd = Function.prototype.bind.call(console.groupEnd, console), r.warn = Function.prototype.bind.call(console.warn, console, ...n), r.error = Function.prototype.bind.call(console.error, console, ...n);\n\t}, a = (e) => {\n\t\tn !== e && (n = e, i(n));\n\t};\n\treturn i(n), r;\n}, O = D(), k = \"prose-reader-resource-error\", A = (e, t) => {\n\tlet n = e.split(/(\\d+)/), r = t.split(/(\\d+)/);\n\tfor (let e = 0, t = n.length; e < t; e++) if (n[e] !== r[e]) return n[e]?.match(/\\d/) ? (n[e] || \"\") - +(r[e] || \"\") : (n[e] || \"\").localeCompare(r[e] || \"\");\n\treturn 1;\n}, j = (e) => e.endsWith(\"/\") ? e.slice(0, -1) : e, M = (e) => {\n\tlet t = j(e);\n\treturn t.substring(t.lastIndexOf(\"/\") + 1) || t;\n}, N = (e) => {\n\tlet t = e.lastIndexOf(\"/\");\n\treturn t >= 0 ? e.substring(0, t) : \"\";\n}, P = (e) => e.replaceAll(\"&\", \"&amp;\").replaceAll(\"\\\"\", \"&quot;\").replaceAll(\"'\", \"&apos;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\");\n//#endregion\nexport { k as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME, O as Report, e as arrayEqual, _ as createUniqueXmlSafeId, g as createXmlSafeId, v as createXmlSafeIdFactory, a as detectMimeTypeFromName, P as escapeXmlAttributeValue, y as getItemSpreadPosition, i as getParentPath, N as getUriBasePath, M as getUriBasename, t as getUrlExtension, x as groupBy, s as isMediaContentMimeType, b as isShallowEqual, o as isXmlBasedMimeType, c as parseContentType, j as removeTrailingSlash, S as shallowMergeIfDefined, A as sortByTitleComparator, r as urlJoin };\n\n//# sourceMappingURL=index.js.map","import type { Manifest } from \"@prose-reader/core\"\n\nconst AUDIO_EXTENSIONS = new Set([\n `mp3`,\n `m4a`,\n `m4b`,\n `aac`,\n `ogg`,\n `oga`,\n `wav`,\n `flac`,\n `opus`,\n])\n\nconst getExtensionFromHref = (href: string) => {\n const [pathname = ``] = href.split(/[?#]/)\n const segments = pathname.split(`/`)\n const basename = segments.at(-1) ?? ``\n const extension = basename.split(`.`).at(-1)\n\n return extension?.toLowerCase()\n}\n\nexport const isAudioSpineItem = (item: Manifest[\"spineItems\"][number]) => {\n const mediaType = item.mediaType?.split(`;`).at(0)?.trim().toLowerCase()\n\n if (mediaType?.startsWith(`audio/`)) {\n return true\n }\n\n const extension = getExtensionFromHref(item.href)\n\n return extension ? AUDIO_EXTENSIONS.has(extension) : false\n}\n","import { mapKeysTo } from \"@prose-reader/core\"\nimport { arrayEqual, isShallowEqual } from \"@prose-reader/shared\"\nimport {\n combineLatest,\n distinctUntilChanged,\n map,\n type Observable,\n shareReplay,\n} from \"rxjs\"\nimport type { AudioEnhancerState, AudioTrack } from \"../types\"\nimport { isAudioSpineItem } from \"../utils\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype PaginationTrackWindow = {\n beginSpineItemIndex: number | undefined\n endSpineItemIndex: number | undefined\n}\n\nconst getTrackAtSpineItemIndex = (\n tracks: AudioTrack[],\n index: number | undefined,\n) => {\n if (index === undefined) return undefined\n\n return tracks.find((track) => track.index === index)\n}\n\nconst getVisibleTracks = (\n tracks: AudioTrack[],\n pagination: PaginationTrackWindow,\n) => {\n const beginTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.beginSpineItemIndex,\n )\n const endTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.endSpineItemIndex,\n )\n\n return [beginTrack, endTrack].filter(\n (track, i, arr): track is AudioTrack =>\n track !== undefined && arr.indexOf(track) === i,\n )\n}\n\nexport function createTrackStreams(\n reader: Pick<AudioControllerReader, \"context\" | \"pagination\">,\n state$: Observable<AudioEnhancerState>,\n) {\n const tracks$ = reader.context.manifest$.pipe(\n map((manifest) =>\n manifest.spineItems.filter(isAudioSpineItem).map((item) => ({\n id: item.id,\n href: item.href,\n index: item.index,\n mediaType: item.mediaType,\n })),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const pagination$ = reader.pagination.state$.pipe(\n mapKeysTo([`beginSpineItemIndex`, `endSpineItemIndex`]),\n distinctUntilChanged(isShallowEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const visibleTrackIds$ = combineLatest([tracks$, pagination$]).pipe(\n map(([tracks, pagination]) =>\n getVisibleTracks(tracks, pagination).map(({ id }) => id),\n ),\n distinctUntilChanged(arrayEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const currentTrack$ = state$.pipe(\n map((state) => state.currentTrack),\n distinctUntilChanged(),\n )\n\n const nextTrack$ = combineLatest([tracks$, pagination$, currentTrack$]).pipe(\n map(([tracks, { endSpineItemIndex }, currentTrack]) => {\n const nextTrackInPaginationWindow =\n currentTrack && endSpineItemIndex !== undefined\n ? tracks.find(\n ({ index }) =>\n index > currentTrack.index && index <= endSpineItemIndex,\n )\n : undefined\n\n const nextTrackAfterCurrentTrack = currentTrack\n ? tracks.find(({ index }) => index > currentTrack.index)\n : undefined\n\n return { nextTrackInPaginationWindow, nextTrackAfterCurrentTrack }\n }),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n tracks$,\n visibleTrackIds$,\n nextTrack$,\n }\n}\n","import { isDefined, ReactiveEntity } from \"@prose-reader/core\"\nimport {\n BehaviorSubject,\n catchError,\n defaultIfEmpty,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n merge,\n type Observable,\n of,\n Subject,\n Subscription,\n share,\n switchMap,\n takeUntil,\n tap,\n withLatestFrom,\n} from \"rxjs\"\nimport type {\n AudioEnhancerState,\n AudioTrack,\n SelectAudioTrackOptions,\n} from \"../types\"\nimport { AudioVisualizer } from \"../visualizer\"\nimport { AudioElementAdapter } from \"./AudioElementAdapter\"\nimport { ResourcesResolver } from \"./ResourcesResolver\"\nimport { createTrackStreams } from \"./trackStreams\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype SelectCommand = {\n trackId: string\n options: SelectAudioTrackOptions\n}\n\ntype DesiredPlayback = {\n shouldPlay: boolean\n trackId: string | undefined\n}\n\nconst initialDesiredPlayback: DesiredPlayback = {\n shouldPlay: false,\n trackId: undefined,\n}\n\nconst initialState: AudioEnhancerState = {\n tracks: [],\n currentTrack: undefined,\n isPlaying: false,\n isLoading: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n}\n\nexport type { AudioControllerReader }\n\nexport class AudioController extends ReactiveEntity<AudioEnhancerState> {\n private readonly reader: AudioControllerReader\n private readonly audio: AudioElementAdapter\n readonly visualizer$: AudioVisualizer\n readonly resourcesResolver = new ResourcesResolver()\n readonly visibleTrackIds$: Observable<string[]>\n\n private readonly playCommandSubject = new Subject<void>()\n private readonly pauseCommandSubject = new Subject<void>()\n private readonly selectCommandSubject = new Subject<SelectCommand>()\n private readonly desiredPlayback$ = new BehaviorSubject(\n initialDesiredPlayback,\n )\n private readonly subscriptions = new Subscription()\n\n constructor(\n reader: AudioControllerReader,\n audio = new AudioElementAdapter(),\n ) {\n super(initialState)\n\n this.reader = reader\n this.audio = audio\n this.visualizer$ = new AudioVisualizer(this.audio.element)\n\n const { tracks$, visibleTrackIds$, nextTrack$ } = createTrackStreams(\n this.reader,\n this.state$,\n )\n\n this.visibleTrackIds$ = visibleTrackIds$\n\n const firstVisibleTrackId$ = this.visibleTrackIds$.pipe(\n map((trackIds) => trackIds[0]),\n distinctUntilChanged(),\n share(),\n )\n\n const visibleTrackReset$ = firstVisibleTrackId$.pipe(\n withLatestFrom(this.state$),\n filter(\n ([trackId, state]) =>\n trackId === undefined &&\n (state.currentTrack?.id !== undefined || state.isLoading),\n ),\n map(([, state]) => state.tracks),\n )\n\n const visibleTrackSelectionIntent$ = firstVisibleTrackId$.pipe(\n filter((trackId): trackId is string => trackId !== undefined),\n withLatestFrom(this.desiredPlayback$),\n map(([trackId, { shouldPlay }]) => ({\n trackId,\n options: {\n navigate: false,\n play: shouldPlay ? true : undefined,\n },\n })),\n )\n\n const tracksChanged$ = tracks$.pipe(\n tap(() => this.resourcesResolver.releaseAll()),\n )\n\n const playbackReset$ = merge(visibleTrackReset$, tracksChanged$).pipe(\n tap((tracks) => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n this.visualizer$.stop({ resetLevels: true })\n this.unmountCurrentSource()\n\n this.mergeCompare({\n tracks,\n currentTrack: undefined,\n isLoading: false,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(undefined)\n }),\n share(),\n )\n\n const playSelectionIntent$ = this.playCommandSubject.pipe(\n filter(() => !this.audio.hasSource),\n map(() => this.state.currentTrack ?? this.state.tracks[0]),\n filter(isDefined),\n map((track) => ({\n trackId: track.id,\n options: { navigate: false, play: true },\n })),\n )\n\n const resumePlayback$ = this.playCommandSubject.pipe(\n filter(() => this.audio.hasSource),\n tap(() => {\n this.emitDesiredPlayback({\n shouldPlay: true,\n trackId: this.state.currentTrack?.id,\n })\n }),\n )\n\n const endedSelectionIntent$ = this.audio.ended$.pipe(\n withLatestFrom(nextTrack$),\n switchMap(\n ([, { nextTrackAfterCurrentTrack, nextTrackInPaginationWindow }]) => {\n this.visualizer$.stop({ resetLevels: true })\n\n if (nextTrackInPaginationWindow) {\n return of({\n trackId: nextTrackInPaginationWindow.id,\n options: { navigate: false, play: true },\n })\n }\n\n if (!nextTrackAfterCurrentTrack) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }\n\n this.reader.navigation.goToRightOrBottomSpineItem()\n\n return EMPTY\n },\n ),\n )\n\n const selection$ = merge(\n this.selectCommandSubject,\n visibleTrackSelectionIntent$,\n endedSelectionIntent$,\n playSelectionIntent$,\n ).pipe(\n withLatestFrom(this.state$),\n filter(([selectionIntent, state]) =>\n state.tracks.some(({ id }) => id === selectionIntent.trackId),\n ),\n map(([selectionIntent, state]) => ({\n selectionIntent,\n state,\n isReselectionWhileLoading:\n state.currentTrack?.id === selectionIntent.trackId && state.isLoading,\n })),\n tap(({ selectionIntent, state, isReselectionWhileLoading }) => {\n if (selectionIntent.options.navigate !== false) {\n this.reader.navigation.navigate({\n spineItem: selectionIntent.trackId,\n animation: `turn`,\n })\n }\n\n if (\n isReselectionWhileLoading &&\n selectionIntent.options.play !== undefined\n ) {\n this.emitDesiredPlayback({\n shouldPlay: selectionIntent.options.play,\n trackId: state.currentTrack?.id,\n })\n }\n }),\n filter(({ isReselectionWhileLoading }) => !isReselectionWhileLoading),\n switchMap(({ selectionIntent: { trackId, options } }) =>\n this.selectTrack$({ trackId, options, playbackReset$ }),\n ),\n )\n\n const playback$ = merge(resumePlayback$, selection$).pipe(\n withLatestFrom(this.desiredPlayback$),\n switchMap(([, { shouldPlay, trackId }]) => {\n if (\n !shouldPlay ||\n !this.audio.hasSource ||\n this.state.currentTrack?.id !== trackId\n ) {\n return EMPTY\n }\n\n this.mergeCompare({ hasError: false })\n\n return this.audio.play$().pipe(\n catchError(() => {\n this.mergeCompare({ hasError: true })\n\n return EMPTY\n }),\n )\n }),\n )\n\n this.subscriptions.add(playbackReset$.subscribe())\n this.subscriptions.add(playback$.subscribe())\n\n this.subscriptions.add(\n this.pauseCommandSubject.subscribe(() => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }),\n )\n\n this.subscriptions.add(\n this.audio.isPlaying$.subscribe((isPlaying) => {\n this.mergeCompare({ isPlaying })\n\n if (!isPlaying) {\n this.visualizer$.stop()\n return\n }\n\n if (!this.state.currentTrack) return\n\n this.visualizer$.start(this.state.currentTrack)\n }),\n )\n\n this.subscriptions.add(\n this.audio.metrics$.subscribe(({ currentTime, duration }) => {\n this.mergeCompare({ currentTime, duration })\n }),\n )\n }\n\n get state() {\n return this.value\n }\n\n get visualizer() {\n return this.visualizer$.value\n }\n\n private resetTrackSelection({\n currentTrack,\n isLoading,\n }: {\n currentTrack: AudioTrack\n isLoading: boolean\n }) {\n this.mergeCompare({\n currentTrack,\n isLoading,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(currentTrack?.id)\n }\n\n private emitDesiredPlayback({ shouldPlay, trackId }: DesiredPlayback) {\n this.desiredPlayback$.next({\n shouldPlay,\n trackId: shouldPlay ? trackId : undefined,\n })\n\n if (!shouldPlay) {\n this.audio.pause()\n }\n }\n\n private unmountCurrentSource() {\n if (!this.audio.hasSource) return\n\n const trackId = this.state.currentTrack?.id\n\n if (!this.audio.paused) {\n this.audio.pause()\n }\n\n this.audio.unloadSource()\n\n if (trackId) {\n this.resourcesResolver.releaseTrackSource(trackId)\n }\n }\n\n private mountSource(source: string) {\n this.unmountCurrentSource()\n this.audio.loadSource(source)\n }\n\n private resolveTrackSource(track: AudioTrack) {\n const spineItem = this.reader.spineItemsManager.get(track.index)\n\n if (!spineItem) return EMPTY\n\n return this.resourcesResolver.getTrackResourceUrl$(\n track,\n spineItem.resourcesHandler,\n )\n }\n\n private selectTrack$({\n trackId,\n options,\n playbackReset$,\n }: {\n trackId: string\n options: SelectAudioTrackOptions\n playbackReset$: Observable<AudioTrack[]>\n }) {\n const track = this.state.tracks.find(({ id }) => id === trackId)\n\n if (!track) return EMPTY\n\n const currentTrack = this.state.currentTrack\n const shouldPlay =\n options.play ?? (!this.audio.paused && currentTrack !== undefined)\n\n this.emitDesiredPlayback({ shouldPlay, trackId: track.id })\n\n if (currentTrack?.id === track.id && this.audio.hasSource) {\n this.visualizer$.setTrack(track.id)\n\n return shouldPlay ? of(undefined) : EMPTY\n }\n\n this.unmountCurrentSource()\n\n this.resetTrackSelection({\n currentTrack: track,\n isLoading: true,\n })\n\n return this.resolveTrackSource(track).pipe(\n map((source) => ({ source })),\n defaultIfEmpty({ source: undefined }),\n catchError(() => of({ source: undefined })),\n takeUntil(playbackReset$),\n tap(({ source }) => {\n if (!source) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n } else {\n this.mountSource(source)\n }\n\n this.mergeCompare({ isLoading: false })\n }),\n filter(({ source }) => source !== undefined),\n map(() => undefined),\n )\n }\n\n select(trackId: string, options: SelectAudioTrackOptions = {}) {\n this.selectCommandSubject.next({ trackId, options })\n }\n\n play() {\n this.playCommandSubject.next()\n }\n\n pause() {\n this.pauseCommandSubject.next()\n }\n\n toggle() {\n if (this.audio.paused) {\n this.play()\n return\n }\n\n this.pause()\n }\n\n setCurrentTime(value: number) {\n this.mergeCompare({ currentTime: value })\n this.audio.setCurrentTime(value)\n }\n\n destroy() {\n this.subscriptions.unsubscribe()\n this.playCommandSubject.complete()\n this.pauseCommandSubject.complete()\n this.selectCommandSubject.complete()\n this.desiredPlayback$.complete()\n this.visualizer$.destroy()\n this.unmountCurrentSource()\n this.resourcesResolver.destroy()\n\n super.destroy()\n }\n}\n","import { DocumentRenderer, setStylePropertyIfChanged } from \"@prose-reader/core\"\nimport { EMPTY, of } from \"rxjs\"\n\nexport class AudioRenderer extends DocumentRenderer {\n onCreateDocument() {\n const ownerDocument = this.containerElement.ownerDocument\n const rootElement = ownerDocument.createElement(`div`)\n\n rootElement.style.cssText = `\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: block;\n `\n rootElement.setAttribute(`data-prose-reader-audio-page`, this.item.id)\n\n this.setDocumentContainer(rootElement)\n\n return of(rootElement)\n }\n\n onLoadDocument() {\n this.attach()\n\n return EMPTY\n }\n\n onUnload() {\n this.detach()\n\n return EMPTY\n }\n\n onLayout() {\n const { width, height } = this.viewport.pageSize\n const rootElement = this.documentContainer\n\n if (rootElement) {\n setStylePropertyIfChanged(rootElement.style, `width`, `${width}px`)\n setStylePropertyIfChanged(rootElement.style, `height`, `${height}px`)\n }\n\n return of({ width, height })\n }\n\n onRenderHeadless() {\n return EMPTY\n }\n\n getDocumentFrame() {\n return undefined\n }\n\n /**\n * Audio spine items are always pre-paginated (one track per page).\n * Mixed audio/text books with reflowable audio chapters may need\n * to revisit this if that use case arises.\n */\n get renditionLayout(): `pre-paginated` {\n return `pre-paginated`\n }\n}\n","import type { CreateReaderOptions, Reader } from \"@prose-reader/core\"\nimport { AudioController } from \"./playback/AudioController\"\nimport { AudioRenderer } from \"./renderer/AudioRenderer\"\nimport type { AudioEnhancerAPI } from \"./types\"\nimport { isAudioSpineItem } from \"./utils\"\n\nexport const audioEnhancer =\n <InheritOptions extends CreateReaderOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & AudioEnhancerAPI => {\n const readerOptions = { ...options }\n\n const reader = next({\n ...readerOptions,\n getRenderer(item) {\n const maybeFactory = options.getRenderer?.(item)\n\n if (maybeFactory) {\n return maybeFactory\n }\n\n if (isAudioSpineItem(item)) {\n return (params) => new AudioRenderer(params)\n }\n\n return undefined\n },\n })\n\n const controller = new AudioController(reader)\n\n const destroy = () => {\n controller.destroy()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_AUDIO: true,\n destroy,\n audio: {\n state$: controller.state$,\n visualizer$: controller.visualizer$,\n visibleTrackIds$: controller.visibleTrackIds$,\n get state() {\n return controller.state\n },\n get visualizer() {\n return controller.visualizer\n },\n isAudioRenderer: (renderer) => renderer instanceof AudioRenderer,\n play: () => controller.play(),\n pause: () => controller.pause(),\n toggle: () => controller.toggle(),\n setCurrentTime: (value) => controller.setCurrentTime(value),\n select: (trackId, options) => controller.select(trackId, options),\n },\n }\n }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/visualizer/levels.ts","../src/visualizer/VisualizerAudioGraph.ts","../src/visualizer/AudioVisualizer.ts","../src/playback/AudioElementAdapter.ts","../src/playback/ResourcesResolver.ts","../../shared/dist/index.js","../src/utils.ts","../src/playback/trackStreams.ts","../src/playback/AudioController.ts","../src/renderer/AudioRenderer.ts","../src/audioEnhancer.ts"],"sourcesContent":["const AUDIO_VISUALIZER_LEVEL_COUNT = 80\nconst AUDIO_VISUALIZER_NOISE_FLOOR = 0.035\n\nexport const getIdleVisualizerLevels = () =>\n Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, () => 0)\n\nexport const getVisualizerLevels = (data: ArrayLike<number>) => {\n if (data.length === 0) return getIdleVisualizerLevels()\n\n const bucketSize = Math.max(\n 1,\n Math.floor(data.length / AUDIO_VISUALIZER_LEVEL_COUNT),\n )\n\n return Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, (_, index) => {\n const start = index * bucketSize\n const end = Math.min(data.length, start + bucketSize)\n\n if (start >= data.length || start === end) return 0\n\n let total = 0\n\n for (let cursor = start; cursor < end; cursor += 1) {\n total += data[cursor] ?? 0\n }\n\n const average = total / (end - start)\n const normalizedAverage = average / 255\n const gatedAverage =\n (normalizedAverage - AUDIO_VISUALIZER_NOISE_FLOOR) /\n (1 - AUDIO_VISUALIZER_NOISE_FLOOR)\n\n return Math.max(0, Math.min(1, gatedAverage))\n })\n}\n","import { getIdleVisualizerLevels, getVisualizerLevels } from \"./levels\"\n\nconst AUDIO_VISUALIZER_FFT_SIZE = 256\n\nexport class VisualizerAudioGraph {\n private audioContext: AudioContext | undefined\n private audioSourceNode: MediaElementAudioSourceNode | undefined\n private analyserNode: AnalyserNode | undefined\n private frequencyData: Uint8Array<ArrayBuffer> | undefined\n\n constructor(private readonly audioElement: HTMLAudioElement) {}\n\n async resumeIfNeeded() {\n if (!this.ensure()) {\n return false\n }\n\n const audioContext = this.audioContext\n\n if (!audioContext) {\n return false\n }\n\n if (audioContext.state !== `suspended`) {\n return true\n }\n\n try {\n await audioContext.resume()\n\n return true\n } catch {\n return false\n }\n }\n\n readLevels() {\n if (!this.analyserNode || !this.frequencyData) {\n return getIdleVisualizerLevels()\n }\n\n this.analyserNode.getByteFrequencyData(this.frequencyData)\n\n return getVisualizerLevels(this.frequencyData)\n }\n\n destroy() {\n this.audioSourceNode?.disconnect()\n this.analyserNode?.disconnect()\n this.audioContext?.close().catch(() => undefined)\n this.audioContext = undefined\n this.audioSourceNode = undefined\n this.analyserNode = undefined\n this.frequencyData = undefined\n }\n\n private ensure() {\n if (this.audioContext && this.analyserNode && this.frequencyData) {\n return true\n }\n\n if (typeof window === `undefined` || !window.AudioContext) {\n return false\n }\n\n const audioContext = new window.AudioContext()\n const audioSourceNode = audioContext.createMediaElementSource(\n this.audioElement,\n )\n const analyserNode = audioContext.createAnalyser()\n analyserNode.fftSize = AUDIO_VISUALIZER_FFT_SIZE\n analyserNode.smoothingTimeConstant = 0.8\n audioSourceNode.connect(analyserNode)\n analyserNode.connect(audioContext.destination)\n\n this.audioContext = audioContext\n this.audioSourceNode = audioSourceNode\n this.analyserNode = analyserNode\n this.frequencyData = new Uint8Array(analyserNode.frequencyBinCount)\n\n return true\n }\n}\n","import { ReactiveEntity } from \"@prose-reader/core\"\nimport {\n animationFrames,\n BehaviorSubject,\n concat,\n defaultIfEmpty,\n defer,\n distinctUntilChanged,\n EMPTY,\n from,\n map,\n of,\n Subscription,\n switchMap,\n} from \"rxjs\"\nimport type { AudioTrack, AudioVisualizerState } from \"../types\"\nimport { getIdleVisualizerLevels } from \"./levels\"\nimport { VisualizerAudioGraph } from \"./VisualizerAudioGraph\"\n\ntype AudioVisualizerPlaybackState = {\n trackId: string | undefined\n isRunning: boolean\n resetLevels: boolean\n}\n\nexport class AudioVisualizer extends ReactiveEntity<AudioVisualizerState> {\n private readonly audioGraph: VisualizerAudioGraph\n private readonly playbackState$ =\n new BehaviorSubject<AudioVisualizerPlaybackState>({\n trackId: undefined,\n isRunning: false,\n resetLevels: false,\n })\n private readonly subscriptions = new Subscription()\n\n constructor(audioElement: HTMLAudioElement) {\n super({\n levels: getIdleVisualizerLevels(),\n isActive: false,\n trackId: undefined,\n })\n this.audioGraph = new VisualizerAudioGraph(audioElement)\n\n this.subscriptions.add(\n this.playbackState$\n .pipe(\n distinctUntilChanged(\n (previous, next) =>\n previous.trackId === next.trackId &&\n previous.isRunning === next.isRunning &&\n previous.resetLevels === next.resetLevels,\n ),\n switchMap(({ trackId, isRunning, resetLevels }) => {\n if (!trackId || !isRunning) {\n return of({\n trackId,\n isActive: false,\n ...(resetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n })\n }\n\n const shouldResetLevels = this.value.trackId !== trackId\n\n return concat(\n of({\n trackId,\n isActive: true,\n ...(shouldResetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n }),\n this.createLevels$().pipe(\n map((levels) => ({\n trackId,\n levels,\n isActive: true,\n })),\n defaultIfEmpty({\n trackId,\n isActive: false,\n levels: getIdleVisualizerLevels(),\n }),\n ),\n )\n }),\n )\n .subscribe((value) => {\n this.mergeCompare(value)\n }),\n )\n }\n\n start(currentTrack: AudioTrack | undefined) {\n if (!currentTrack) return\n\n this.playbackState$.next({\n trackId: currentTrack.id,\n isRunning: true,\n resetLevels: false,\n })\n }\n\n setTrack(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: false,\n })\n }\n\n reset(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: true,\n })\n }\n\n stop({ resetLevels = false }: { resetLevels?: boolean } = {}) {\n this.playbackState$.next({\n trackId: this.playbackState$.value.trackId,\n isRunning: false,\n resetLevels,\n })\n }\n\n override destroy() {\n this.stop({\n resetLevels: true,\n })\n this.subscriptions.unsubscribe()\n this.playbackState$.complete()\n this.audioGraph.destroy()\n\n super.destroy()\n }\n\n private createLevels$() {\n return defer(() => {\n return from(this.audioGraph.resumeIfNeeded()).pipe(\n switchMap((isReady) =>\n isReady\n ? animationFrames().pipe(map(() => this.audioGraph.readLevels()))\n : EMPTY,\n ),\n )\n })\n }\n}\n","import {\n defer,\n from,\n fromEvent,\n map,\n merge,\n type Observable,\n retry,\n share,\n take,\n} from \"rxjs\"\n\nexport type AudioMetrics = {\n currentTime: number\n duration: number | undefined\n}\n\nexport class AudioElementAdapter {\n readonly element: HTMLAudioElement\n readonly canPlay$: Observable<Event>\n readonly ended$: Observable<Event>\n readonly isPlaying$: Observable<boolean>\n readonly metrics$: Observable<AudioMetrics>\n\n constructor() {\n this.element = document.createElement(`audio`)\n this.element.preload = `metadata`\n\n this.canPlay$ = fromEvent(this.element, `canplay`).pipe(share())\n this.ended$ = fromEvent(this.element, `ended`).pipe(share())\n\n this.isPlaying$ = merge(\n fromEvent(this.element, `play`).pipe(map(() => true)),\n fromEvent(this.element, `pause`).pipe(map(() => false)),\n ).pipe(share())\n\n this.metrics$ = merge(\n fromEvent(this.element, `timeupdate`),\n fromEvent(this.element, `seeking`),\n fromEvent(this.element, `seeked`),\n fromEvent(this.element, `loadedmetadata`),\n fromEvent(this.element, `durationchange`),\n this.canPlay$,\n ).pipe(\n map(() => ({\n currentTime: this.element.currentTime,\n duration: Number.isFinite(this.element.duration)\n ? this.element.duration\n : undefined,\n })),\n share(),\n )\n }\n\n get paused() {\n return this.element.paused\n }\n\n get hasSource() {\n return this.element.hasAttribute(`src`)\n }\n\n play$() {\n return defer(() => from(this.element.play())).pipe(\n retry({\n count: 1,\n delay: () => this.canPlay$.pipe(take(1)),\n }),\n )\n }\n\n pause() {\n this.element.pause()\n }\n\n loadSource(src: string) {\n this.element.src = src\n this.element.load()\n }\n\n unloadSource() {\n this.element.removeAttribute(`src`)\n this.element.load()\n }\n\n setCurrentTime(value: number) {\n this.element.currentTime = value\n }\n}\n","import type { ResourceHandler } from \"@prose-reader/core\"\nimport { from, map, of, switchMap } from \"rxjs\"\nimport type { AudioTrack } from \"../types\"\n\ntype CachedTrackSource = {\n url: string\n release?: () => void\n}\n\nexport class ResourcesResolver {\n private readonly cachedSourceByTrackId = new Map<string, CachedTrackSource>()\n\n public hasCachedSource(trackId: string): boolean {\n return this.cachedSourceByTrackId.has(trackId)\n }\n\n public getTrackResourceUrl$ = (\n track: AudioTrack,\n resourcesHandler: Pick<ResourceHandler, \"getResource\">,\n ) => {\n const cachedSource = this.cachedSourceByTrackId.get(track.id)\n\n if (cachedSource) {\n return of(cachedSource.url)\n }\n\n const resource$ = from(resourcesHandler.getResource())\n\n return resource$.pipe(\n switchMap((resource) => {\n if (resource instanceof URL) {\n this.cachedSourceByTrackId.set(track.id, {\n url: resource.href,\n })\n\n return of(resource.href)\n }\n\n if (resource instanceof Response) {\n return from(resource.blob()).pipe(\n map((blob) => {\n const objectUrl = URL.createObjectURL(blob)\n\n this.cachedSourceByTrackId.set(track.id, {\n url: objectUrl,\n release: () => {\n URL.revokeObjectURL(objectUrl)\n },\n })\n\n return objectUrl\n }),\n )\n }\n\n this.cachedSourceByTrackId.set(track.id, {\n url: track.href,\n })\n\n return of(track.href)\n }),\n )\n }\n\n public releaseTrackSource(trackId: string) {\n const cachedSource = this.cachedSourceByTrackId.get(trackId)\n\n if (!cachedSource) return\n\n this.cachedSourceByTrackId.delete(trackId)\n cachedSource.release?.()\n }\n\n public releaseAll() {\n for (const trackId of this.cachedSourceByTrackId.keys()) {\n this.releaseTrackSource(trackId)\n }\n }\n\n public destroy() {\n this.releaseAll()\n }\n}\n","//#region src/array.ts\nvar e = (e, t) => e === t || e.length === t.length && e.every((e, n) => e === t[n]), t = (e) => e.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction n(e) {\n\tlet t = [];\n\tif (e.length === 0) return \"\";\n\tif (typeof e[0] != \"string\") throw TypeError(`Url must be a string. Received ${e[0]}`);\n\te[0].match(/^[^/:]+:\\/*$/) && e.length > 1 && (e[0] = e.shift() + e[0]), e[0].match(/^file:\\/\\/\\//) ? e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n\tfor (let n = 0; n < e.length; n++) {\n\t\tlet r = e[n];\n\t\tif (typeof r != \"string\") throw TypeError(`Url must be a string. Received ${r}`);\n\t\tr !== \"\" && (n > 0 && (r = r.replace(/^[/]+/, \"\")), r = n < e.length - 1 ? r.replace(/[/]+$/, \"\") : r.replace(/[/]+$/, \"/\"), t.push(r));\n\t}\n\tlet n = t.join(\"/\");\n\tn = n.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n\tlet r = n.split(\"?\");\n\treturn n = r.shift() + (r.length > 0 ? \"?\" : \"\") + r.join(\"&\"), n;\n}\nfunction r(...e) {\n\treturn n(Array.from(Array.isArray(e[0]) ? e[0] : e));\n}\nfunction i(e) {\n\tlet t = e.split(\"/\");\n\treturn t.pop(), t.join(\"/\");\n}\n//#endregion\n//#region src/contentType.ts\nvar a = (e) => {\n\tswitch (t(e)) {\n\t\tcase \"png\": return \"image/png\";\n\t\tcase \"jpg\": return \"image/jpg\";\n\t\tcase \"jpeg\": return \"image/jpeg\";\n\t\tcase \"txt\": return \"text/plain\";\n\t\tcase \"webp\": return \"image/webp\";\n\t\tcase \"xhtml\": return \"application/xhtml+xml\";\n\t\tcase \"mp3\": return \"audio/mpeg\";\n\t\tcase \"m4a\":\n\t\tcase \"m4b\": return \"audio/mp4\";\n\t\tcase \"aac\": return \"audio/aac\";\n\t\tcase \"ogg\":\n\t\tcase \"oga\": return \"audio/ogg\";\n\t\tcase \"wav\": return \"audio/wav\";\n\t\tcase \"flac\": return \"audio/flac\";\n\t\tcase \"opus\": return \"audio/opus\";\n\t}\n}, o = ({ mimeType: e, uri: t }) => (e ?? a(t ?? \"\"))?.startsWith(\"application/xhtml+xml\"), s = (e) => e.startsWith(\"image/\") || e.startsWith(\"audio/\") || e.startsWith(\"video/\"), c = (e) => {\n\tif (!e.length) return;\n\tlet t = e.indexOf(\";\");\n\treturn t >= 0 ? e.substring(0, t) : e;\n}, l = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/, u = /^[A-Za-z0-9_]/, d = /^xml/i, f = /[^A-Za-z0-9_.-]+/g, p = \"_\", m = (e) => e.replace(/^_+|_+$/g, \"\"), h = (e) => m(e.trim().replaceAll(\"/\", \"_\").replace(f, \"_\")), g = (e) => {\n\tif (l.test(e) && !d.test(e)) return e;\n\tlet t = h(e), n = t && !d.test(t) ? t : `${p}${t}`;\n\treturn u.test(n) ? n : `${p}${n}`;\n}, _ = (e, t) => {\n\tlet n = g(e);\n\tif (!t.has(n)) return t.add(n), n;\n\tlet r = 2, i = `${n}-${r}`;\n\tfor (; t.has(i);) r += 1, i = `${n}-${r}`;\n\treturn t.add(i), i;\n}, v = () => {\n\tlet e = /* @__PURE__ */ new Set();\n\treturn (t) => _(t, e);\n}, y = (e) => {\n\tlet t = e.pageSpreadLeft === !0;\n\tif (t !== (e.pageSpreadRight === !0)) return t ? \"left\" : \"right\";\n}, b = (e, t, n) => {\n\tif (Object.is(e, t)) return !0;\n\tif (typeof e != \"object\" || !e || typeof t != \"object\" || !t) return !1;\n\tlet r = Object.keys(e), i = Object.keys(t);\n\tif (r.length !== i.length) return !1;\n\tlet a = n?.customEqual ?? Object.is, o = e, s = t;\n\tfor (let e of r) if (!Object.hasOwn(s, e) || !a(o[e], s[e])) return !1;\n\treturn !0;\n}, x = (e, t) => e.reduce((e, n) => {\n\tlet r = t(n);\n\treturn e[r] || (e[r] = []), e[r].push(n), e;\n}, {});\nfunction S(e, t) {\n\tlet n = { ...e };\n\tfor (let r in t) if (Object.hasOwn(t, r)) {\n\t\tlet i = t[r];\n\t\t(i !== void 0 || !(r in e)) && (n[r] = i);\n\t}\n\treturn n;\n}\n//#endregion\n//#region src/report.ts\nfunction C() {\n\tlet e = globalThis.__PROSE_READER_DEBUG;\n\treturn e === !0 || e === \"true\";\n}\nvar w = (e, t) => typeof e == \"boolean\" ? {\n\tenabled: e,\n\toptions: t\n} : {\n\tenabled: e?.enabled,\n\toptions: e ?? t\n}, T = (e) => e.at(-1)?.color, E = (e) => {\n\tif (!e.length) return;\n\tif (!e.some((e) => e.color)) return [e.map((e) => e.label).join(\" \")];\n\tlet t = \"\", n = [];\n\tfor (let r of e) t += `%c${t ? ` ${r.label}` : r.label}`, n.push(r.color ? `color: ${r.color}` : \"\");\n\treturn [t, ...n];\n}, D = (e = [], t = C()) => {\n\tlet n = t, r = {\n\t\tenable: (e) => {\n\t\t\ta(e);\n\t\t},\n\t\tnamespace: (t, r, i) => {\n\t\t\tlet a = w(r, i), o = a.options?.color ?? T(e);\n\t\t\treturn D([...e, {\n\t\t\t\tlabel: `[${t}]`,\n\t\t\t\tcolor: o\n\t\t\t}], a.enabled ?? n);\n\t\t},\n\t\tisEnabled: () => n,\n\t\tdebug: () => {},\n\t\tinfo: () => {},\n\t\tlog: () => {},\n\t\tgroupCollapsed: () => {},\n\t\tgroupEnd: () => {},\n\t\tgetGroupArgs: (t) => {\n\t\t\tlet n = E(e);\n\t\t\tif (!n) return [t];\n\t\t\tlet [r, ...i] = n;\n\t\t\treturn [`${r} ${t}`, ...i];\n\t\t},\n\t\twarn: () => {},\n\t\terror: () => {}\n\t}, i = (t) => {\n\t\tif (!t) {\n\t\t\tr.debug = () => {}, r.info = () => {}, r.log = () => {}, r.groupCollapsed = () => {}, r.groupEnd = () => {}, r.warn = () => {}, r.error = () => {};\n\t\t\treturn;\n\t\t}\n\t\tlet n = E(e) ?? [];\n\t\tr.debug = Function.prototype.bind.call(console.debug, console, ...n), r.info = Function.prototype.bind.call(console.info, console, ...n), r.log = Function.prototype.bind.call(console.log, console, ...n), r.groupCollapsed = Function.prototype.bind.call(console.groupCollapsed, console), r.groupEnd = Function.prototype.bind.call(console.groupEnd, console), r.warn = Function.prototype.bind.call(console.warn, console, ...n), r.error = Function.prototype.bind.call(console.error, console, ...n);\n\t}, a = (e) => {\n\t\tn !== e && (n = e, i(n));\n\t};\n\treturn i(n), r;\n}, O = D(), k = \"prose-reader-resource-error\", A = (e, t) => {\n\tlet n = e.split(/(\\d+)/), r = t.split(/(\\d+)/);\n\tfor (let e = 0, t = n.length; e < t; e++) if (n[e] !== r[e]) return n[e]?.match(/\\d/) ? (n[e] || \"\") - +(r[e] || \"\") : (n[e] || \"\").localeCompare(r[e] || \"\");\n\treturn 1;\n}, j = (e) => e.endsWith(\"/\") ? e.slice(0, -1) : e, M = (e) => {\n\tlet t = j(e);\n\treturn t.substring(t.lastIndexOf(\"/\") + 1) || t;\n}, N = (e) => {\n\tlet t = e.lastIndexOf(\"/\");\n\treturn t >= 0 ? e.substring(0, t) : \"\";\n}, P = (e) => e.replaceAll(\"&\", \"&amp;\").replaceAll(\"\\\"\", \"&quot;\").replaceAll(\"'\", \"&apos;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\");\n//#endregion\nexport { k as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME, O as Report, e as arrayEqual, _ as createUniqueXmlSafeId, g as createXmlSafeId, v as createXmlSafeIdFactory, a as detectMimeTypeFromName, P as escapeXmlAttributeValue, y as getItemSpreadPosition, i as getParentPath, N as getUriBasePath, M as getUriBasename, t as getUrlExtension, x as groupBy, s as isMediaContentMimeType, b as isShallowEqual, o as isXmlBasedMimeType, c as parseContentType, j as removeTrailingSlash, S as shallowMergeIfDefined, A as sortByTitleComparator, r as urlJoin };\n\n//# sourceMappingURL=index.js.map","import type { Manifest } from \"@prose-reader/core\"\n\nconst AUDIO_EXTENSIONS = new Set([\n `mp3`,\n `m4a`,\n `m4b`,\n `aac`,\n `ogg`,\n `oga`,\n `wav`,\n `flac`,\n `opus`,\n])\n\nconst getExtensionFromHref = (href: string) => {\n const [pathname = ``] = href.split(/[?#]/)\n const segments = pathname.split(`/`)\n const basename = segments.at(-1) ?? ``\n const extension = basename.split(`.`).at(-1)\n\n return extension?.toLowerCase()\n}\n\nexport const isAudioSpineItem = (item: Manifest[\"spineItems\"][number]) => {\n const mediaType = item.mediaType?.split(`;`).at(0)?.trim().toLowerCase()\n\n if (mediaType?.startsWith(`audio/`)) {\n return true\n }\n\n const extension = getExtensionFromHref(item.href)\n\n return extension ? AUDIO_EXTENSIONS.has(extension) : false\n}\n","import { mapKeysTo } from \"@prose-reader/core\"\nimport { arrayEqual, isShallowEqual } from \"@prose-reader/shared\"\nimport {\n combineLatest,\n distinctUntilChanged,\n map,\n type Observable,\n of,\n shareReplay,\n} from \"rxjs\"\nimport type { AudioEnhancerState, AudioTrack } from \"../types\"\nimport { isAudioSpineItem } from \"../utils\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype PaginationTrackWindow = {\n beginSpineItemIndex: number | undefined\n endSpineItemIndex: number | undefined\n}\n\nconst getTrackAtSpineItemIndex = (\n tracks: AudioTrack[],\n index: number | undefined,\n) => {\n if (index === undefined) return undefined\n\n return tracks.find((track) => track.index === index)\n}\n\nconst getVisibleTracks = (\n tracks: AudioTrack[],\n pagination: PaginationTrackWindow,\n) => {\n const beginTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.beginSpineItemIndex,\n )\n const endTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.endSpineItemIndex,\n )\n\n return [beginTrack, endTrack].filter(\n (track, i, arr): track is AudioTrack =>\n track !== undefined && arr.indexOf(track) === i,\n )\n}\n\nexport function createTrackStreams(\n reader: Pick<AudioControllerReader, \"context\" | \"pagination\">,\n state$: Observable<AudioEnhancerState>,\n) {\n const tracks = reader.context.manifest.spineItems\n .filter(isAudioSpineItem)\n .map((item) => ({\n id: item.id,\n href: item.href,\n index: item.index,\n mediaType: item.mediaType,\n }))\n const tracks$ = of(tracks)\n\n const pagination$ = reader.pagination.state$.pipe(\n mapKeysTo([`beginSpineItemIndex`, `endSpineItemIndex`]),\n distinctUntilChanged(isShallowEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const visibleTrackIds$ = combineLatest([tracks$, pagination$]).pipe(\n map(([tracks, pagination]) =>\n getVisibleTracks(tracks, pagination).map(({ id }) => id),\n ),\n distinctUntilChanged(arrayEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const currentTrack$ = state$.pipe(\n map((state) => state.currentTrack),\n distinctUntilChanged(),\n )\n\n const nextTrack$ = combineLatest([tracks$, pagination$, currentTrack$]).pipe(\n map(([tracks, { endSpineItemIndex }, currentTrack]) => {\n const nextTrackInPaginationWindow =\n currentTrack && endSpineItemIndex !== undefined\n ? tracks.find(\n ({ index }) =>\n index > currentTrack.index && index <= endSpineItemIndex,\n )\n : undefined\n\n const nextTrackAfterCurrentTrack = currentTrack\n ? tracks.find(({ index }) => index > currentTrack.index)\n : undefined\n\n return { nextTrackInPaginationWindow, nextTrackAfterCurrentTrack }\n }),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n tracks$,\n visibleTrackIds$,\n nextTrack$,\n }\n}\n","import { isDefined, ReactiveEntity } from \"@prose-reader/core\"\nimport {\n BehaviorSubject,\n catchError,\n defaultIfEmpty,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n merge,\n type Observable,\n of,\n Subject,\n Subscription,\n share,\n switchMap,\n takeUntil,\n tap,\n withLatestFrom,\n} from \"rxjs\"\nimport type {\n AudioEnhancerState,\n AudioTrack,\n SelectAudioTrackOptions,\n} from \"../types\"\nimport { AudioVisualizer } from \"../visualizer\"\nimport { AudioElementAdapter } from \"./AudioElementAdapter\"\nimport { ResourcesResolver } from \"./ResourcesResolver\"\nimport { createTrackStreams } from \"./trackStreams\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype SelectCommand = {\n trackId: string\n options: SelectAudioTrackOptions\n}\n\ntype DesiredPlayback = {\n shouldPlay: boolean\n trackId: string | undefined\n}\n\nconst initialDesiredPlayback: DesiredPlayback = {\n shouldPlay: false,\n trackId: undefined,\n}\n\nconst initialState: AudioEnhancerState = {\n tracks: [],\n currentTrack: undefined,\n isPlaying: false,\n isLoading: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n}\n\nexport type { AudioControllerReader }\n\nexport class AudioController extends ReactiveEntity<AudioEnhancerState> {\n private readonly reader: AudioControllerReader\n private readonly audio: AudioElementAdapter\n readonly visualizer$: AudioVisualizer\n readonly resourcesResolver = new ResourcesResolver()\n readonly visibleTrackIds$: Observable<string[]>\n\n private readonly playCommandSubject = new Subject<void>()\n private readonly pauseCommandSubject = new Subject<void>()\n private readonly selectCommandSubject = new Subject<SelectCommand>()\n private readonly desiredPlayback$ = new BehaviorSubject(\n initialDesiredPlayback,\n )\n private readonly subscriptions = new Subscription()\n\n constructor(\n reader: AudioControllerReader,\n audio = new AudioElementAdapter(),\n ) {\n super(initialState)\n\n this.reader = reader\n this.audio = audio\n this.visualizer$ = new AudioVisualizer(this.audio.element)\n\n const { tracks$, visibleTrackIds$, nextTrack$ } = createTrackStreams(\n this.reader,\n this.state$,\n )\n\n this.visibleTrackIds$ = visibleTrackIds$\n\n const firstVisibleTrackId$ = this.visibleTrackIds$.pipe(\n map((trackIds) => trackIds[0]),\n distinctUntilChanged(),\n share(),\n )\n\n const visibleTrackReset$ = firstVisibleTrackId$.pipe(\n withLatestFrom(this.state$),\n filter(\n ([trackId, state]) =>\n trackId === undefined &&\n (state.currentTrack?.id !== undefined || state.isLoading),\n ),\n map(([, state]) => state.tracks),\n )\n\n const visibleTrackSelectionIntent$ = firstVisibleTrackId$.pipe(\n filter((trackId): trackId is string => trackId !== undefined),\n withLatestFrom(this.desiredPlayback$),\n map(([trackId, { shouldPlay }]) => ({\n trackId,\n options: {\n navigate: false,\n play: shouldPlay ? true : undefined,\n },\n })),\n )\n\n const tracksChanged$ = tracks$.pipe(\n tap(() => this.resourcesResolver.releaseAll()),\n )\n\n const playbackReset$ = merge(visibleTrackReset$, tracksChanged$).pipe(\n tap((tracks) => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n this.visualizer$.stop({ resetLevels: true })\n this.unmountCurrentSource()\n\n this.mergeCompare({\n tracks,\n currentTrack: undefined,\n isLoading: false,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(undefined)\n }),\n share(),\n )\n\n const playSelectionIntent$ = this.playCommandSubject.pipe(\n filter(() => !this.audio.hasSource),\n map(() => this.state.currentTrack ?? this.state.tracks[0]),\n filter(isDefined),\n map((track) => ({\n trackId: track.id,\n options: { navigate: false, play: true },\n })),\n )\n\n const resumePlayback$ = this.playCommandSubject.pipe(\n filter(() => this.audio.hasSource),\n tap(() => {\n this.emitDesiredPlayback({\n shouldPlay: true,\n trackId: this.state.currentTrack?.id,\n })\n }),\n )\n\n const endedSelectionIntent$ = this.audio.ended$.pipe(\n withLatestFrom(nextTrack$),\n switchMap(\n ([, { nextTrackAfterCurrentTrack, nextTrackInPaginationWindow }]) => {\n this.visualizer$.stop({ resetLevels: true })\n\n if (nextTrackInPaginationWindow) {\n return of({\n trackId: nextTrackInPaginationWindow.id,\n options: { navigate: false, play: true },\n })\n }\n\n if (!nextTrackAfterCurrentTrack) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }\n\n this.reader.navigation.goToRightOrBottomSpineItem()\n\n return EMPTY\n },\n ),\n )\n\n const selection$ = merge(\n this.selectCommandSubject,\n visibleTrackSelectionIntent$,\n endedSelectionIntent$,\n playSelectionIntent$,\n ).pipe(\n withLatestFrom(this.state$),\n filter(([selectionIntent, state]) =>\n state.tracks.some(({ id }) => id === selectionIntent.trackId),\n ),\n map(([selectionIntent, state]) => ({\n selectionIntent,\n state,\n isReselectionWhileLoading:\n state.currentTrack?.id === selectionIntent.trackId && state.isLoading,\n })),\n tap(({ selectionIntent, state, isReselectionWhileLoading }) => {\n if (selectionIntent.options.navigate !== false) {\n this.reader.navigation.navigate({\n spineItem: selectionIntent.trackId,\n animation: `turn`,\n })\n }\n\n if (\n isReselectionWhileLoading &&\n selectionIntent.options.play !== undefined\n ) {\n this.emitDesiredPlayback({\n shouldPlay: selectionIntent.options.play,\n trackId: state.currentTrack?.id,\n })\n }\n }),\n filter(({ isReselectionWhileLoading }) => !isReselectionWhileLoading),\n switchMap(({ selectionIntent: { trackId, options } }) =>\n this.selectTrack$({ trackId, options, playbackReset$ }),\n ),\n )\n\n const playback$ = merge(resumePlayback$, selection$).pipe(\n withLatestFrom(this.desiredPlayback$),\n switchMap(([, { shouldPlay, trackId }]) => {\n if (\n !shouldPlay ||\n !this.audio.hasSource ||\n this.state.currentTrack?.id !== trackId\n ) {\n return EMPTY\n }\n\n this.mergeCompare({ hasError: false })\n\n return this.audio.play$().pipe(\n catchError(() => {\n this.mergeCompare({ hasError: true })\n\n return EMPTY\n }),\n )\n }),\n )\n\n this.subscriptions.add(playbackReset$.subscribe())\n this.subscriptions.add(playback$.subscribe())\n\n this.subscriptions.add(\n this.pauseCommandSubject.subscribe(() => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }),\n )\n\n this.subscriptions.add(\n this.audio.isPlaying$.subscribe((isPlaying) => {\n this.mergeCompare({ isPlaying })\n\n if (!isPlaying) {\n this.visualizer$.stop()\n return\n }\n\n if (!this.state.currentTrack) return\n\n this.visualizer$.start(this.state.currentTrack)\n }),\n )\n\n this.subscriptions.add(\n this.audio.metrics$.subscribe(({ currentTime, duration }) => {\n this.mergeCompare({ currentTime, duration })\n }),\n )\n }\n\n get state() {\n return this.value\n }\n\n get visualizer() {\n return this.visualizer$.value\n }\n\n private resetTrackSelection({\n currentTrack,\n isLoading,\n }: {\n currentTrack: AudioTrack\n isLoading: boolean\n }) {\n this.mergeCompare({\n currentTrack,\n isLoading,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(currentTrack?.id)\n }\n\n private emitDesiredPlayback({ shouldPlay, trackId }: DesiredPlayback) {\n this.desiredPlayback$.next({\n shouldPlay,\n trackId: shouldPlay ? trackId : undefined,\n })\n\n if (!shouldPlay) {\n this.audio.pause()\n }\n }\n\n private unmountCurrentSource() {\n if (!this.audio.hasSource) return\n\n const trackId = this.state.currentTrack?.id\n\n if (!this.audio.paused) {\n this.audio.pause()\n }\n\n this.audio.unloadSource()\n\n if (trackId) {\n this.resourcesResolver.releaseTrackSource(trackId)\n }\n }\n\n private mountSource(source: string) {\n this.unmountCurrentSource()\n this.audio.loadSource(source)\n }\n\n private resolveTrackSource(track: AudioTrack) {\n const spineItem = this.reader.spineItemsManager.get(track.index)\n\n if (!spineItem) return EMPTY\n\n return this.resourcesResolver.getTrackResourceUrl$(\n track,\n spineItem.resourcesHandler,\n )\n }\n\n private selectTrack$({\n trackId,\n options,\n playbackReset$,\n }: {\n trackId: string\n options: SelectAudioTrackOptions\n playbackReset$: Observable<AudioTrack[]>\n }) {\n const track = this.state.tracks.find(({ id }) => id === trackId)\n\n if (!track) return EMPTY\n\n const currentTrack = this.state.currentTrack\n const shouldPlay =\n options.play ?? (!this.audio.paused && currentTrack !== undefined)\n\n this.emitDesiredPlayback({ shouldPlay, trackId: track.id })\n\n if (currentTrack?.id === track.id && this.audio.hasSource) {\n this.visualizer$.setTrack(track.id)\n\n return shouldPlay ? of(undefined) : EMPTY\n }\n\n this.unmountCurrentSource()\n\n this.resetTrackSelection({\n currentTrack: track,\n isLoading: true,\n })\n\n return this.resolveTrackSource(track).pipe(\n map((source) => ({ source })),\n defaultIfEmpty({ source: undefined }),\n catchError(() => of({ source: undefined })),\n takeUntil(playbackReset$),\n tap(({ source }) => {\n if (!source) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n } else {\n this.mountSource(source)\n }\n\n this.mergeCompare({ isLoading: false })\n }),\n filter(({ source }) => source !== undefined),\n map(() => undefined),\n )\n }\n\n select(trackId: string, options: SelectAudioTrackOptions = {}) {\n this.selectCommandSubject.next({ trackId, options })\n }\n\n play() {\n this.playCommandSubject.next()\n }\n\n pause() {\n this.pauseCommandSubject.next()\n }\n\n toggle() {\n if (this.audio.paused) {\n this.play()\n return\n }\n\n this.pause()\n }\n\n setCurrentTime(value: number) {\n this.mergeCompare({ currentTime: value })\n this.audio.setCurrentTime(value)\n }\n\n destroy() {\n this.subscriptions.unsubscribe()\n this.playCommandSubject.complete()\n this.pauseCommandSubject.complete()\n this.selectCommandSubject.complete()\n this.desiredPlayback$.complete()\n this.visualizer$.destroy()\n this.unmountCurrentSource()\n this.resourcesResolver.destroy()\n\n super.destroy()\n }\n}\n","import { DocumentRenderer, setStylePropertyIfChanged } from \"@prose-reader/core\"\nimport { EMPTY, of } from \"rxjs\"\n\nexport class AudioRenderer extends DocumentRenderer {\n onCreateDocument() {\n const ownerDocument = this.containerElement.ownerDocument\n const rootElement = ownerDocument.createElement(`div`)\n\n rootElement.style.cssText = `\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: block;\n `\n rootElement.setAttribute(`data-prose-reader-audio-page`, this.item.id)\n\n this.setDocumentContainer(rootElement)\n\n return of(rootElement)\n }\n\n onLoadDocument() {\n this.attach()\n\n return EMPTY\n }\n\n onUnload() {\n this.detach()\n }\n\n onLayout() {\n const { width, height } = this.viewport.pageSize\n const rootElement = this.documentContainer\n\n if (rootElement) {\n setStylePropertyIfChanged(rootElement.style, `width`, `${width}px`)\n setStylePropertyIfChanged(rootElement.style, `height`, `${height}px`)\n }\n\n return of({ width, height })\n }\n\n onRenderHeadless() {\n return EMPTY\n }\n\n getDocumentFrame() {\n return undefined\n }\n\n /**\n * Audio spine items are always pre-paginated (one track per page).\n * Mixed audio/text books with reflowable audio chapters may need\n * to revisit this if that use case arises.\n */\n get renditionLayout(): `pre-paginated` {\n return `pre-paginated`\n }\n}\n","import type { CreateReaderOptions, Reader } from \"@prose-reader/core\"\nimport { AudioController } from \"./playback/AudioController\"\nimport { AudioRenderer } from \"./renderer/AudioRenderer\"\nimport type { AudioEnhancerAPI } from \"./types\"\nimport { isAudioSpineItem } from \"./utils\"\n\nexport const audioEnhancer =\n <InheritOptions extends CreateReaderOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & AudioEnhancerAPI => {\n const readerOptions = { ...options }\n\n const reader = next({\n ...readerOptions,\n getRenderer(item) {\n const maybeFactory = options.getRenderer?.(item)\n\n if (maybeFactory) {\n return maybeFactory\n }\n\n if (isAudioSpineItem(item)) {\n return (params) => new AudioRenderer(params)\n }\n\n return undefined\n },\n })\n\n const controller = new AudioController(reader)\n\n const destroy = () => {\n controller.destroy()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_AUDIO: true,\n destroy,\n audio: {\n state$: controller.state$,\n visualizer$: controller.visualizer$,\n visibleTrackIds$: controller.visibleTrackIds$,\n get state() {\n return controller.state\n },\n get visualizer() {\n return controller.visualizer\n },\n isAudioRenderer: (renderer) => renderer instanceof AudioRenderer,\n play: () => controller.play(),\n pause: () => controller.pause(),\n toggle: () => controller.toggle(),\n setCurrentTime: (value) => controller.setCurrentTime(value),\n select: (trackId, options) => controller.select(trackId, options),\n },\n }\n }\n"],"mappings":"yHAAA,IAAM,EAA+B,GAC/B,EAA+B,KAExB,MACX,MAAM,KAAK,CAAE,OAAQ,CAA6B,MAAS,CAAC,EAEjD,EAAuB,GAA4B,CAC9D,GAAI,EAAK,SAAW,EAAG,OAAO,EAAwB,EAEtD,IAAM,EAAa,KAAK,IACtB,EACA,KAAK,MAAM,EAAK,OAAS,CAA4B,CACvD,EAEA,OAAO,MAAM,KAAK,CAAE,OAAQ,CAA6B,GAAI,EAAG,IAAU,CACxE,IAAM,EAAQ,EAAQ,EAChB,EAAM,KAAK,IAAI,EAAK,OAAQ,EAAQ,CAAU,EAEpD,GAAI,GAAS,EAAK,QAAU,IAAU,EAAK,MAAO,GAElD,IAAI,EAAQ,EAEZ,IAAK,IAAI,EAAS,EAAO,EAAS,EAAK,GAAU,EAC/C,GAAS,EAAK,IAAW,EAK3B,IAAM,GAFU,GAAS,EAAM,GACK,IAEb,IACpB,EAAI,GAEP,OAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAG,CAAY,CAAC,CAC9C,CAAC,CACH,EChCM,EAA4B,IAErB,EAAb,KAAkC,CAMH,aAL7B,aACA,gBACA,aACA,cAEA,YAAY,EAAiD,CAAhC,KAAA,aAAA,CAAiC,CAE9D,MAAM,gBAAiB,CACrB,GAAI,CAAC,KAAK,OAAO,EACf,MAAO,GAGT,IAAM,EAAe,KAAK,aAE1B,GAAI,CAAC,EACH,MAAO,GAGT,GAAI,EAAa,QAAU,YACzB,MAAO,GAGT,GAAI,CAGF,OAFA,MAAM,EAAa,OAAO,EAEnB,EACT,MAAQ,CACN,MAAO,EACT,CACF,CAEA,YAAa,CAOX,MANI,CAAC,KAAK,cAAgB,CAAC,KAAK,cACvB,EAAwB,GAGjC,KAAK,aAAa,qBAAqB,KAAK,aAAa,EAElD,EAAoB,KAAK,aAAa,EAC/C,CAEA,SAAU,CACR,KAAK,iBAAiB,WAAW,EACjC,KAAK,cAAc,WAAW,EAC9B,KAAK,cAAc,MAAM,CAAC,CAAC,UAAY,IAAA,EAAS,EAChD,KAAK,aAAe,IAAA,GACpB,KAAK,gBAAkB,IAAA,GACvB,KAAK,aAAe,IAAA,GACpB,KAAK,cAAgB,IAAA,EACvB,CAEA,QAAiB,CACf,GAAI,KAAK,cAAgB,KAAK,cAAgB,KAAK,cACjD,MAAO,GAGT,GAAI,OAAO,OAAW,KAAe,CAAC,OAAO,aAC3C,MAAO,GAGT,IAAM,EAAe,IAAI,OAAO,aAC1B,EAAkB,EAAa,yBACnC,KAAK,YACP,EACM,EAAe,EAAa,eAAe,EAWjD,MAVA,GAAa,QAAU,EACvB,EAAa,sBAAwB,GACrC,EAAgB,QAAQ,CAAY,EACpC,EAAa,QAAQ,EAAa,WAAW,EAE7C,KAAK,aAAe,EACpB,KAAK,gBAAkB,EACvB,KAAK,aAAe,EACpB,KAAK,cAAgB,IAAI,WAAW,EAAa,iBAAiB,EAE3D,EACT,CACF,ECzDa,EAAb,cAAqC,EAAA,cAAqC,CACxE,WACA,eACE,IAAI,EAAA,gBAA8C,CAChD,QAAS,IAAA,GACT,UAAW,GACX,YAAa,EACf,CAAC,EACH,cAAiC,IAAI,EAAA,aAErC,YAAY,EAAgC,CAC1C,MAAM,CACJ,OAAQ,EAAwB,EAChC,SAAU,GACV,QAAS,IAAA,EACX,CAAC,EACD,KAAK,WAAa,IAAI,EAAqB,CAAY,EAEvD,KAAK,cAAc,IACjB,KAAK,eACF,MAAA,EAAA,EAAA,qBAAA,EAEI,EAAU,IACT,EAAS,UAAY,EAAK,SAC1B,EAAS,YAAc,EAAK,WAC5B,EAAS,cAAgB,EAAK,WAClC,GAAA,EAAA,EAAA,UAAA,EACW,CAAE,UAAS,YAAW,iBAC3B,CAAC,GAAW,CAAC,GACf,EAAA,EAAA,GAAA,CAAU,CACR,UACA,SAAU,GACV,GAAI,EACA,CAAE,OAAQ,EAAwB,CAAE,EACpC,IAAA,EACN,CAAC,GAKH,EAAA,EAAA,OAAA,EAAA,EAAA,EAAA,GAAA,CACK,CACD,UACA,SAAU,GACV,GANsB,KAAK,MAAM,UAAY,EAQzC,IAAA,GADA,CAAE,OAAQ,EAAwB,CAAE,CAE1C,CAAC,EACD,KAAK,cAAc,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,CACd,IAAY,CACf,UACA,SACA,SAAU,EACZ,EAAE,GAAA,EAAA,EAAA,eAAA,CACa,CACb,UACA,SAAU,GACV,OAAQ,EAAwB,CAClC,CAAC,CACH,CACF,CACD,CACH,CAAC,CACA,UAAW,GAAU,CACpB,KAAK,aAAa,CAAK,CACzB,CAAC,CACL,CACF,CAEA,MAAM,EAAsC,CACrC,GAEL,KAAK,eAAe,KAAK,CACvB,QAAS,EAAa,GACtB,UAAW,GACX,YAAa,EACf,CAAC,CACH,CAEA,SAAS,EAA6B,CACpC,KAAK,eAAe,KAAK,CACvB,UACA,UAAW,GACX,YAAa,EACf,CAAC,CACH,CAEA,MAAM,EAA6B,CACjC,KAAK,eAAe,KAAK,CACvB,UACA,UAAW,GACX,YAAa,EACf,CAAC,CACH,CAEA,KAAK,CAAE,cAAc,IAAqC,CAAC,EAAG,CAC5D,KAAK,eAAe,KAAK,CACvB,QAAS,KAAK,eAAe,MAAM,QACnC,UAAW,GACX,aACF,CAAC,CACH,CAEA,SAAmB,CACjB,KAAK,KAAK,CACR,YAAa,EACf,CAAC,EACD,KAAK,cAAc,YAAY,EAC/B,KAAK,eAAe,SAAS,EAC7B,KAAK,WAAW,QAAQ,EAExB,MAAM,QAAQ,CAChB,CAEA,eAAwB,CACtB,OAAA,EAAA,EAAA,MAAA,MACE,EAAA,EAAA,KAAA,CAAY,KAAK,WAAW,eAAe,CAAC,CAAC,CAAC,MAAA,EAAA,EAAA,UAAA,CACjC,GACT,GAAA,EAAA,EAAA,gBAAA,CACoB,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,KAAe,KAAK,WAAW,WAAW,CAAC,CAAC,EAC9D,EAAA,KACN,CACF,CACD,CACH,CACF,ECrIa,EAAb,KAAiC,CAC/B,QACA,SACA,OACA,WACA,SAEA,aAAc,CACZ,KAAK,QAAU,SAAS,cAAc,OAAO,EAC7C,KAAK,QAAQ,QAAU,WAEvB,KAAK,UAAA,EAAA,EAAA,UAAA,CAAqB,KAAK,QAAS,SAAS,CAAC,CAAC,MAAA,EAAA,EAAA,MAAA,CAAW,CAAC,EAC/D,KAAK,QAAA,EAAA,EAAA,UAAA,CAAmB,KAAK,QAAS,OAAO,CAAC,CAAC,MAAA,EAAA,EAAA,MAAA,CAAW,CAAC,EAE3D,KAAK,YAAA,EAAA,EAAA,MAAA,EAAA,EAAA,EAAA,UAAA,CACO,KAAK,QAAS,MAAM,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,KAAe,EAAI,CAAC,GAAA,EAAA,EAAA,UAAA,CAC1C,KAAK,QAAS,OAAO,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,KAAe,EAAK,CAAC,CACxD,CAAC,CAAC,MAAA,EAAA,EAAA,MAAA,CAAW,CAAC,EAEd,KAAK,UAAA,EAAA,EAAA,MAAA,EAAA,EAAA,EAAA,UAAA,CACO,KAAK,QAAS,YAAY,GAAA,EAAA,EAAA,UAAA,CAC1B,KAAK,QAAS,SAAS,GAAA,EAAA,EAAA,UAAA,CACvB,KAAK,QAAS,QAAQ,GAAA,EAAA,EAAA,UAAA,CACtB,KAAK,QAAS,gBAAgB,GAAA,EAAA,EAAA,UAAA,CAC9B,KAAK,QAAS,gBAAgB,EACxC,KAAK,QACP,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,MACW,CACT,YAAa,KAAK,QAAQ,YAC1B,SAAU,OAAO,SAAS,KAAK,QAAQ,QAAQ,EAC3C,KAAK,QAAQ,SACb,IAAA,EACN,EAAE,GAAA,EAAA,EAAA,MAAA,CACI,CACR,CACF,CAEA,IAAI,QAAS,CACX,OAAO,KAAK,QAAQ,MACtB,CAEA,IAAI,WAAY,CACd,OAAO,KAAK,QAAQ,aAAa,KAAK,CACxC,CAEA,OAAQ,CACN,OAAA,EAAA,EAAA,MAAA,MAAA,EAAA,EAAA,KAAA,CAAwB,KAAK,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,MAAA,EAAA,EAAA,MAAA,CACtC,CACJ,MAAO,EACP,UAAa,KAAK,SAAS,MAAA,EAAA,EAAA,KAAA,CAAU,CAAC,CAAC,CACzC,CAAC,CACH,CACF,CAEA,OAAQ,CACN,KAAK,QAAQ,MAAM,CACrB,CAEA,WAAW,EAAa,CACtB,KAAK,QAAQ,IAAM,EACnB,KAAK,QAAQ,KAAK,CACpB,CAEA,cAAe,CACb,KAAK,QAAQ,gBAAgB,KAAK,EAClC,KAAK,QAAQ,KAAK,CACpB,CAEA,eAAe,EAAe,CAC5B,KAAK,QAAQ,YAAc,CAC7B,CACF,EC/Ea,EAAb,KAA+B,CAC7B,sBAAyC,IAAI,IAE7C,gBAAuB,EAA0B,CAC/C,OAAO,KAAK,sBAAsB,IAAI,CAAO,CAC/C,CAEA,sBACE,EACA,IACG,CACH,IAAM,EAAe,KAAK,sBAAsB,IAAI,EAAM,EAAE,EAQ5D,OANI,GACF,EAAA,EAAA,GAAA,CAAU,EAAa,GAAG,GAK5B,EAAA,EAAA,KAAA,CAFuB,EAAiB,YAAY,CAE7C,CAAA,CAAU,MAAA,EAAA,EAAA,UAAA,CACJ,GACL,aAAoB,KACtB,KAAK,sBAAsB,IAAI,EAAM,GAAI,CACvC,IAAK,EAAS,IAChB,CAAC,GAED,EAAA,EAAA,GAAA,CAAU,EAAS,IAAI,GAGrB,aAAoB,UACtB,EAAA,EAAA,KAAA,CAAY,EAAS,KAAK,CAAC,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,CACtB,GAAS,CACZ,IAAM,EAAY,IAAI,gBAAgB,CAAI,EAS1C,OAPA,KAAK,sBAAsB,IAAI,EAAM,GAAI,CACvC,IAAK,EACL,YAAe,CACb,IAAI,gBAAgB,CAAS,CAC/B,CACF,CAAC,EAEM,CACT,CAAC,CACH,GAGF,KAAK,sBAAsB,IAAI,EAAM,GAAI,CACvC,IAAK,EAAM,IACb,CAAC,GAED,EAAA,EAAA,GAAA,CAAU,EAAM,IAAI,EACrB,CACH,CACF,EAEA,mBAA0B,EAAiB,CACzC,IAAM,EAAe,KAAK,sBAAsB,IAAI,CAAO,EAEtD,IAEL,KAAK,sBAAsB,OAAO,CAAO,EACzC,EAAa,UAAU,EACzB,CAEA,YAAoB,CAClB,IAAK,IAAM,KAAW,KAAK,sBAAsB,KAAK,EACpD,KAAK,mBAAmB,CAAO,CAEnC,CAEA,SAAiB,CACf,KAAK,WAAW,CAClB,CACF,ECjFI,GAAK,EAAG,IAAM,IAAM,GAAK,EAAE,SAAW,EAAE,QAAU,EAAE,OAAO,EAAG,IAAM,IAAM,EAAE,EAAE,EA+D/E,GAAK,EAAG,EAAG,IAAM,CACnB,GAAI,OAAO,GAAG,EAAG,CAAC,EAAG,MAAO,CAAC,EAC7B,GAAI,OAAO,GAAK,UAAY,CAAC,GAAK,OAAO,GAAK,UAAY,CAAC,EAAG,MAAO,CAAC,EACtE,IAAI,EAAI,OAAO,KAAK,CAAC,EAAG,EAAI,OAAO,KAAK,CAAC,EACzC,GAAI,EAAE,SAAW,EAAE,OAAQ,MAAO,CAAC,EACnC,IAAI,EAAI,GAAG,aAAe,OAAO,GAAI,EAAI,EAAG,EAAI,EAChD,IAAK,IAAI,KAAK,EAAG,GAAI,CAAC,OAAO,OAAO,EAAG,CAAC,GAAK,CAAC,EAAE,EAAE,GAAI,EAAE,EAAE,EAAG,MAAO,CAAC,EACrE,MAAO,CAAC,CACT,EAcA,SAAS,GAAI,CACZ,IAAI,EAAI,WAAW,qBACnB,OAAO,IAAM,CAAC,GAAK,IAAM,MAC1B,CACA,IAAI,GAAK,EAAG,IAAM,OAAO,GAAK,UAAY,CACzC,QAAS,EACT,QAAS,CACV,EAAI,CACH,QAAS,GAAG,QACZ,QAAS,GAAK,CACf,EAAG,EAAK,GAAM,EAAE,GAAG,EAAE,CAAC,EAAE,MAAO,EAAK,GAAM,CACzC,GAAI,CAAC,EAAE,OAAQ,OACf,GAAI,CAAC,EAAE,KAAM,GAAM,EAAE,KAAK,EAAG,MAAO,CAAC,EAAE,IAAK,GAAM,EAAE,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,EACpE,IAAI,EAAI,GAAI,EAAI,CAAC,EACjB,IAAK,IAAI,KAAK,EAAG,GAAK,KAAK,EAAI,IAAI,EAAE,QAAU,EAAE,QAAS,EAAE,KAAK,EAAE,MAAQ,UAAU,EAAE,QAAU,EAAE,EACnG,MAAO,CAAC,EAAG,GAAG,CAAC,CAChB,EAAG,GAAK,EAAI,CAAC,EAAG,EAAI,EAAE,IAAM,CAC3B,IAAI,EAAI,EAAG,EAAI,CACd,OAAS,GAAM,CACd,EAAE,CAAC,CACJ,EACA,WAAY,EAAG,EAAG,IAAM,CACvB,IAAI,EAAI,EAAE,EAAG,CAAC,EAAG,EAAI,EAAE,SAAS,OAAS,EAAE,CAAC,EAC5C,OAAO,EAAE,CAAC,GAAG,EAAG,CACf,MAAO,IAAI,EAAE,GACb,MAAO,CACR,CAAC,EAAG,EAAE,SAAW,CAAC,CACnB,EACA,cAAiB,EACjB,UAAa,CAAC,EACd,SAAY,CAAC,EACb,QAAW,CAAC,EACZ,mBAAsB,CAAC,EACvB,aAAgB,CAAC,EACjB,aAAe,GAAM,CACpB,IAAI,EAAI,EAAE,CAAC,EACX,GAAI,CAAC,EAAG,MAAO,CAAC,CAAC,EACjB,GAAI,CAAC,EAAG,GAAG,GAAK,EAChB,MAAO,CAAC,GAAG,EAAE,GAAG,IAAK,GAAG,CAAC,CAC1B,EACA,SAAY,CAAC,EACb,UAAa,CAAC,CACf,EAAG,EAAK,GAAM,CACb,GAAI,CAAC,EAAG,CACP,EAAE,UAAc,CAAC,EAAG,EAAE,SAAa,CAAC,EAAG,EAAE,QAAY,CAAC,EAAG,EAAE,mBAAuB,CAAC,EAAG,EAAE,aAAiB,CAAC,EAAG,EAAE,SAAa,CAAC,EAAG,EAAE,UAAc,CAAC,EACjJ,MACD,CACA,IAAI,EAAI,EAAE,CAAC,GAAK,CAAC,EACjB,EAAE,MAAQ,SAAS,UAAU,KAAK,KAAK,QAAQ,MAAO,QAAS,GAAG,CAAC,EAAG,EAAE,KAAO,SAAS,UAAU,KAAK,KAAK,QAAQ,KAAM,QAAS,GAAG,CAAC,EAAG,EAAE,IAAM,SAAS,UAAU,KAAK,KAAK,QAAQ,IAAK,QAAS,GAAG,CAAC,EAAG,EAAE,eAAiB,SAAS,UAAU,KAAK,KAAK,QAAQ,eAAgB,OAAO,EAAG,EAAE,SAAW,SAAS,UAAU,KAAK,KAAK,QAAQ,SAAU,OAAO,EAAG,EAAE,KAAO,SAAS,UAAU,KAAK,KAAK,QAAQ,KAAM,QAAS,GAAG,CAAC,EAAG,EAAE,MAAQ,SAAS,UAAU,KAAK,KAAK,QAAQ,MAAO,QAAS,GAAG,CAAC,CAC5e,EAAG,EAAK,GAAM,CACb,IAAM,IAAM,EAAI,EAAG,EAAE,CAAC,EACvB,EACA,OAAO,EAAE,CAAC,EAAG,CACd,EAAO,EAAE,ECzIT,IAAM,EAAmB,IAAI,IAAI,CAC/B,MACA,MACA,MACA,MACA,MACA,MACA,MACA,OACA,MACF,CAAC,EAEK,EAAwB,GAAiB,CAC7C,GAAM,CAAC,EAAW,IAAM,EAAK,MAAM,MAAM,EAKzC,OAJiB,EAAS,MAAM,GACf,CAAA,CAAS,GAAG,EAAE,GAAK,GAAA,CACT,MAAM,GAAG,CAAC,CAAC,GAAG,EAElC,CAAA,EAAW,YAAY,CAChC,EAEa,EAAoB,GAAyC,CAGxE,IAFkB,EAAK,WAAW,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,YAAY,EAAA,EAExD,WAAW,QAAQ,EAChC,MAAO,GAGT,IAAM,EAAY,EAAqB,EAAK,IAAI,EAEhD,OAAO,EAAY,EAAiB,IAAI,CAAS,EAAI,EACvD,ECdM,GACJ,EACA,IACG,CACC,OAAU,IAAA,GAEd,OAAO,EAAO,KAAM,GAAU,EAAM,QAAU,CAAK,CACrD,EAEM,GACJ,EACA,IAWO,CATY,EACjB,EACA,EAAW,mBAOL,EALS,EACf,EACA,EAAW,iBAGO,CAAQ,CAAC,CAAC,QAC3B,EAAO,EAAG,IACT,IAAU,IAAA,IAAa,EAAI,QAAQ,CAAK,IAAM,CAClD,EAGF,SAAgB,EACd,EACA,EACA,CASA,IAAM,GAAA,EAAA,EAAA,GAAA,CARS,EAAO,QAAQ,SAAS,WACpC,OAAO,CAAgB,CAAC,CACxB,IAAK,IAAU,CACd,GAAI,EAAK,GACT,KAAM,EAAK,KACX,MAAO,EAAK,MACZ,UAAW,EAAK,SAClB,EACiB,CAAM,EAEnB,EAAc,EAAO,WAAW,OAAO,MAAA,EAAA,EAAA,UAAA,CACjC,CAAC,sBAAuB,mBAAmB,CAAC,GAAA,EAAA,EAAA,qBAAA,CACjC,CAAc,GAAA,EAAA,EAAA,YAAA,CACvB,CAAE,WAAY,EAAG,SAAU,EAAK,CAAC,CAC/C,EAkCA,MAAO,CACL,UACA,kBAAA,EAAA,EAAA,cAAA,CAlCqC,CAAC,EAAS,CAAW,CAAC,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,EACxD,CAAC,EAAQ,KACZ,EAAiB,EAAQ,CAAU,CAAC,CAAC,KAAK,CAAE,QAAS,CAAE,CACzD,GAAA,EAAA,EAAA,qBAAA,CACqB,CAAU,GAAA,EAAA,EAAA,YAAA,CACnB,CAAE,WAAY,EAAG,SAAU,EAAK,CAAC,CA6B7C,EACA,YAAA,EAAA,EAAA,cAAA,CAtB+B,CAAC,EAAS,EALrB,EAAO,MAAA,EAAA,EAAA,IAAA,CACtB,GAAU,EAAM,YAAY,GAAA,EAAA,EAAA,qBAAA,CACZ,CAGiC,CAAa,CAAC,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,EACjE,CAAC,EAAQ,CAAE,qBAAqB,MAa5B,CAAE,4BAXP,GAAgB,IAAsB,IAAA,GAClC,EAAO,MACJ,CAAE,WACD,EAAQ,EAAa,OAAS,GAAS,CAC3C,EACA,IAAA,GAMgC,2BAJH,EAC/B,EAAO,MAAM,CAAE,WAAY,EAAQ,EAAa,KAAK,EACrD,IAAA,EAE6D,EAClE,GAAA,EAAA,EAAA,YAAA,CACW,CAAE,WAAY,EAAG,SAAU,EAAK,CAAC,CAM7C,CACF,CACF,CC/DA,IAAM,EAA0C,CAC9C,WAAY,GACZ,QAAS,IAAA,EACX,EAEM,EAAmC,CACvC,OAAQ,CAAC,EACT,aAAc,IAAA,GACd,UAAW,GACX,UAAW,GACX,SAAU,GACV,YAAa,EACb,SAAU,IAAA,EACZ,EAIa,EAAb,cAAqC,EAAA,cAAmC,CACtE,OACA,MACA,YACA,kBAA6B,IAAI,EACjC,iBAEA,mBAAsC,IAAI,EAAA,QAC1C,oBAAuC,IAAI,EAAA,QAC3C,qBAAwC,IAAI,EAAA,QAC5C,iBAAoC,IAAI,EAAA,gBACtC,CACF,EACA,cAAiC,IAAI,EAAA,aAErC,YACE,EACA,EAAQ,IAAI,EACZ,CACA,MAAM,CAAY,EAElB,KAAK,OAAS,EACd,KAAK,MAAQ,EACb,KAAK,YAAc,IAAI,EAAgB,KAAK,MAAM,OAAO,EAEzD,GAAM,CAAE,UAAS,mBAAkB,cAAe,EAChD,KAAK,OACL,KAAK,MACP,EAEA,KAAK,iBAAmB,EAExB,IAAM,EAAuB,KAAK,iBAAiB,MAAA,EAAA,EAAA,IAAA,CAC5C,GAAa,EAAS,EAAE,GAAA,EAAA,EAAA,qBAAA,CACR,GAAA,EAAA,EAAA,MAAA,CACf,CACR,EAEM,EAAqB,EAAqB,MAAA,EAAA,EAAA,eAAA,CAC/B,KAAK,MAAM,GAAA,EAAA,EAAA,OAAA,EAEvB,CAAC,EAAS,KACT,IAAY,IAAA,KACX,EAAM,cAAc,KAAO,IAAA,IAAa,EAAM,UACnD,GAAA,EAAA,EAAA,IAAA,EACK,EAAG,KAAW,EAAM,MAAM,CACjC,EAEM,EAA+B,EAAqB,MAAA,EAAA,EAAA,OAAA,CAChD,GAA+B,IAAY,IAAA,EAAS,GAAA,EAAA,EAAA,eAAA,CAC7C,KAAK,gBAAgB,GAAA,EAAA,EAAA,IAAA,EAC/B,CAAC,EAAS,CAAE,kBAAmB,CAClC,UACA,QAAS,CACP,SAAU,GACV,KAAM,EAAa,GAAO,IAAA,EAC5B,CACF,EAAE,CACJ,EAMM,GAAA,EAAA,EAAA,MAAA,CAAuB,EAJN,EAAQ,MAAA,EAAA,EAAA,IAAA,KACnB,KAAK,kBAAkB,WAAW,CAAC,CAGE,CAAc,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,CAC1D,GAAW,CACd,KAAK,oBAAoB,CAAE,WAAY,GAAO,QAAS,IAAA,EAAU,CAAC,EAClE,KAAK,YAAY,KAAK,CAAE,YAAa,EAAK,CAAC,EAC3C,KAAK,qBAAqB,EAE1B,KAAK,aAAa,CAChB,SACA,aAAc,IAAA,GACd,UAAW,GACX,UAAW,GACX,SAAU,GACV,YAAa,EACb,SAAU,IAAA,EACZ,CAAC,EACD,KAAK,YAAY,MAAM,IAAA,EAAS,CAClC,CAAC,GAAA,EAAA,EAAA,MAAA,CACK,CACR,EAEM,EAAuB,KAAK,mBAAmB,MAAA,EAAA,EAAA,OAAA,KACtC,CAAC,KAAK,MAAM,SAAS,GAAA,EAAA,EAAA,IAAA,KACxB,KAAK,MAAM,cAAgB,KAAK,MAAM,OAAO,EAAE,GAAA,EAAA,EAAA,OAAA,CAClD,EAAA,SAAS,GAAA,EAAA,EAAA,IAAA,CACX,IAAW,CACd,QAAS,EAAM,GACf,QAAS,CAAE,SAAU,GAAO,KAAM,EAAK,CACzC,EAAE,CACJ,EAEM,EAAkB,KAAK,mBAAmB,MAAA,EAAA,EAAA,OAAA,KACjC,KAAK,MAAM,SAAS,GAAA,EAAA,EAAA,IAAA,KACvB,CACR,KAAK,oBAAoB,CACvB,WAAY,GACZ,QAAS,KAAK,MAAM,cAAc,EACpC,CAAC,CACH,CAAC,CACH,EAEM,EAAwB,KAAK,MAAM,OAAO,MAAA,EAAA,EAAA,eAAA,CAC/B,CAAU,GAAA,EAAA,EAAA,UAAA,EAEtB,EAAG,CAAE,6BAA4B,mCAChC,KAAK,YAAY,KAAK,CAAE,YAAa,EAAK,CAAC,EAEvC,GACF,EAAA,EAAA,GAAA,CAAU,CACR,QAAS,EAA4B,GACrC,QAAS,CAAE,SAAU,GAAO,KAAM,EAAK,CACzC,CAAC,GAGE,GACH,KAAK,oBAAoB,CAAE,WAAY,GAAO,QAAS,IAAA,EAAU,CAAC,EAGpE,KAAK,OAAO,WAAW,2BAA2B,EAE3C,EAAA,OAEX,CACF,EA0CM,GAAA,EAAA,EAAA,MAAA,CAAkB,GAAA,EAAA,EAAA,MAAA,CAvCtB,KAAK,qBACL,EACA,EACA,CACF,CAAC,CAAC,MAAA,EAAA,EAAA,eAAA,CACe,KAAK,MAAM,GAAA,EAAA,EAAA,OAAA,EAClB,CAAC,EAAiB,KACxB,EAAM,OAAO,MAAM,CAAE,QAAS,IAAO,EAAgB,OAAO,CAC9D,GAAA,EAAA,EAAA,IAAA,EACK,CAAC,EAAiB,MAAY,CACjC,kBACA,QACA,0BACE,EAAM,cAAc,KAAO,EAAgB,SAAW,EAAM,SAChE,EAAE,GAAA,EAAA,EAAA,IAAA,EACG,CAAE,kBAAiB,QAAO,+BAAgC,CACzD,EAAgB,QAAQ,WAAa,IACvC,KAAK,OAAO,WAAW,SAAS,CAC9B,UAAW,EAAgB,QAC3B,UAAW,MACb,CAAC,EAID,GACA,EAAgB,QAAQ,OAAS,IAAA,IAEjC,KAAK,oBAAoB,CACvB,WAAY,EAAgB,QAAQ,KACpC,QAAS,EAAM,cAAc,EAC/B,CAAC,CAEL,CAAC,GAAA,EAAA,EAAA,OAAA,EACO,CAAE,+BAAgC,CAAC,CAAyB,GAAA,EAAA,EAAA,UAAA,EACzD,CAAE,gBAAiB,CAAE,UAAS,cACvC,KAAK,aAAa,CAAE,UAAS,UAAS,gBAAe,CAAC,CACxD,CAGuC,CAAU,CAAC,CAAC,MAAA,EAAA,EAAA,eAAA,CACpC,KAAK,gBAAgB,GAAA,EAAA,EAAA,UAAA,EACzB,EAAG,CAAE,aAAY,cAExB,CAAC,GACD,CAAC,KAAK,MAAM,WACZ,KAAK,MAAM,cAAc,KAAO,EAEzB,EAAA,OAGT,KAAK,aAAa,CAAE,SAAU,EAAM,CAAC,EAE9B,KAAK,MAAM,MAAM,CAAC,CAAC,MAAA,EAAA,EAAA,WAAA,MAEtB,KAAK,aAAa,CAAE,SAAU,EAAK,CAAC,EAE7B,EAAA,MACR,CACH,EACD,CACH,EAEA,KAAK,cAAc,IAAI,EAAe,UAAU,CAAC,EACjD,KAAK,cAAc,IAAI,EAAU,UAAU,CAAC,EAE5C,KAAK,cAAc,IACjB,KAAK,oBAAoB,cAAgB,CACvC,KAAK,oBAAoB,CAAE,WAAY,GAAO,QAAS,IAAA,EAAU,CAAC,CACpE,CAAC,CACH,EAEA,KAAK,cAAc,IACjB,KAAK,MAAM,WAAW,UAAW,GAAc,CAG7C,GAFA,KAAK,aAAa,CAAE,WAAU,CAAC,EAE3B,CAAC,EAAW,CACd,KAAK,YAAY,KAAK,EACtB,MACF,CAEK,KAAK,MAAM,cAEhB,KAAK,YAAY,MAAM,KAAK,MAAM,YAAY,CAChD,CAAC,CACH,EAEA,KAAK,cAAc,IACjB,KAAK,MAAM,SAAS,WAAW,CAAE,cAAa,cAAe,CAC3D,KAAK,aAAa,CAAE,cAAa,UAAS,CAAC,CAC7C,CAAC,CACH,CACF,CAEA,IAAI,OAAQ,CACV,OAAO,KAAK,KACd,CAEA,IAAI,YAAa,CACf,OAAO,KAAK,YAAY,KAC1B,CAEA,oBAA4B,CAC1B,eACA,aAIC,CACD,KAAK,aAAa,CAChB,eACA,YACA,UAAW,GACX,SAAU,GACV,YAAa,EACb,SAAU,IAAA,EACZ,CAAC,EACD,KAAK,YAAY,MAAM,GAAc,EAAE,CACzC,CAEA,oBAA4B,CAAE,aAAY,WAA4B,CACpE,KAAK,iBAAiB,KAAK,CACzB,aACA,QAAS,EAAa,EAAU,IAAA,EAClC,CAAC,EAEI,GACH,KAAK,MAAM,MAAM,CAErB,CAEA,sBAA+B,CAC7B,GAAI,CAAC,KAAK,MAAM,UAAW,OAE3B,IAAM,EAAU,KAAK,MAAM,cAAc,GAEpC,KAAK,MAAM,QACd,KAAK,MAAM,MAAM,EAGnB,KAAK,MAAM,aAAa,EAEpB,GACF,KAAK,kBAAkB,mBAAmB,CAAO,CAErD,CAEA,YAAoB,EAAgB,CAClC,KAAK,qBAAqB,EAC1B,KAAK,MAAM,WAAW,CAAM,CAC9B,CAEA,mBAA2B,EAAmB,CAC5C,IAAM,EAAY,KAAK,OAAO,kBAAkB,IAAI,EAAM,KAAK,EAI/D,OAFK,EAEE,KAAK,kBAAkB,qBAC5B,EACA,EAAU,gBACZ,EALuB,EAAA,KAMzB,CAEA,aAAqB,CACnB,UACA,UACA,kBAKC,CACD,IAAM,EAAQ,KAAK,MAAM,OAAO,MAAM,CAAE,QAAS,IAAO,CAAO,EAE/D,GAAI,CAAC,EAAO,OAAO,EAAA,MAEnB,IAAM,EAAe,KAAK,MAAM,aAC1B,EACJ,EAAQ,OAAS,CAAC,KAAK,MAAM,QAAU,IAAiB,IAAA,IAiB1D,OAfA,KAAK,oBAAoB,CAAE,aAAY,QAAS,EAAM,EAAG,CAAC,EAEtD,GAAc,KAAO,EAAM,IAAM,KAAK,MAAM,WAC9C,KAAK,YAAY,SAAS,EAAM,EAAE,EAE3B,GAAA,EAAA,EAAA,GAAA,CAAgB,IAAA,EAAS,EAAI,EAAA,QAGtC,KAAK,qBAAqB,EAE1B,KAAK,oBAAoB,CACvB,aAAc,EACd,UAAW,EACb,CAAC,EAEM,KAAK,mBAAmB,CAAK,CAAC,CAAC,MAAA,EAAA,EAAA,IAAA,CAC/B,IAAY,CAAE,QAAO,EAAE,GAAA,EAAA,EAAA,eAAA,CACb,CAAE,OAAQ,IAAA,EAAU,CAAC,GAAA,EAAA,EAAA,WAAA,MAAA,EAAA,EAAA,GAAA,CAChB,CAAE,OAAQ,IAAA,EAAU,CAAC,CAAC,GAAA,EAAA,EAAA,UAAA,CAChC,CAAc,GAAA,EAAA,EAAA,IAAA,EACnB,CAAE,YAAa,CACb,EAGH,KAAK,YAAY,CAAM,EAFvB,KAAK,oBAAoB,CAAE,WAAY,GAAO,QAAS,IAAA,EAAU,CAAC,EAKpE,KAAK,aAAa,CAAE,UAAW,EAAM,CAAC,CACxC,CAAC,GAAA,EAAA,EAAA,OAAA,EACO,CAAE,YAAa,IAAW,IAAA,EAAS,GAAA,EAAA,EAAA,IAAA,KACjC,IAAA,EAAS,CACrB,EACF,CAEA,OAAO,EAAiB,EAAmC,CAAC,EAAG,CAC7D,KAAK,qBAAqB,KAAK,CAAE,UAAS,SAAQ,CAAC,CACrD,CAEA,MAAO,CACL,KAAK,mBAAmB,KAAK,CAC/B,CAEA,OAAQ,CACN,KAAK,oBAAoB,KAAK,CAChC,CAEA,QAAS,CACP,GAAI,KAAK,MAAM,OAAQ,CACrB,KAAK,KAAK,EACV,MACF,CAEA,KAAK,MAAM,CACb,CAEA,eAAe,EAAe,CAC5B,KAAK,aAAa,CAAE,YAAa,CAAM,CAAC,EACxC,KAAK,MAAM,eAAe,CAAK,CACjC,CAEA,SAAU,CACR,KAAK,cAAc,YAAY,EAC/B,KAAK,mBAAmB,SAAS,EACjC,KAAK,oBAAoB,SAAS,EAClC,KAAK,qBAAqB,SAAS,EACnC,KAAK,iBAAiB,SAAS,EAC/B,KAAK,YAAY,QAAQ,EACzB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,QAAQ,EAE/B,MAAM,QAAQ,CAChB,CACF,ECnba,EAAb,cAAmC,EAAA,gBAAiB,CAClD,kBAAmB,CAEjB,IAAM,EADgB,KAAK,iBAAiB,cACV,cAAc,KAAK,EAYrD,MAVA,GAAY,MAAM,QAAU;;;;;MAM5B,EAAY,aAAa,+BAAgC,KAAK,KAAK,EAAE,EAErE,KAAK,qBAAqB,CAAW,GAErC,EAAA,EAAA,GAAA,CAAU,CAAW,CACvB,CAEA,gBAAiB,CAGf,OAFA,KAAK,OAAO,EAEL,EAAA,KACT,CAEA,UAAW,CACT,KAAK,OAAO,CACd,CAEA,UAAW,CACT,GAAM,CAAE,QAAO,UAAW,KAAK,SAAS,SAClC,EAAc,KAAK,kBAOzB,OALI,KACF,EAAA,EAAA,0BAAA,CAA0B,EAAY,MAAO,QAAS,GAAG,EAAM,GAAG,GAClE,EAAA,EAAA,0BAAA,CAA0B,EAAY,MAAO,SAAU,GAAG,EAAO,GAAG,IAGtE,EAAA,EAAA,GAAA,CAAU,CAAE,QAAO,QAAO,CAAC,CAC7B,CAEA,kBAAmB,CACjB,OAAO,EAAA,KACT,CAEA,kBAAmB,CAEnB,CAOA,IAAI,iBAAmC,CACrC,MAAO,eACT,CACF,ECrDa,EAET,GAED,GAA8D,CAG7D,IAAM,EAAS,EAAK,CAFI,GAAG,EAIzB,YAAY,EAAM,CAChB,IAAM,EAAe,EAAQ,cAAc,CAAI,EAE/C,GAAI,EACF,OAAO,EAGT,GAAI,EAAiB,CAAI,EACvB,MAAQ,IAAW,IAAI,EAAc,CAAM,CAI/C,CACF,CAAC,EAEK,EAAa,IAAI,EAAgB,CAAM,EAEvC,MAAgB,CACpB,EAAW,QAAQ,EACnB,EAAO,QAAQ,CACjB,EAEA,MAAO,CACL,GAAG,EACH,8BAA+B,GAC/B,UACA,MAAO,CACL,OAAQ,EAAW,OACnB,YAAa,EAAW,YACxB,iBAAkB,EAAW,iBAC7B,IAAI,OAAQ,CACV,OAAO,EAAW,KACpB,EACA,IAAI,YAAa,CACf,OAAO,EAAW,UACpB,EACA,gBAAkB,GAAa,aAAoB,EACnD,SAAY,EAAW,KAAK,EAC5B,UAAa,EAAW,MAAM,EAC9B,WAAc,EAAW,OAAO,EAChC,eAAiB,GAAU,EAAW,eAAe,CAAK,EAC1D,QAAS,EAAS,IAAY,EAAW,OAAO,EAAS,CAAO,CAClE,CACF,CACF"}
package/dist/index.js CHANGED
@@ -261,15 +261,12 @@ var G = /* @__PURE__ */ new Set([
261
261
  if (t !== void 0) return e.find((e) => e.index === t);
262
262
  }, Y = (e, t) => [J(e, t.beginSpineItemIndex), J(e, t.endSpineItemIndex)].filter((e, t, n) => e !== void 0 && n.indexOf(e) === t);
263
263
  function X(e, t) {
264
- let n = e.context.manifest$.pipe(y((e) => e.spineItems.filter(q).map((e) => ({
264
+ let n = x(e.context.manifest.spineItems.filter(q).map((e) => ({
265
265
  id: e.id,
266
266
  href: e.href,
267
267
  index: e.index,
268
268
  mediaType: e.mediaType
269
- }))), w({
270
- bufferSize: 1,
271
- refCount: !0
272
- })), i = e.pagination.state$.pipe(r(["beginSpineItemIndex", "endSpineItemIndex"]), h(z), w({
269
+ }))), i = e.pagination.state$.pipe(r(["beginSpineItemIndex", "endSpineItemIndex"]), h(z), w({
273
270
  bufferSize: 1,
274
271
  refCount: !0
275
272
  }));
@@ -479,7 +476,7 @@ var Z = {
479
476
  return this.attach(), o;
480
477
  }
481
478
  onUnload() {
482
- return this.detach(), o;
479
+ this.detach();
483
480
  }
484
481
  onLayout() {
485
482
  let { width: e, height: t } = this.viewport.pageSize, n = this.documentContainer;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/visualizer/levels.ts","../src/visualizer/VisualizerAudioGraph.ts","../src/visualizer/AudioVisualizer.ts","../src/playback/AudioElementAdapter.ts","../src/playback/ResourcesResolver.ts","../../shared/dist/index.js","../src/utils.ts","../src/playback/trackStreams.ts","../src/playback/AudioController.ts","../src/renderer/AudioRenderer.ts","../src/audioEnhancer.ts"],"sourcesContent":["const AUDIO_VISUALIZER_LEVEL_COUNT = 80\nconst AUDIO_VISUALIZER_NOISE_FLOOR = 0.035\n\nexport const getIdleVisualizerLevels = () =>\n Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, () => 0)\n\nexport const getVisualizerLevels = (data: ArrayLike<number>) => {\n if (data.length === 0) return getIdleVisualizerLevels()\n\n const bucketSize = Math.max(\n 1,\n Math.floor(data.length / AUDIO_VISUALIZER_LEVEL_COUNT),\n )\n\n return Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, (_, index) => {\n const start = index * bucketSize\n const end = Math.min(data.length, start + bucketSize)\n\n if (start >= data.length || start === end) return 0\n\n let total = 0\n\n for (let cursor = start; cursor < end; cursor += 1) {\n total += data[cursor] ?? 0\n }\n\n const average = total / (end - start)\n const normalizedAverage = average / 255\n const gatedAverage =\n (normalizedAverage - AUDIO_VISUALIZER_NOISE_FLOOR) /\n (1 - AUDIO_VISUALIZER_NOISE_FLOOR)\n\n return Math.max(0, Math.min(1, gatedAverage))\n })\n}\n","import { getIdleVisualizerLevels, getVisualizerLevels } from \"./levels\"\n\nconst AUDIO_VISUALIZER_FFT_SIZE = 256\n\nexport class VisualizerAudioGraph {\n private audioContext: AudioContext | undefined\n private audioSourceNode: MediaElementAudioSourceNode | undefined\n private analyserNode: AnalyserNode | undefined\n private frequencyData: Uint8Array<ArrayBuffer> | undefined\n\n constructor(private readonly audioElement: HTMLAudioElement) {}\n\n async resumeIfNeeded() {\n if (!this.ensure()) {\n return false\n }\n\n const audioContext = this.audioContext\n\n if (!audioContext) {\n return false\n }\n\n if (audioContext.state !== `suspended`) {\n return true\n }\n\n try {\n await audioContext.resume()\n\n return true\n } catch {\n return false\n }\n }\n\n readLevels() {\n if (!this.analyserNode || !this.frequencyData) {\n return getIdleVisualizerLevels()\n }\n\n this.analyserNode.getByteFrequencyData(this.frequencyData)\n\n return getVisualizerLevels(this.frequencyData)\n }\n\n destroy() {\n this.audioSourceNode?.disconnect()\n this.analyserNode?.disconnect()\n this.audioContext?.close().catch(() => undefined)\n this.audioContext = undefined\n this.audioSourceNode = undefined\n this.analyserNode = undefined\n this.frequencyData = undefined\n }\n\n private ensure() {\n if (this.audioContext && this.analyserNode && this.frequencyData) {\n return true\n }\n\n if (typeof window === `undefined` || !window.AudioContext) {\n return false\n }\n\n const audioContext = new window.AudioContext()\n const audioSourceNode = audioContext.createMediaElementSource(\n this.audioElement,\n )\n const analyserNode = audioContext.createAnalyser()\n analyserNode.fftSize = AUDIO_VISUALIZER_FFT_SIZE\n analyserNode.smoothingTimeConstant = 0.8\n audioSourceNode.connect(analyserNode)\n analyserNode.connect(audioContext.destination)\n\n this.audioContext = audioContext\n this.audioSourceNode = audioSourceNode\n this.analyserNode = analyserNode\n this.frequencyData = new Uint8Array(analyserNode.frequencyBinCount)\n\n return true\n }\n}\n","import { ReactiveEntity } from \"@prose-reader/core\"\nimport {\n animationFrames,\n BehaviorSubject,\n concat,\n defaultIfEmpty,\n defer,\n distinctUntilChanged,\n EMPTY,\n from,\n map,\n of,\n Subscription,\n switchMap,\n} from \"rxjs\"\nimport type { AudioTrack, AudioVisualizerState } from \"../types\"\nimport { getIdleVisualizerLevels } from \"./levels\"\nimport { VisualizerAudioGraph } from \"./VisualizerAudioGraph\"\n\ntype AudioVisualizerPlaybackState = {\n trackId: string | undefined\n isRunning: boolean\n resetLevels: boolean\n}\n\nexport class AudioVisualizer extends ReactiveEntity<AudioVisualizerState> {\n private readonly audioGraph: VisualizerAudioGraph\n private readonly playbackState$ =\n new BehaviorSubject<AudioVisualizerPlaybackState>({\n trackId: undefined,\n isRunning: false,\n resetLevels: false,\n })\n private readonly subscriptions = new Subscription()\n\n constructor(audioElement: HTMLAudioElement) {\n super({\n levels: getIdleVisualizerLevels(),\n isActive: false,\n trackId: undefined,\n })\n this.audioGraph = new VisualizerAudioGraph(audioElement)\n\n this.subscriptions.add(\n this.playbackState$\n .pipe(\n distinctUntilChanged(\n (previous, next) =>\n previous.trackId === next.trackId &&\n previous.isRunning === next.isRunning &&\n previous.resetLevels === next.resetLevels,\n ),\n switchMap(({ trackId, isRunning, resetLevels }) => {\n if (!trackId || !isRunning) {\n return of({\n trackId,\n isActive: false,\n ...(resetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n })\n }\n\n const shouldResetLevels = this.value.trackId !== trackId\n\n return concat(\n of({\n trackId,\n isActive: true,\n ...(shouldResetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n }),\n this.createLevels$().pipe(\n map((levels) => ({\n trackId,\n levels,\n isActive: true,\n })),\n defaultIfEmpty({\n trackId,\n isActive: false,\n levels: getIdleVisualizerLevels(),\n }),\n ),\n )\n }),\n )\n .subscribe((value) => {\n this.mergeCompare(value)\n }),\n )\n }\n\n start(currentTrack: AudioTrack | undefined) {\n if (!currentTrack) return\n\n this.playbackState$.next({\n trackId: currentTrack.id,\n isRunning: true,\n resetLevels: false,\n })\n }\n\n setTrack(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: false,\n })\n }\n\n reset(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: true,\n })\n }\n\n stop({ resetLevels = false }: { resetLevels?: boolean } = {}) {\n this.playbackState$.next({\n trackId: this.playbackState$.value.trackId,\n isRunning: false,\n resetLevels,\n })\n }\n\n override destroy() {\n this.stop({\n resetLevels: true,\n })\n this.subscriptions.unsubscribe()\n this.playbackState$.complete()\n this.audioGraph.destroy()\n\n super.destroy()\n }\n\n private createLevels$() {\n return defer(() => {\n return from(this.audioGraph.resumeIfNeeded()).pipe(\n switchMap((isReady) =>\n isReady\n ? animationFrames().pipe(map(() => this.audioGraph.readLevels()))\n : EMPTY,\n ),\n )\n })\n }\n}\n","import {\n defer,\n from,\n fromEvent,\n map,\n merge,\n type Observable,\n retry,\n share,\n take,\n} from \"rxjs\"\n\nexport type AudioMetrics = {\n currentTime: number\n duration: number | undefined\n}\n\nexport class AudioElementAdapter {\n readonly element: HTMLAudioElement\n readonly canPlay$: Observable<Event>\n readonly ended$: Observable<Event>\n readonly isPlaying$: Observable<boolean>\n readonly metrics$: Observable<AudioMetrics>\n\n constructor() {\n this.element = document.createElement(`audio`)\n this.element.preload = `metadata`\n\n this.canPlay$ = fromEvent(this.element, `canplay`).pipe(share())\n this.ended$ = fromEvent(this.element, `ended`).pipe(share())\n\n this.isPlaying$ = merge(\n fromEvent(this.element, `play`).pipe(map(() => true)),\n fromEvent(this.element, `pause`).pipe(map(() => false)),\n ).pipe(share())\n\n this.metrics$ = merge(\n fromEvent(this.element, `timeupdate`),\n fromEvent(this.element, `seeking`),\n fromEvent(this.element, `seeked`),\n fromEvent(this.element, `loadedmetadata`),\n fromEvent(this.element, `durationchange`),\n this.canPlay$,\n ).pipe(\n map(() => ({\n currentTime: this.element.currentTime,\n duration: Number.isFinite(this.element.duration)\n ? this.element.duration\n : undefined,\n })),\n share(),\n )\n }\n\n get paused() {\n return this.element.paused\n }\n\n get hasSource() {\n return this.element.hasAttribute(`src`)\n }\n\n play$() {\n return defer(() => from(this.element.play())).pipe(\n retry({\n count: 1,\n delay: () => this.canPlay$.pipe(take(1)),\n }),\n )\n }\n\n pause() {\n this.element.pause()\n }\n\n loadSource(src: string) {\n this.element.src = src\n this.element.load()\n }\n\n unloadSource() {\n this.element.removeAttribute(`src`)\n this.element.load()\n }\n\n setCurrentTime(value: number) {\n this.element.currentTime = value\n }\n}\n","import type { ResourceHandler } from \"@prose-reader/core\"\nimport { from, map, of, switchMap } from \"rxjs\"\nimport type { AudioTrack } from \"../types\"\n\ntype CachedTrackSource = {\n url: string\n release?: () => void\n}\n\nexport class ResourcesResolver {\n private readonly cachedSourceByTrackId = new Map<string, CachedTrackSource>()\n\n public hasCachedSource(trackId: string): boolean {\n return this.cachedSourceByTrackId.has(trackId)\n }\n\n public getTrackResourceUrl$ = (\n track: AudioTrack,\n resourcesHandler: Pick<ResourceHandler, \"getResource\">,\n ) => {\n const cachedSource = this.cachedSourceByTrackId.get(track.id)\n\n if (cachedSource) {\n return of(cachedSource.url)\n }\n\n const resource$ = from(resourcesHandler.getResource())\n\n return resource$.pipe(\n switchMap((resource) => {\n if (resource instanceof URL) {\n this.cachedSourceByTrackId.set(track.id, {\n url: resource.href,\n })\n\n return of(resource.href)\n }\n\n if (resource instanceof Response) {\n return from(resource.blob()).pipe(\n map((blob) => {\n const objectUrl = URL.createObjectURL(blob)\n\n this.cachedSourceByTrackId.set(track.id, {\n url: objectUrl,\n release: () => {\n URL.revokeObjectURL(objectUrl)\n },\n })\n\n return objectUrl\n }),\n )\n }\n\n this.cachedSourceByTrackId.set(track.id, {\n url: track.href,\n })\n\n return of(track.href)\n }),\n )\n }\n\n public releaseTrackSource(trackId: string) {\n const cachedSource = this.cachedSourceByTrackId.get(trackId)\n\n if (!cachedSource) return\n\n this.cachedSourceByTrackId.delete(trackId)\n cachedSource.release?.()\n }\n\n public releaseAll() {\n for (const trackId of this.cachedSourceByTrackId.keys()) {\n this.releaseTrackSource(trackId)\n }\n }\n\n public destroy() {\n this.releaseAll()\n }\n}\n","//#region src/array.ts\nvar e = (e, t) => e === t || e.length === t.length && e.every((e, n) => e === t[n]), t = (e) => e.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction n(e) {\n\tlet t = [];\n\tif (e.length === 0) return \"\";\n\tif (typeof e[0] != \"string\") throw TypeError(`Url must be a string. Received ${e[0]}`);\n\te[0].match(/^[^/:]+:\\/*$/) && e.length > 1 && (e[0] = e.shift() + e[0]), e[0].match(/^file:\\/\\/\\//) ? e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n\tfor (let n = 0; n < e.length; n++) {\n\t\tlet r = e[n];\n\t\tif (typeof r != \"string\") throw TypeError(`Url must be a string. Received ${r}`);\n\t\tr !== \"\" && (n > 0 && (r = r.replace(/^[/]+/, \"\")), r = n < e.length - 1 ? r.replace(/[/]+$/, \"\") : r.replace(/[/]+$/, \"/\"), t.push(r));\n\t}\n\tlet n = t.join(\"/\");\n\tn = n.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n\tlet r = n.split(\"?\");\n\treturn n = r.shift() + (r.length > 0 ? \"?\" : \"\") + r.join(\"&\"), n;\n}\nfunction r(...e) {\n\treturn n(Array.from(Array.isArray(e[0]) ? e[0] : e));\n}\nfunction i(e) {\n\tlet t = e.split(\"/\");\n\treturn t.pop(), t.join(\"/\");\n}\n//#endregion\n//#region src/contentType.ts\nvar a = (e) => {\n\tswitch (t(e)) {\n\t\tcase \"png\": return \"image/png\";\n\t\tcase \"jpg\": return \"image/jpg\";\n\t\tcase \"jpeg\": return \"image/jpeg\";\n\t\tcase \"txt\": return \"text/plain\";\n\t\tcase \"webp\": return \"image/webp\";\n\t\tcase \"xhtml\": return \"application/xhtml+xml\";\n\t\tcase \"mp3\": return \"audio/mpeg\";\n\t\tcase \"m4a\":\n\t\tcase \"m4b\": return \"audio/mp4\";\n\t\tcase \"aac\": return \"audio/aac\";\n\t\tcase \"ogg\":\n\t\tcase \"oga\": return \"audio/ogg\";\n\t\tcase \"wav\": return \"audio/wav\";\n\t\tcase \"flac\": return \"audio/flac\";\n\t\tcase \"opus\": return \"audio/opus\";\n\t}\n}, o = ({ mimeType: e, uri: t }) => (e ?? a(t ?? \"\"))?.startsWith(\"application/xhtml+xml\"), s = (e) => e.startsWith(\"image/\") || e.startsWith(\"audio/\") || e.startsWith(\"video/\"), c = (e) => {\n\tif (!e.length) return;\n\tlet t = e.indexOf(\";\");\n\treturn t >= 0 ? e.substring(0, t) : e;\n}, l = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/, u = /^[A-Za-z0-9_]/, d = /^xml/i, f = /[^A-Za-z0-9_.-]+/g, p = \"_\", m = (e) => e.replace(/^_+|_+$/g, \"\"), h = (e) => m(e.trim().replaceAll(\"/\", \"_\").replace(f, \"_\")), g = (e) => {\n\tif (l.test(e) && !d.test(e)) return e;\n\tlet t = h(e), n = t && !d.test(t) ? t : `${p}${t}`;\n\treturn u.test(n) ? n : `${p}${n}`;\n}, _ = (e, t) => {\n\tlet n = g(e);\n\tif (!t.has(n)) return t.add(n), n;\n\tlet r = 2, i = `${n}-${r}`;\n\tfor (; t.has(i);) r += 1, i = `${n}-${r}`;\n\treturn t.add(i), i;\n}, v = () => {\n\tlet e = /* @__PURE__ */ new Set();\n\treturn (t) => _(t, e);\n}, y = (e) => {\n\tlet t = e.pageSpreadLeft === !0;\n\tif (t !== (e.pageSpreadRight === !0)) return t ? \"left\" : \"right\";\n}, b = (e, t, n) => {\n\tif (Object.is(e, t)) return !0;\n\tif (typeof e != \"object\" || !e || typeof t != \"object\" || !t) return !1;\n\tlet r = Object.keys(e), i = Object.keys(t);\n\tif (r.length !== i.length) return !1;\n\tlet a = n?.customEqual ?? Object.is, o = e, s = t;\n\tfor (let e of r) if (!Object.hasOwn(s, e) || !a(o[e], s[e])) return !1;\n\treturn !0;\n}, x = (e, t) => e.reduce((e, n) => {\n\tlet r = t(n);\n\treturn e[r] || (e[r] = []), e[r].push(n), e;\n}, {});\nfunction S(e, t) {\n\tlet n = { ...e };\n\tfor (let r in t) if (Object.hasOwn(t, r)) {\n\t\tlet i = t[r];\n\t\t(i !== void 0 || !(r in e)) && (n[r] = i);\n\t}\n\treturn n;\n}\n//#endregion\n//#region src/report.ts\nfunction C() {\n\tlet e = globalThis.__PROSE_READER_DEBUG;\n\treturn e === !0 || e === \"true\";\n}\nvar w = (e, t) => typeof e == \"boolean\" ? {\n\tenabled: e,\n\toptions: t\n} : {\n\tenabled: e?.enabled,\n\toptions: e ?? t\n}, T = (e) => e.at(-1)?.color, E = (e) => {\n\tif (!e.length) return;\n\tif (!e.some((e) => e.color)) return [e.map((e) => e.label).join(\" \")];\n\tlet t = \"\", n = [];\n\tfor (let r of e) t += `%c${t ? ` ${r.label}` : r.label}`, n.push(r.color ? `color: ${r.color}` : \"\");\n\treturn [t, ...n];\n}, D = (e = [], t = C()) => {\n\tlet n = t, r = {\n\t\tenable: (e) => {\n\t\t\ta(e);\n\t\t},\n\t\tnamespace: (t, r, i) => {\n\t\t\tlet a = w(r, i), o = a.options?.color ?? T(e);\n\t\t\treturn D([...e, {\n\t\t\t\tlabel: `[${t}]`,\n\t\t\t\tcolor: o\n\t\t\t}], a.enabled ?? n);\n\t\t},\n\t\tisEnabled: () => n,\n\t\tdebug: () => {},\n\t\tinfo: () => {},\n\t\tlog: () => {},\n\t\tgroupCollapsed: () => {},\n\t\tgroupEnd: () => {},\n\t\tgetGroupArgs: (t) => {\n\t\t\tlet n = E(e);\n\t\t\tif (!n) return [t];\n\t\t\tlet [r, ...i] = n;\n\t\t\treturn [`${r} ${t}`, ...i];\n\t\t},\n\t\twarn: () => {},\n\t\terror: () => {}\n\t}, i = (t) => {\n\t\tif (!t) {\n\t\t\tr.debug = () => {}, r.info = () => {}, r.log = () => {}, r.groupCollapsed = () => {}, r.groupEnd = () => {}, r.warn = () => {}, r.error = () => {};\n\t\t\treturn;\n\t\t}\n\t\tlet n = E(e) ?? [];\n\t\tr.debug = Function.prototype.bind.call(console.debug, console, ...n), r.info = Function.prototype.bind.call(console.info, console, ...n), r.log = Function.prototype.bind.call(console.log, console, ...n), r.groupCollapsed = Function.prototype.bind.call(console.groupCollapsed, console), r.groupEnd = Function.prototype.bind.call(console.groupEnd, console), r.warn = Function.prototype.bind.call(console.warn, console, ...n), r.error = Function.prototype.bind.call(console.error, console, ...n);\n\t}, a = (e) => {\n\t\tn !== e && (n = e, i(n));\n\t};\n\treturn i(n), r;\n}, O = D(), k = \"prose-reader-resource-error\", A = (e, t) => {\n\tlet n = e.split(/(\\d+)/), r = t.split(/(\\d+)/);\n\tfor (let e = 0, t = n.length; e < t; e++) if (n[e] !== r[e]) return n[e]?.match(/\\d/) ? (n[e] || \"\") - +(r[e] || \"\") : (n[e] || \"\").localeCompare(r[e] || \"\");\n\treturn 1;\n}, j = (e) => e.endsWith(\"/\") ? e.slice(0, -1) : e, M = (e) => {\n\tlet t = j(e);\n\treturn t.substring(t.lastIndexOf(\"/\") + 1) || t;\n}, N = (e) => {\n\tlet t = e.lastIndexOf(\"/\");\n\treturn t >= 0 ? e.substring(0, t) : \"\";\n}, P = (e) => e.replaceAll(\"&\", \"&amp;\").replaceAll(\"\\\"\", \"&quot;\").replaceAll(\"'\", \"&apos;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\");\n//#endregion\nexport { k as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME, O as Report, e as arrayEqual, _ as createUniqueXmlSafeId, g as createXmlSafeId, v as createXmlSafeIdFactory, a as detectMimeTypeFromName, P as escapeXmlAttributeValue, y as getItemSpreadPosition, i as getParentPath, N as getUriBasePath, M as getUriBasename, t as getUrlExtension, x as groupBy, s as isMediaContentMimeType, b as isShallowEqual, o as isXmlBasedMimeType, c as parseContentType, j as removeTrailingSlash, S as shallowMergeIfDefined, A as sortByTitleComparator, r as urlJoin };\n\n//# sourceMappingURL=index.js.map","import type { Manifest } from \"@prose-reader/core\"\n\nconst AUDIO_EXTENSIONS = new Set([\n `mp3`,\n `m4a`,\n `m4b`,\n `aac`,\n `ogg`,\n `oga`,\n `wav`,\n `flac`,\n `opus`,\n])\n\nconst getExtensionFromHref = (href: string) => {\n const [pathname = ``] = href.split(/[?#]/)\n const segments = pathname.split(`/`)\n const basename = segments.at(-1) ?? ``\n const extension = basename.split(`.`).at(-1)\n\n return extension?.toLowerCase()\n}\n\nexport const isAudioSpineItem = (item: Manifest[\"spineItems\"][number]) => {\n const mediaType = item.mediaType?.split(`;`).at(0)?.trim().toLowerCase()\n\n if (mediaType?.startsWith(`audio/`)) {\n return true\n }\n\n const extension = getExtensionFromHref(item.href)\n\n return extension ? AUDIO_EXTENSIONS.has(extension) : false\n}\n","import { mapKeysTo } from \"@prose-reader/core\"\nimport { arrayEqual, isShallowEqual } from \"@prose-reader/shared\"\nimport {\n combineLatest,\n distinctUntilChanged,\n map,\n type Observable,\n shareReplay,\n} from \"rxjs\"\nimport type { AudioEnhancerState, AudioTrack } from \"../types\"\nimport { isAudioSpineItem } from \"../utils\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype PaginationTrackWindow = {\n beginSpineItemIndex: number | undefined\n endSpineItemIndex: number | undefined\n}\n\nconst getTrackAtSpineItemIndex = (\n tracks: AudioTrack[],\n index: number | undefined,\n) => {\n if (index === undefined) return undefined\n\n return tracks.find((track) => track.index === index)\n}\n\nconst getVisibleTracks = (\n tracks: AudioTrack[],\n pagination: PaginationTrackWindow,\n) => {\n const beginTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.beginSpineItemIndex,\n )\n const endTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.endSpineItemIndex,\n )\n\n return [beginTrack, endTrack].filter(\n (track, i, arr): track is AudioTrack =>\n track !== undefined && arr.indexOf(track) === i,\n )\n}\n\nexport function createTrackStreams(\n reader: Pick<AudioControllerReader, \"context\" | \"pagination\">,\n state$: Observable<AudioEnhancerState>,\n) {\n const tracks$ = reader.context.manifest$.pipe(\n map((manifest) =>\n manifest.spineItems.filter(isAudioSpineItem).map((item) => ({\n id: item.id,\n href: item.href,\n index: item.index,\n mediaType: item.mediaType,\n })),\n ),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const pagination$ = reader.pagination.state$.pipe(\n mapKeysTo([`beginSpineItemIndex`, `endSpineItemIndex`]),\n distinctUntilChanged(isShallowEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const visibleTrackIds$ = combineLatest([tracks$, pagination$]).pipe(\n map(([tracks, pagination]) =>\n getVisibleTracks(tracks, pagination).map(({ id }) => id),\n ),\n distinctUntilChanged(arrayEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const currentTrack$ = state$.pipe(\n map((state) => state.currentTrack),\n distinctUntilChanged(),\n )\n\n const nextTrack$ = combineLatest([tracks$, pagination$, currentTrack$]).pipe(\n map(([tracks, { endSpineItemIndex }, currentTrack]) => {\n const nextTrackInPaginationWindow =\n currentTrack && endSpineItemIndex !== undefined\n ? tracks.find(\n ({ index }) =>\n index > currentTrack.index && index <= endSpineItemIndex,\n )\n : undefined\n\n const nextTrackAfterCurrentTrack = currentTrack\n ? tracks.find(({ index }) => index > currentTrack.index)\n : undefined\n\n return { nextTrackInPaginationWindow, nextTrackAfterCurrentTrack }\n }),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n tracks$,\n visibleTrackIds$,\n nextTrack$,\n }\n}\n","import { isDefined, ReactiveEntity } from \"@prose-reader/core\"\nimport {\n BehaviorSubject,\n catchError,\n defaultIfEmpty,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n merge,\n type Observable,\n of,\n Subject,\n Subscription,\n share,\n switchMap,\n takeUntil,\n tap,\n withLatestFrom,\n} from \"rxjs\"\nimport type {\n AudioEnhancerState,\n AudioTrack,\n SelectAudioTrackOptions,\n} from \"../types\"\nimport { AudioVisualizer } from \"../visualizer\"\nimport { AudioElementAdapter } from \"./AudioElementAdapter\"\nimport { ResourcesResolver } from \"./ResourcesResolver\"\nimport { createTrackStreams } from \"./trackStreams\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype SelectCommand = {\n trackId: string\n options: SelectAudioTrackOptions\n}\n\ntype DesiredPlayback = {\n shouldPlay: boolean\n trackId: string | undefined\n}\n\nconst initialDesiredPlayback: DesiredPlayback = {\n shouldPlay: false,\n trackId: undefined,\n}\n\nconst initialState: AudioEnhancerState = {\n tracks: [],\n currentTrack: undefined,\n isPlaying: false,\n isLoading: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n}\n\nexport type { AudioControllerReader }\n\nexport class AudioController extends ReactiveEntity<AudioEnhancerState> {\n private readonly reader: AudioControllerReader\n private readonly audio: AudioElementAdapter\n readonly visualizer$: AudioVisualizer\n readonly resourcesResolver = new ResourcesResolver()\n readonly visibleTrackIds$: Observable<string[]>\n\n private readonly playCommandSubject = new Subject<void>()\n private readonly pauseCommandSubject = new Subject<void>()\n private readonly selectCommandSubject = new Subject<SelectCommand>()\n private readonly desiredPlayback$ = new BehaviorSubject(\n initialDesiredPlayback,\n )\n private readonly subscriptions = new Subscription()\n\n constructor(\n reader: AudioControllerReader,\n audio = new AudioElementAdapter(),\n ) {\n super(initialState)\n\n this.reader = reader\n this.audio = audio\n this.visualizer$ = new AudioVisualizer(this.audio.element)\n\n const { tracks$, visibleTrackIds$, nextTrack$ } = createTrackStreams(\n this.reader,\n this.state$,\n )\n\n this.visibleTrackIds$ = visibleTrackIds$\n\n const firstVisibleTrackId$ = this.visibleTrackIds$.pipe(\n map((trackIds) => trackIds[0]),\n distinctUntilChanged(),\n share(),\n )\n\n const visibleTrackReset$ = firstVisibleTrackId$.pipe(\n withLatestFrom(this.state$),\n filter(\n ([trackId, state]) =>\n trackId === undefined &&\n (state.currentTrack?.id !== undefined || state.isLoading),\n ),\n map(([, state]) => state.tracks),\n )\n\n const visibleTrackSelectionIntent$ = firstVisibleTrackId$.pipe(\n filter((trackId): trackId is string => trackId !== undefined),\n withLatestFrom(this.desiredPlayback$),\n map(([trackId, { shouldPlay }]) => ({\n trackId,\n options: {\n navigate: false,\n play: shouldPlay ? true : undefined,\n },\n })),\n )\n\n const tracksChanged$ = tracks$.pipe(\n tap(() => this.resourcesResolver.releaseAll()),\n )\n\n const playbackReset$ = merge(visibleTrackReset$, tracksChanged$).pipe(\n tap((tracks) => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n this.visualizer$.stop({ resetLevels: true })\n this.unmountCurrentSource()\n\n this.mergeCompare({\n tracks,\n currentTrack: undefined,\n isLoading: false,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(undefined)\n }),\n share(),\n )\n\n const playSelectionIntent$ = this.playCommandSubject.pipe(\n filter(() => !this.audio.hasSource),\n map(() => this.state.currentTrack ?? this.state.tracks[0]),\n filter(isDefined),\n map((track) => ({\n trackId: track.id,\n options: { navigate: false, play: true },\n })),\n )\n\n const resumePlayback$ = this.playCommandSubject.pipe(\n filter(() => this.audio.hasSource),\n tap(() => {\n this.emitDesiredPlayback({\n shouldPlay: true,\n trackId: this.state.currentTrack?.id,\n })\n }),\n )\n\n const endedSelectionIntent$ = this.audio.ended$.pipe(\n withLatestFrom(nextTrack$),\n switchMap(\n ([, { nextTrackAfterCurrentTrack, nextTrackInPaginationWindow }]) => {\n this.visualizer$.stop({ resetLevels: true })\n\n if (nextTrackInPaginationWindow) {\n return of({\n trackId: nextTrackInPaginationWindow.id,\n options: { navigate: false, play: true },\n })\n }\n\n if (!nextTrackAfterCurrentTrack) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }\n\n this.reader.navigation.goToRightOrBottomSpineItem()\n\n return EMPTY\n },\n ),\n )\n\n const selection$ = merge(\n this.selectCommandSubject,\n visibleTrackSelectionIntent$,\n endedSelectionIntent$,\n playSelectionIntent$,\n ).pipe(\n withLatestFrom(this.state$),\n filter(([selectionIntent, state]) =>\n state.tracks.some(({ id }) => id === selectionIntent.trackId),\n ),\n map(([selectionIntent, state]) => ({\n selectionIntent,\n state,\n isReselectionWhileLoading:\n state.currentTrack?.id === selectionIntent.trackId && state.isLoading,\n })),\n tap(({ selectionIntent, state, isReselectionWhileLoading }) => {\n if (selectionIntent.options.navigate !== false) {\n this.reader.navigation.navigate({\n spineItem: selectionIntent.trackId,\n animation: `turn`,\n })\n }\n\n if (\n isReselectionWhileLoading &&\n selectionIntent.options.play !== undefined\n ) {\n this.emitDesiredPlayback({\n shouldPlay: selectionIntent.options.play,\n trackId: state.currentTrack?.id,\n })\n }\n }),\n filter(({ isReselectionWhileLoading }) => !isReselectionWhileLoading),\n switchMap(({ selectionIntent: { trackId, options } }) =>\n this.selectTrack$({ trackId, options, playbackReset$ }),\n ),\n )\n\n const playback$ = merge(resumePlayback$, selection$).pipe(\n withLatestFrom(this.desiredPlayback$),\n switchMap(([, { shouldPlay, trackId }]) => {\n if (\n !shouldPlay ||\n !this.audio.hasSource ||\n this.state.currentTrack?.id !== trackId\n ) {\n return EMPTY\n }\n\n this.mergeCompare({ hasError: false })\n\n return this.audio.play$().pipe(\n catchError(() => {\n this.mergeCompare({ hasError: true })\n\n return EMPTY\n }),\n )\n }),\n )\n\n this.subscriptions.add(playbackReset$.subscribe())\n this.subscriptions.add(playback$.subscribe())\n\n this.subscriptions.add(\n this.pauseCommandSubject.subscribe(() => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }),\n )\n\n this.subscriptions.add(\n this.audio.isPlaying$.subscribe((isPlaying) => {\n this.mergeCompare({ isPlaying })\n\n if (!isPlaying) {\n this.visualizer$.stop()\n return\n }\n\n if (!this.state.currentTrack) return\n\n this.visualizer$.start(this.state.currentTrack)\n }),\n )\n\n this.subscriptions.add(\n this.audio.metrics$.subscribe(({ currentTime, duration }) => {\n this.mergeCompare({ currentTime, duration })\n }),\n )\n }\n\n get state() {\n return this.value\n }\n\n get visualizer() {\n return this.visualizer$.value\n }\n\n private resetTrackSelection({\n currentTrack,\n isLoading,\n }: {\n currentTrack: AudioTrack\n isLoading: boolean\n }) {\n this.mergeCompare({\n currentTrack,\n isLoading,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(currentTrack?.id)\n }\n\n private emitDesiredPlayback({ shouldPlay, trackId }: DesiredPlayback) {\n this.desiredPlayback$.next({\n shouldPlay,\n trackId: shouldPlay ? trackId : undefined,\n })\n\n if (!shouldPlay) {\n this.audio.pause()\n }\n }\n\n private unmountCurrentSource() {\n if (!this.audio.hasSource) return\n\n const trackId = this.state.currentTrack?.id\n\n if (!this.audio.paused) {\n this.audio.pause()\n }\n\n this.audio.unloadSource()\n\n if (trackId) {\n this.resourcesResolver.releaseTrackSource(trackId)\n }\n }\n\n private mountSource(source: string) {\n this.unmountCurrentSource()\n this.audio.loadSource(source)\n }\n\n private resolveTrackSource(track: AudioTrack) {\n const spineItem = this.reader.spineItemsManager.get(track.index)\n\n if (!spineItem) return EMPTY\n\n return this.resourcesResolver.getTrackResourceUrl$(\n track,\n spineItem.resourcesHandler,\n )\n }\n\n private selectTrack$({\n trackId,\n options,\n playbackReset$,\n }: {\n trackId: string\n options: SelectAudioTrackOptions\n playbackReset$: Observable<AudioTrack[]>\n }) {\n const track = this.state.tracks.find(({ id }) => id === trackId)\n\n if (!track) return EMPTY\n\n const currentTrack = this.state.currentTrack\n const shouldPlay =\n options.play ?? (!this.audio.paused && currentTrack !== undefined)\n\n this.emitDesiredPlayback({ shouldPlay, trackId: track.id })\n\n if (currentTrack?.id === track.id && this.audio.hasSource) {\n this.visualizer$.setTrack(track.id)\n\n return shouldPlay ? of(undefined) : EMPTY\n }\n\n this.unmountCurrentSource()\n\n this.resetTrackSelection({\n currentTrack: track,\n isLoading: true,\n })\n\n return this.resolveTrackSource(track).pipe(\n map((source) => ({ source })),\n defaultIfEmpty({ source: undefined }),\n catchError(() => of({ source: undefined })),\n takeUntil(playbackReset$),\n tap(({ source }) => {\n if (!source) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n } else {\n this.mountSource(source)\n }\n\n this.mergeCompare({ isLoading: false })\n }),\n filter(({ source }) => source !== undefined),\n map(() => undefined),\n )\n }\n\n select(trackId: string, options: SelectAudioTrackOptions = {}) {\n this.selectCommandSubject.next({ trackId, options })\n }\n\n play() {\n this.playCommandSubject.next()\n }\n\n pause() {\n this.pauseCommandSubject.next()\n }\n\n toggle() {\n if (this.audio.paused) {\n this.play()\n return\n }\n\n this.pause()\n }\n\n setCurrentTime(value: number) {\n this.mergeCompare({ currentTime: value })\n this.audio.setCurrentTime(value)\n }\n\n destroy() {\n this.subscriptions.unsubscribe()\n this.playCommandSubject.complete()\n this.pauseCommandSubject.complete()\n this.selectCommandSubject.complete()\n this.desiredPlayback$.complete()\n this.visualizer$.destroy()\n this.unmountCurrentSource()\n this.resourcesResolver.destroy()\n\n super.destroy()\n }\n}\n","import { DocumentRenderer, setStylePropertyIfChanged } from \"@prose-reader/core\"\nimport { EMPTY, of } from \"rxjs\"\n\nexport class AudioRenderer extends DocumentRenderer {\n onCreateDocument() {\n const ownerDocument = this.containerElement.ownerDocument\n const rootElement = ownerDocument.createElement(`div`)\n\n rootElement.style.cssText = `\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: block;\n `\n rootElement.setAttribute(`data-prose-reader-audio-page`, this.item.id)\n\n this.setDocumentContainer(rootElement)\n\n return of(rootElement)\n }\n\n onLoadDocument() {\n this.attach()\n\n return EMPTY\n }\n\n onUnload() {\n this.detach()\n\n return EMPTY\n }\n\n onLayout() {\n const { width, height } = this.viewport.pageSize\n const rootElement = this.documentContainer\n\n if (rootElement) {\n setStylePropertyIfChanged(rootElement.style, `width`, `${width}px`)\n setStylePropertyIfChanged(rootElement.style, `height`, `${height}px`)\n }\n\n return of({ width, height })\n }\n\n onRenderHeadless() {\n return EMPTY\n }\n\n getDocumentFrame() {\n return undefined\n }\n\n /**\n * Audio spine items are always pre-paginated (one track per page).\n * Mixed audio/text books with reflowable audio chapters may need\n * to revisit this if that use case arises.\n */\n get renditionLayout(): `pre-paginated` {\n return `pre-paginated`\n }\n}\n","import type { CreateReaderOptions, Reader } from \"@prose-reader/core\"\nimport { AudioController } from \"./playback/AudioController\"\nimport { AudioRenderer } from \"./renderer/AudioRenderer\"\nimport type { AudioEnhancerAPI } from \"./types\"\nimport { isAudioSpineItem } from \"./utils\"\n\nexport const audioEnhancer =\n <InheritOptions extends CreateReaderOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & AudioEnhancerAPI => {\n const readerOptions = { ...options }\n\n const reader = next({\n ...readerOptions,\n getRenderer(item) {\n const maybeFactory = options.getRenderer?.(item)\n\n if (maybeFactory) {\n return maybeFactory\n }\n\n if (isAudioSpineItem(item)) {\n return (params) => new AudioRenderer(params)\n }\n\n return undefined\n },\n })\n\n const controller = new AudioController(reader)\n\n const destroy = () => {\n controller.destroy()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_AUDIO: true,\n destroy,\n audio: {\n state$: controller.state$,\n visualizer$: controller.visualizer$,\n visibleTrackIds$: controller.visibleTrackIds$,\n get state() {\n return controller.state\n },\n get visualizer() {\n return controller.visualizer\n },\n isAudioRenderer: (renderer) => renderer instanceof AudioRenderer,\n play: () => controller.play(),\n pause: () => controller.pause(),\n toggle: () => controller.toggle(),\n setCurrentTime: (value) => controller.setCurrentTime(value),\n select: (trackId, options) => controller.select(trackId, options),\n },\n }\n }\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/visualizer/levels.ts","../src/visualizer/VisualizerAudioGraph.ts","../src/visualizer/AudioVisualizer.ts","../src/playback/AudioElementAdapter.ts","../src/playback/ResourcesResolver.ts","../../shared/dist/index.js","../src/utils.ts","../src/playback/trackStreams.ts","../src/playback/AudioController.ts","../src/renderer/AudioRenderer.ts","../src/audioEnhancer.ts"],"sourcesContent":["const AUDIO_VISUALIZER_LEVEL_COUNT = 80\nconst AUDIO_VISUALIZER_NOISE_FLOOR = 0.035\n\nexport const getIdleVisualizerLevels = () =>\n Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, () => 0)\n\nexport const getVisualizerLevels = (data: ArrayLike<number>) => {\n if (data.length === 0) return getIdleVisualizerLevels()\n\n const bucketSize = Math.max(\n 1,\n Math.floor(data.length / AUDIO_VISUALIZER_LEVEL_COUNT),\n )\n\n return Array.from({ length: AUDIO_VISUALIZER_LEVEL_COUNT }, (_, index) => {\n const start = index * bucketSize\n const end = Math.min(data.length, start + bucketSize)\n\n if (start >= data.length || start === end) return 0\n\n let total = 0\n\n for (let cursor = start; cursor < end; cursor += 1) {\n total += data[cursor] ?? 0\n }\n\n const average = total / (end - start)\n const normalizedAverage = average / 255\n const gatedAverage =\n (normalizedAverage - AUDIO_VISUALIZER_NOISE_FLOOR) /\n (1 - AUDIO_VISUALIZER_NOISE_FLOOR)\n\n return Math.max(0, Math.min(1, gatedAverage))\n })\n}\n","import { getIdleVisualizerLevels, getVisualizerLevels } from \"./levels\"\n\nconst AUDIO_VISUALIZER_FFT_SIZE = 256\n\nexport class VisualizerAudioGraph {\n private audioContext: AudioContext | undefined\n private audioSourceNode: MediaElementAudioSourceNode | undefined\n private analyserNode: AnalyserNode | undefined\n private frequencyData: Uint8Array<ArrayBuffer> | undefined\n\n constructor(private readonly audioElement: HTMLAudioElement) {}\n\n async resumeIfNeeded() {\n if (!this.ensure()) {\n return false\n }\n\n const audioContext = this.audioContext\n\n if (!audioContext) {\n return false\n }\n\n if (audioContext.state !== `suspended`) {\n return true\n }\n\n try {\n await audioContext.resume()\n\n return true\n } catch {\n return false\n }\n }\n\n readLevels() {\n if (!this.analyserNode || !this.frequencyData) {\n return getIdleVisualizerLevels()\n }\n\n this.analyserNode.getByteFrequencyData(this.frequencyData)\n\n return getVisualizerLevels(this.frequencyData)\n }\n\n destroy() {\n this.audioSourceNode?.disconnect()\n this.analyserNode?.disconnect()\n this.audioContext?.close().catch(() => undefined)\n this.audioContext = undefined\n this.audioSourceNode = undefined\n this.analyserNode = undefined\n this.frequencyData = undefined\n }\n\n private ensure() {\n if (this.audioContext && this.analyserNode && this.frequencyData) {\n return true\n }\n\n if (typeof window === `undefined` || !window.AudioContext) {\n return false\n }\n\n const audioContext = new window.AudioContext()\n const audioSourceNode = audioContext.createMediaElementSource(\n this.audioElement,\n )\n const analyserNode = audioContext.createAnalyser()\n analyserNode.fftSize = AUDIO_VISUALIZER_FFT_SIZE\n analyserNode.smoothingTimeConstant = 0.8\n audioSourceNode.connect(analyserNode)\n analyserNode.connect(audioContext.destination)\n\n this.audioContext = audioContext\n this.audioSourceNode = audioSourceNode\n this.analyserNode = analyserNode\n this.frequencyData = new Uint8Array(analyserNode.frequencyBinCount)\n\n return true\n }\n}\n","import { ReactiveEntity } from \"@prose-reader/core\"\nimport {\n animationFrames,\n BehaviorSubject,\n concat,\n defaultIfEmpty,\n defer,\n distinctUntilChanged,\n EMPTY,\n from,\n map,\n of,\n Subscription,\n switchMap,\n} from \"rxjs\"\nimport type { AudioTrack, AudioVisualizerState } from \"../types\"\nimport { getIdleVisualizerLevels } from \"./levels\"\nimport { VisualizerAudioGraph } from \"./VisualizerAudioGraph\"\n\ntype AudioVisualizerPlaybackState = {\n trackId: string | undefined\n isRunning: boolean\n resetLevels: boolean\n}\n\nexport class AudioVisualizer extends ReactiveEntity<AudioVisualizerState> {\n private readonly audioGraph: VisualizerAudioGraph\n private readonly playbackState$ =\n new BehaviorSubject<AudioVisualizerPlaybackState>({\n trackId: undefined,\n isRunning: false,\n resetLevels: false,\n })\n private readonly subscriptions = new Subscription()\n\n constructor(audioElement: HTMLAudioElement) {\n super({\n levels: getIdleVisualizerLevels(),\n isActive: false,\n trackId: undefined,\n })\n this.audioGraph = new VisualizerAudioGraph(audioElement)\n\n this.subscriptions.add(\n this.playbackState$\n .pipe(\n distinctUntilChanged(\n (previous, next) =>\n previous.trackId === next.trackId &&\n previous.isRunning === next.isRunning &&\n previous.resetLevels === next.resetLevels,\n ),\n switchMap(({ trackId, isRunning, resetLevels }) => {\n if (!trackId || !isRunning) {\n return of({\n trackId,\n isActive: false,\n ...(resetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n })\n }\n\n const shouldResetLevels = this.value.trackId !== trackId\n\n return concat(\n of({\n trackId,\n isActive: true,\n ...(shouldResetLevels\n ? { levels: getIdleVisualizerLevels() }\n : undefined),\n }),\n this.createLevels$().pipe(\n map((levels) => ({\n trackId,\n levels,\n isActive: true,\n })),\n defaultIfEmpty({\n trackId,\n isActive: false,\n levels: getIdleVisualizerLevels(),\n }),\n ),\n )\n }),\n )\n .subscribe((value) => {\n this.mergeCompare(value)\n }),\n )\n }\n\n start(currentTrack: AudioTrack | undefined) {\n if (!currentTrack) return\n\n this.playbackState$.next({\n trackId: currentTrack.id,\n isRunning: true,\n resetLevels: false,\n })\n }\n\n setTrack(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: false,\n })\n }\n\n reset(trackId: string | undefined) {\n this.playbackState$.next({\n trackId,\n isRunning: false,\n resetLevels: true,\n })\n }\n\n stop({ resetLevels = false }: { resetLevels?: boolean } = {}) {\n this.playbackState$.next({\n trackId: this.playbackState$.value.trackId,\n isRunning: false,\n resetLevels,\n })\n }\n\n override destroy() {\n this.stop({\n resetLevels: true,\n })\n this.subscriptions.unsubscribe()\n this.playbackState$.complete()\n this.audioGraph.destroy()\n\n super.destroy()\n }\n\n private createLevels$() {\n return defer(() => {\n return from(this.audioGraph.resumeIfNeeded()).pipe(\n switchMap((isReady) =>\n isReady\n ? animationFrames().pipe(map(() => this.audioGraph.readLevels()))\n : EMPTY,\n ),\n )\n })\n }\n}\n","import {\n defer,\n from,\n fromEvent,\n map,\n merge,\n type Observable,\n retry,\n share,\n take,\n} from \"rxjs\"\n\nexport type AudioMetrics = {\n currentTime: number\n duration: number | undefined\n}\n\nexport class AudioElementAdapter {\n readonly element: HTMLAudioElement\n readonly canPlay$: Observable<Event>\n readonly ended$: Observable<Event>\n readonly isPlaying$: Observable<boolean>\n readonly metrics$: Observable<AudioMetrics>\n\n constructor() {\n this.element = document.createElement(`audio`)\n this.element.preload = `metadata`\n\n this.canPlay$ = fromEvent(this.element, `canplay`).pipe(share())\n this.ended$ = fromEvent(this.element, `ended`).pipe(share())\n\n this.isPlaying$ = merge(\n fromEvent(this.element, `play`).pipe(map(() => true)),\n fromEvent(this.element, `pause`).pipe(map(() => false)),\n ).pipe(share())\n\n this.metrics$ = merge(\n fromEvent(this.element, `timeupdate`),\n fromEvent(this.element, `seeking`),\n fromEvent(this.element, `seeked`),\n fromEvent(this.element, `loadedmetadata`),\n fromEvent(this.element, `durationchange`),\n this.canPlay$,\n ).pipe(\n map(() => ({\n currentTime: this.element.currentTime,\n duration: Number.isFinite(this.element.duration)\n ? this.element.duration\n : undefined,\n })),\n share(),\n )\n }\n\n get paused() {\n return this.element.paused\n }\n\n get hasSource() {\n return this.element.hasAttribute(`src`)\n }\n\n play$() {\n return defer(() => from(this.element.play())).pipe(\n retry({\n count: 1,\n delay: () => this.canPlay$.pipe(take(1)),\n }),\n )\n }\n\n pause() {\n this.element.pause()\n }\n\n loadSource(src: string) {\n this.element.src = src\n this.element.load()\n }\n\n unloadSource() {\n this.element.removeAttribute(`src`)\n this.element.load()\n }\n\n setCurrentTime(value: number) {\n this.element.currentTime = value\n }\n}\n","import type { ResourceHandler } from \"@prose-reader/core\"\nimport { from, map, of, switchMap } from \"rxjs\"\nimport type { AudioTrack } from \"../types\"\n\ntype CachedTrackSource = {\n url: string\n release?: () => void\n}\n\nexport class ResourcesResolver {\n private readonly cachedSourceByTrackId = new Map<string, CachedTrackSource>()\n\n public hasCachedSource(trackId: string): boolean {\n return this.cachedSourceByTrackId.has(trackId)\n }\n\n public getTrackResourceUrl$ = (\n track: AudioTrack,\n resourcesHandler: Pick<ResourceHandler, \"getResource\">,\n ) => {\n const cachedSource = this.cachedSourceByTrackId.get(track.id)\n\n if (cachedSource) {\n return of(cachedSource.url)\n }\n\n const resource$ = from(resourcesHandler.getResource())\n\n return resource$.pipe(\n switchMap((resource) => {\n if (resource instanceof URL) {\n this.cachedSourceByTrackId.set(track.id, {\n url: resource.href,\n })\n\n return of(resource.href)\n }\n\n if (resource instanceof Response) {\n return from(resource.blob()).pipe(\n map((blob) => {\n const objectUrl = URL.createObjectURL(blob)\n\n this.cachedSourceByTrackId.set(track.id, {\n url: objectUrl,\n release: () => {\n URL.revokeObjectURL(objectUrl)\n },\n })\n\n return objectUrl\n }),\n )\n }\n\n this.cachedSourceByTrackId.set(track.id, {\n url: track.href,\n })\n\n return of(track.href)\n }),\n )\n }\n\n public releaseTrackSource(trackId: string) {\n const cachedSource = this.cachedSourceByTrackId.get(trackId)\n\n if (!cachedSource) return\n\n this.cachedSourceByTrackId.delete(trackId)\n cachedSource.release?.()\n }\n\n public releaseAll() {\n for (const trackId of this.cachedSourceByTrackId.keys()) {\n this.releaseTrackSource(trackId)\n }\n }\n\n public destroy() {\n this.releaseAll()\n }\n}\n","//#region src/array.ts\nvar e = (e, t) => e === t || e.length === t.length && e.every((e, n) => e === t[n]), t = (e) => e.split(/[#?]/)[0]?.split(\".\").pop()?.trim() || \"\";\nfunction n(e) {\n\tlet t = [];\n\tif (e.length === 0) return \"\";\n\tif (typeof e[0] != \"string\") throw TypeError(`Url must be a string. Received ${e[0]}`);\n\te[0].match(/^[^/:]+:\\/*$/) && e.length > 1 && (e[0] = e.shift() + e[0]), e[0].match(/^file:\\/\\/\\//) ? e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1:///\") : e[0] = e[0].replace(/^([^/:]+):\\/*/, \"$1://\");\n\tfor (let n = 0; n < e.length; n++) {\n\t\tlet r = e[n];\n\t\tif (typeof r != \"string\") throw TypeError(`Url must be a string. Received ${r}`);\n\t\tr !== \"\" && (n > 0 && (r = r.replace(/^[/]+/, \"\")), r = n < e.length - 1 ? r.replace(/[/]+$/, \"\") : r.replace(/[/]+$/, \"/\"), t.push(r));\n\t}\n\tlet n = t.join(\"/\");\n\tn = n.replace(/\\/(\\?|&|#[^!])/g, \"$1\");\n\tlet r = n.split(\"?\");\n\treturn n = r.shift() + (r.length > 0 ? \"?\" : \"\") + r.join(\"&\"), n;\n}\nfunction r(...e) {\n\treturn n(Array.from(Array.isArray(e[0]) ? e[0] : e));\n}\nfunction i(e) {\n\tlet t = e.split(\"/\");\n\treturn t.pop(), t.join(\"/\");\n}\n//#endregion\n//#region src/contentType.ts\nvar a = (e) => {\n\tswitch (t(e)) {\n\t\tcase \"png\": return \"image/png\";\n\t\tcase \"jpg\": return \"image/jpg\";\n\t\tcase \"jpeg\": return \"image/jpeg\";\n\t\tcase \"txt\": return \"text/plain\";\n\t\tcase \"webp\": return \"image/webp\";\n\t\tcase \"xhtml\": return \"application/xhtml+xml\";\n\t\tcase \"mp3\": return \"audio/mpeg\";\n\t\tcase \"m4a\":\n\t\tcase \"m4b\": return \"audio/mp4\";\n\t\tcase \"aac\": return \"audio/aac\";\n\t\tcase \"ogg\":\n\t\tcase \"oga\": return \"audio/ogg\";\n\t\tcase \"wav\": return \"audio/wav\";\n\t\tcase \"flac\": return \"audio/flac\";\n\t\tcase \"opus\": return \"audio/opus\";\n\t}\n}, o = ({ mimeType: e, uri: t }) => (e ?? a(t ?? \"\"))?.startsWith(\"application/xhtml+xml\"), s = (e) => e.startsWith(\"image/\") || e.startsWith(\"audio/\") || e.startsWith(\"video/\"), c = (e) => {\n\tif (!e.length) return;\n\tlet t = e.indexOf(\";\");\n\treturn t >= 0 ? e.substring(0, t) : e;\n}, l = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/, u = /^[A-Za-z0-9_]/, d = /^xml/i, f = /[^A-Za-z0-9_.-]+/g, p = \"_\", m = (e) => e.replace(/^_+|_+$/g, \"\"), h = (e) => m(e.trim().replaceAll(\"/\", \"_\").replace(f, \"_\")), g = (e) => {\n\tif (l.test(e) && !d.test(e)) return e;\n\tlet t = h(e), n = t && !d.test(t) ? t : `${p}${t}`;\n\treturn u.test(n) ? n : `${p}${n}`;\n}, _ = (e, t) => {\n\tlet n = g(e);\n\tif (!t.has(n)) return t.add(n), n;\n\tlet r = 2, i = `${n}-${r}`;\n\tfor (; t.has(i);) r += 1, i = `${n}-${r}`;\n\treturn t.add(i), i;\n}, v = () => {\n\tlet e = /* @__PURE__ */ new Set();\n\treturn (t) => _(t, e);\n}, y = (e) => {\n\tlet t = e.pageSpreadLeft === !0;\n\tif (t !== (e.pageSpreadRight === !0)) return t ? \"left\" : \"right\";\n}, b = (e, t, n) => {\n\tif (Object.is(e, t)) return !0;\n\tif (typeof e != \"object\" || !e || typeof t != \"object\" || !t) return !1;\n\tlet r = Object.keys(e), i = Object.keys(t);\n\tif (r.length !== i.length) return !1;\n\tlet a = n?.customEqual ?? Object.is, o = e, s = t;\n\tfor (let e of r) if (!Object.hasOwn(s, e) || !a(o[e], s[e])) return !1;\n\treturn !0;\n}, x = (e, t) => e.reduce((e, n) => {\n\tlet r = t(n);\n\treturn e[r] || (e[r] = []), e[r].push(n), e;\n}, {});\nfunction S(e, t) {\n\tlet n = { ...e };\n\tfor (let r in t) if (Object.hasOwn(t, r)) {\n\t\tlet i = t[r];\n\t\t(i !== void 0 || !(r in e)) && (n[r] = i);\n\t}\n\treturn n;\n}\n//#endregion\n//#region src/report.ts\nfunction C() {\n\tlet e = globalThis.__PROSE_READER_DEBUG;\n\treturn e === !0 || e === \"true\";\n}\nvar w = (e, t) => typeof e == \"boolean\" ? {\n\tenabled: e,\n\toptions: t\n} : {\n\tenabled: e?.enabled,\n\toptions: e ?? t\n}, T = (e) => e.at(-1)?.color, E = (e) => {\n\tif (!e.length) return;\n\tif (!e.some((e) => e.color)) return [e.map((e) => e.label).join(\" \")];\n\tlet t = \"\", n = [];\n\tfor (let r of e) t += `%c${t ? ` ${r.label}` : r.label}`, n.push(r.color ? `color: ${r.color}` : \"\");\n\treturn [t, ...n];\n}, D = (e = [], t = C()) => {\n\tlet n = t, r = {\n\t\tenable: (e) => {\n\t\t\ta(e);\n\t\t},\n\t\tnamespace: (t, r, i) => {\n\t\t\tlet a = w(r, i), o = a.options?.color ?? T(e);\n\t\t\treturn D([...e, {\n\t\t\t\tlabel: `[${t}]`,\n\t\t\t\tcolor: o\n\t\t\t}], a.enabled ?? n);\n\t\t},\n\t\tisEnabled: () => n,\n\t\tdebug: () => {},\n\t\tinfo: () => {},\n\t\tlog: () => {},\n\t\tgroupCollapsed: () => {},\n\t\tgroupEnd: () => {},\n\t\tgetGroupArgs: (t) => {\n\t\t\tlet n = E(e);\n\t\t\tif (!n) return [t];\n\t\t\tlet [r, ...i] = n;\n\t\t\treturn [`${r} ${t}`, ...i];\n\t\t},\n\t\twarn: () => {},\n\t\terror: () => {}\n\t}, i = (t) => {\n\t\tif (!t) {\n\t\t\tr.debug = () => {}, r.info = () => {}, r.log = () => {}, r.groupCollapsed = () => {}, r.groupEnd = () => {}, r.warn = () => {}, r.error = () => {};\n\t\t\treturn;\n\t\t}\n\t\tlet n = E(e) ?? [];\n\t\tr.debug = Function.prototype.bind.call(console.debug, console, ...n), r.info = Function.prototype.bind.call(console.info, console, ...n), r.log = Function.prototype.bind.call(console.log, console, ...n), r.groupCollapsed = Function.prototype.bind.call(console.groupCollapsed, console), r.groupEnd = Function.prototype.bind.call(console.groupEnd, console), r.warn = Function.prototype.bind.call(console.warn, console, ...n), r.error = Function.prototype.bind.call(console.error, console, ...n);\n\t}, a = (e) => {\n\t\tn !== e && (n = e, i(n));\n\t};\n\treturn i(n), r;\n}, O = D(), k = \"prose-reader-resource-error\", A = (e, t) => {\n\tlet n = e.split(/(\\d+)/), r = t.split(/(\\d+)/);\n\tfor (let e = 0, t = n.length; e < t; e++) if (n[e] !== r[e]) return n[e]?.match(/\\d/) ? (n[e] || \"\") - +(r[e] || \"\") : (n[e] || \"\").localeCompare(r[e] || \"\");\n\treturn 1;\n}, j = (e) => e.endsWith(\"/\") ? e.slice(0, -1) : e, M = (e) => {\n\tlet t = j(e);\n\treturn t.substring(t.lastIndexOf(\"/\") + 1) || t;\n}, N = (e) => {\n\tlet t = e.lastIndexOf(\"/\");\n\treturn t >= 0 ? e.substring(0, t) : \"\";\n}, P = (e) => e.replaceAll(\"&\", \"&amp;\").replaceAll(\"\\\"\", \"&quot;\").replaceAll(\"'\", \"&apos;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\");\n//#endregion\nexport { k as PROSE_READER_RESOURCE_ERROR_INJECTED_META_NAME, O as Report, e as arrayEqual, _ as createUniqueXmlSafeId, g as createXmlSafeId, v as createXmlSafeIdFactory, a as detectMimeTypeFromName, P as escapeXmlAttributeValue, y as getItemSpreadPosition, i as getParentPath, N as getUriBasePath, M as getUriBasename, t as getUrlExtension, x as groupBy, s as isMediaContentMimeType, b as isShallowEqual, o as isXmlBasedMimeType, c as parseContentType, j as removeTrailingSlash, S as shallowMergeIfDefined, A as sortByTitleComparator, r as urlJoin };\n\n//# sourceMappingURL=index.js.map","import type { Manifest } from \"@prose-reader/core\"\n\nconst AUDIO_EXTENSIONS = new Set([\n `mp3`,\n `m4a`,\n `m4b`,\n `aac`,\n `ogg`,\n `oga`,\n `wav`,\n `flac`,\n `opus`,\n])\n\nconst getExtensionFromHref = (href: string) => {\n const [pathname = ``] = href.split(/[?#]/)\n const segments = pathname.split(`/`)\n const basename = segments.at(-1) ?? ``\n const extension = basename.split(`.`).at(-1)\n\n return extension?.toLowerCase()\n}\n\nexport const isAudioSpineItem = (item: Manifest[\"spineItems\"][number]) => {\n const mediaType = item.mediaType?.split(`;`).at(0)?.trim().toLowerCase()\n\n if (mediaType?.startsWith(`audio/`)) {\n return true\n }\n\n const extension = getExtensionFromHref(item.href)\n\n return extension ? AUDIO_EXTENSIONS.has(extension) : false\n}\n","import { mapKeysTo } from \"@prose-reader/core\"\nimport { arrayEqual, isShallowEqual } from \"@prose-reader/shared\"\nimport {\n combineLatest,\n distinctUntilChanged,\n map,\n type Observable,\n of,\n shareReplay,\n} from \"rxjs\"\nimport type { AudioEnhancerState, AudioTrack } from \"../types\"\nimport { isAudioSpineItem } from \"../utils\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype PaginationTrackWindow = {\n beginSpineItemIndex: number | undefined\n endSpineItemIndex: number | undefined\n}\n\nconst getTrackAtSpineItemIndex = (\n tracks: AudioTrack[],\n index: number | undefined,\n) => {\n if (index === undefined) return undefined\n\n return tracks.find((track) => track.index === index)\n}\n\nconst getVisibleTracks = (\n tracks: AudioTrack[],\n pagination: PaginationTrackWindow,\n) => {\n const beginTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.beginSpineItemIndex,\n )\n const endTrack = getTrackAtSpineItemIndex(\n tracks,\n pagination.endSpineItemIndex,\n )\n\n return [beginTrack, endTrack].filter(\n (track, i, arr): track is AudioTrack =>\n track !== undefined && arr.indexOf(track) === i,\n )\n}\n\nexport function createTrackStreams(\n reader: Pick<AudioControllerReader, \"context\" | \"pagination\">,\n state$: Observable<AudioEnhancerState>,\n) {\n const tracks = reader.context.manifest.spineItems\n .filter(isAudioSpineItem)\n .map((item) => ({\n id: item.id,\n href: item.href,\n index: item.index,\n mediaType: item.mediaType,\n }))\n const tracks$ = of(tracks)\n\n const pagination$ = reader.pagination.state$.pipe(\n mapKeysTo([`beginSpineItemIndex`, `endSpineItemIndex`]),\n distinctUntilChanged(isShallowEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const visibleTrackIds$ = combineLatest([tracks$, pagination$]).pipe(\n map(([tracks, pagination]) =>\n getVisibleTracks(tracks, pagination).map(({ id }) => id),\n ),\n distinctUntilChanged(arrayEqual),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n const currentTrack$ = state$.pipe(\n map((state) => state.currentTrack),\n distinctUntilChanged(),\n )\n\n const nextTrack$ = combineLatest([tracks$, pagination$, currentTrack$]).pipe(\n map(([tracks, { endSpineItemIndex }, currentTrack]) => {\n const nextTrackInPaginationWindow =\n currentTrack && endSpineItemIndex !== undefined\n ? tracks.find(\n ({ index }) =>\n index > currentTrack.index && index <= endSpineItemIndex,\n )\n : undefined\n\n const nextTrackAfterCurrentTrack = currentTrack\n ? tracks.find(({ index }) => index > currentTrack.index)\n : undefined\n\n return { nextTrackInPaginationWindow, nextTrackAfterCurrentTrack }\n }),\n shareReplay({ bufferSize: 1, refCount: true }),\n )\n\n return {\n tracks$,\n visibleTrackIds$,\n nextTrack$,\n }\n}\n","import { isDefined, ReactiveEntity } from \"@prose-reader/core\"\nimport {\n BehaviorSubject,\n catchError,\n defaultIfEmpty,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n merge,\n type Observable,\n of,\n Subject,\n Subscription,\n share,\n switchMap,\n takeUntil,\n tap,\n withLatestFrom,\n} from \"rxjs\"\nimport type {\n AudioEnhancerState,\n AudioTrack,\n SelectAudioTrackOptions,\n} from \"../types\"\nimport { AudioVisualizer } from \"../visualizer\"\nimport { AudioElementAdapter } from \"./AudioElementAdapter\"\nimport { ResourcesResolver } from \"./ResourcesResolver\"\nimport { createTrackStreams } from \"./trackStreams\"\nimport type { AudioControllerReader } from \"./types\"\n\ntype SelectCommand = {\n trackId: string\n options: SelectAudioTrackOptions\n}\n\ntype DesiredPlayback = {\n shouldPlay: boolean\n trackId: string | undefined\n}\n\nconst initialDesiredPlayback: DesiredPlayback = {\n shouldPlay: false,\n trackId: undefined,\n}\n\nconst initialState: AudioEnhancerState = {\n tracks: [],\n currentTrack: undefined,\n isPlaying: false,\n isLoading: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n}\n\nexport type { AudioControllerReader }\n\nexport class AudioController extends ReactiveEntity<AudioEnhancerState> {\n private readonly reader: AudioControllerReader\n private readonly audio: AudioElementAdapter\n readonly visualizer$: AudioVisualizer\n readonly resourcesResolver = new ResourcesResolver()\n readonly visibleTrackIds$: Observable<string[]>\n\n private readonly playCommandSubject = new Subject<void>()\n private readonly pauseCommandSubject = new Subject<void>()\n private readonly selectCommandSubject = new Subject<SelectCommand>()\n private readonly desiredPlayback$ = new BehaviorSubject(\n initialDesiredPlayback,\n )\n private readonly subscriptions = new Subscription()\n\n constructor(\n reader: AudioControllerReader,\n audio = new AudioElementAdapter(),\n ) {\n super(initialState)\n\n this.reader = reader\n this.audio = audio\n this.visualizer$ = new AudioVisualizer(this.audio.element)\n\n const { tracks$, visibleTrackIds$, nextTrack$ } = createTrackStreams(\n this.reader,\n this.state$,\n )\n\n this.visibleTrackIds$ = visibleTrackIds$\n\n const firstVisibleTrackId$ = this.visibleTrackIds$.pipe(\n map((trackIds) => trackIds[0]),\n distinctUntilChanged(),\n share(),\n )\n\n const visibleTrackReset$ = firstVisibleTrackId$.pipe(\n withLatestFrom(this.state$),\n filter(\n ([trackId, state]) =>\n trackId === undefined &&\n (state.currentTrack?.id !== undefined || state.isLoading),\n ),\n map(([, state]) => state.tracks),\n )\n\n const visibleTrackSelectionIntent$ = firstVisibleTrackId$.pipe(\n filter((trackId): trackId is string => trackId !== undefined),\n withLatestFrom(this.desiredPlayback$),\n map(([trackId, { shouldPlay }]) => ({\n trackId,\n options: {\n navigate: false,\n play: shouldPlay ? true : undefined,\n },\n })),\n )\n\n const tracksChanged$ = tracks$.pipe(\n tap(() => this.resourcesResolver.releaseAll()),\n )\n\n const playbackReset$ = merge(visibleTrackReset$, tracksChanged$).pipe(\n tap((tracks) => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n this.visualizer$.stop({ resetLevels: true })\n this.unmountCurrentSource()\n\n this.mergeCompare({\n tracks,\n currentTrack: undefined,\n isLoading: false,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(undefined)\n }),\n share(),\n )\n\n const playSelectionIntent$ = this.playCommandSubject.pipe(\n filter(() => !this.audio.hasSource),\n map(() => this.state.currentTrack ?? this.state.tracks[0]),\n filter(isDefined),\n map((track) => ({\n trackId: track.id,\n options: { navigate: false, play: true },\n })),\n )\n\n const resumePlayback$ = this.playCommandSubject.pipe(\n filter(() => this.audio.hasSource),\n tap(() => {\n this.emitDesiredPlayback({\n shouldPlay: true,\n trackId: this.state.currentTrack?.id,\n })\n }),\n )\n\n const endedSelectionIntent$ = this.audio.ended$.pipe(\n withLatestFrom(nextTrack$),\n switchMap(\n ([, { nextTrackAfterCurrentTrack, nextTrackInPaginationWindow }]) => {\n this.visualizer$.stop({ resetLevels: true })\n\n if (nextTrackInPaginationWindow) {\n return of({\n trackId: nextTrackInPaginationWindow.id,\n options: { navigate: false, play: true },\n })\n }\n\n if (!nextTrackAfterCurrentTrack) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }\n\n this.reader.navigation.goToRightOrBottomSpineItem()\n\n return EMPTY\n },\n ),\n )\n\n const selection$ = merge(\n this.selectCommandSubject,\n visibleTrackSelectionIntent$,\n endedSelectionIntent$,\n playSelectionIntent$,\n ).pipe(\n withLatestFrom(this.state$),\n filter(([selectionIntent, state]) =>\n state.tracks.some(({ id }) => id === selectionIntent.trackId),\n ),\n map(([selectionIntent, state]) => ({\n selectionIntent,\n state,\n isReselectionWhileLoading:\n state.currentTrack?.id === selectionIntent.trackId && state.isLoading,\n })),\n tap(({ selectionIntent, state, isReselectionWhileLoading }) => {\n if (selectionIntent.options.navigate !== false) {\n this.reader.navigation.navigate({\n spineItem: selectionIntent.trackId,\n animation: `turn`,\n })\n }\n\n if (\n isReselectionWhileLoading &&\n selectionIntent.options.play !== undefined\n ) {\n this.emitDesiredPlayback({\n shouldPlay: selectionIntent.options.play,\n trackId: state.currentTrack?.id,\n })\n }\n }),\n filter(({ isReselectionWhileLoading }) => !isReselectionWhileLoading),\n switchMap(({ selectionIntent: { trackId, options } }) =>\n this.selectTrack$({ trackId, options, playbackReset$ }),\n ),\n )\n\n const playback$ = merge(resumePlayback$, selection$).pipe(\n withLatestFrom(this.desiredPlayback$),\n switchMap(([, { shouldPlay, trackId }]) => {\n if (\n !shouldPlay ||\n !this.audio.hasSource ||\n this.state.currentTrack?.id !== trackId\n ) {\n return EMPTY\n }\n\n this.mergeCompare({ hasError: false })\n\n return this.audio.play$().pipe(\n catchError(() => {\n this.mergeCompare({ hasError: true })\n\n return EMPTY\n }),\n )\n }),\n )\n\n this.subscriptions.add(playbackReset$.subscribe())\n this.subscriptions.add(playback$.subscribe())\n\n this.subscriptions.add(\n this.pauseCommandSubject.subscribe(() => {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n }),\n )\n\n this.subscriptions.add(\n this.audio.isPlaying$.subscribe((isPlaying) => {\n this.mergeCompare({ isPlaying })\n\n if (!isPlaying) {\n this.visualizer$.stop()\n return\n }\n\n if (!this.state.currentTrack) return\n\n this.visualizer$.start(this.state.currentTrack)\n }),\n )\n\n this.subscriptions.add(\n this.audio.metrics$.subscribe(({ currentTime, duration }) => {\n this.mergeCompare({ currentTime, duration })\n }),\n )\n }\n\n get state() {\n return this.value\n }\n\n get visualizer() {\n return this.visualizer$.value\n }\n\n private resetTrackSelection({\n currentTrack,\n isLoading,\n }: {\n currentTrack: AudioTrack\n isLoading: boolean\n }) {\n this.mergeCompare({\n currentTrack,\n isLoading,\n isPlaying: false,\n hasError: false,\n currentTime: 0,\n duration: undefined,\n })\n this.visualizer$.reset(currentTrack?.id)\n }\n\n private emitDesiredPlayback({ shouldPlay, trackId }: DesiredPlayback) {\n this.desiredPlayback$.next({\n shouldPlay,\n trackId: shouldPlay ? trackId : undefined,\n })\n\n if (!shouldPlay) {\n this.audio.pause()\n }\n }\n\n private unmountCurrentSource() {\n if (!this.audio.hasSource) return\n\n const trackId = this.state.currentTrack?.id\n\n if (!this.audio.paused) {\n this.audio.pause()\n }\n\n this.audio.unloadSource()\n\n if (trackId) {\n this.resourcesResolver.releaseTrackSource(trackId)\n }\n }\n\n private mountSource(source: string) {\n this.unmountCurrentSource()\n this.audio.loadSource(source)\n }\n\n private resolveTrackSource(track: AudioTrack) {\n const spineItem = this.reader.spineItemsManager.get(track.index)\n\n if (!spineItem) return EMPTY\n\n return this.resourcesResolver.getTrackResourceUrl$(\n track,\n spineItem.resourcesHandler,\n )\n }\n\n private selectTrack$({\n trackId,\n options,\n playbackReset$,\n }: {\n trackId: string\n options: SelectAudioTrackOptions\n playbackReset$: Observable<AudioTrack[]>\n }) {\n const track = this.state.tracks.find(({ id }) => id === trackId)\n\n if (!track) return EMPTY\n\n const currentTrack = this.state.currentTrack\n const shouldPlay =\n options.play ?? (!this.audio.paused && currentTrack !== undefined)\n\n this.emitDesiredPlayback({ shouldPlay, trackId: track.id })\n\n if (currentTrack?.id === track.id && this.audio.hasSource) {\n this.visualizer$.setTrack(track.id)\n\n return shouldPlay ? of(undefined) : EMPTY\n }\n\n this.unmountCurrentSource()\n\n this.resetTrackSelection({\n currentTrack: track,\n isLoading: true,\n })\n\n return this.resolveTrackSource(track).pipe(\n map((source) => ({ source })),\n defaultIfEmpty({ source: undefined }),\n catchError(() => of({ source: undefined })),\n takeUntil(playbackReset$),\n tap(({ source }) => {\n if (!source) {\n this.emitDesiredPlayback({ shouldPlay: false, trackId: undefined })\n } else {\n this.mountSource(source)\n }\n\n this.mergeCompare({ isLoading: false })\n }),\n filter(({ source }) => source !== undefined),\n map(() => undefined),\n )\n }\n\n select(trackId: string, options: SelectAudioTrackOptions = {}) {\n this.selectCommandSubject.next({ trackId, options })\n }\n\n play() {\n this.playCommandSubject.next()\n }\n\n pause() {\n this.pauseCommandSubject.next()\n }\n\n toggle() {\n if (this.audio.paused) {\n this.play()\n return\n }\n\n this.pause()\n }\n\n setCurrentTime(value: number) {\n this.mergeCompare({ currentTime: value })\n this.audio.setCurrentTime(value)\n }\n\n destroy() {\n this.subscriptions.unsubscribe()\n this.playCommandSubject.complete()\n this.pauseCommandSubject.complete()\n this.selectCommandSubject.complete()\n this.desiredPlayback$.complete()\n this.visualizer$.destroy()\n this.unmountCurrentSource()\n this.resourcesResolver.destroy()\n\n super.destroy()\n }\n}\n","import { DocumentRenderer, setStylePropertyIfChanged } from \"@prose-reader/core\"\nimport { EMPTY, of } from \"rxjs\"\n\nexport class AudioRenderer extends DocumentRenderer {\n onCreateDocument() {\n const ownerDocument = this.containerElement.ownerDocument\n const rootElement = ownerDocument.createElement(`div`)\n\n rootElement.style.cssText = `\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n display: block;\n `\n rootElement.setAttribute(`data-prose-reader-audio-page`, this.item.id)\n\n this.setDocumentContainer(rootElement)\n\n return of(rootElement)\n }\n\n onLoadDocument() {\n this.attach()\n\n return EMPTY\n }\n\n onUnload() {\n this.detach()\n }\n\n onLayout() {\n const { width, height } = this.viewport.pageSize\n const rootElement = this.documentContainer\n\n if (rootElement) {\n setStylePropertyIfChanged(rootElement.style, `width`, `${width}px`)\n setStylePropertyIfChanged(rootElement.style, `height`, `${height}px`)\n }\n\n return of({ width, height })\n }\n\n onRenderHeadless() {\n return EMPTY\n }\n\n getDocumentFrame() {\n return undefined\n }\n\n /**\n * Audio spine items are always pre-paginated (one track per page).\n * Mixed audio/text books with reflowable audio chapters may need\n * to revisit this if that use case arises.\n */\n get renditionLayout(): `pre-paginated` {\n return `pre-paginated`\n }\n}\n","import type { CreateReaderOptions, Reader } from \"@prose-reader/core\"\nimport { AudioController } from \"./playback/AudioController\"\nimport { AudioRenderer } from \"./renderer/AudioRenderer\"\nimport type { AudioEnhancerAPI } from \"./types\"\nimport { isAudioSpineItem } from \"./utils\"\n\nexport const audioEnhancer =\n <InheritOptions extends CreateReaderOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & AudioEnhancerAPI => {\n const readerOptions = { ...options }\n\n const reader = next({\n ...readerOptions,\n getRenderer(item) {\n const maybeFactory = options.getRenderer?.(item)\n\n if (maybeFactory) {\n return maybeFactory\n }\n\n if (isAudioSpineItem(item)) {\n return (params) => new AudioRenderer(params)\n }\n\n return undefined\n },\n })\n\n const controller = new AudioController(reader)\n\n const destroy = () => {\n controller.destroy()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_AUDIO: true,\n destroy,\n audio: {\n state$: controller.state$,\n visualizer$: controller.visualizer$,\n visibleTrackIds$: controller.visibleTrackIds$,\n get state() {\n return controller.state\n },\n get visualizer() {\n return controller.visualizer\n },\n isAudioRenderer: (renderer) => renderer instanceof AudioRenderer,\n play: () => controller.play(),\n pause: () => controller.pause(),\n toggle: () => controller.toggle(),\n setCurrentTime: (value) => controller.setCurrentTime(value),\n select: (trackId, options) => controller.select(trackId, options),\n },\n }\n }\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { Manifest } from '@prose-reader/shared';
3
3
  import { Observable } from 'rxjs';
4
4
  export type AudioControllerReader = {
5
5
  context: {
6
- manifest$: Observable<Manifest>;
6
+ manifest: Manifest;
7
7
  };
8
8
  pagination: {
9
9
  state$: Observable<PaginationInfo>;
@@ -2,7 +2,7 @@ import { DocumentRenderer } from '@prose-reader/core';
2
2
  export declare class AudioRenderer extends DocumentRenderer {
3
3
  onCreateDocument(): import('rxjs').Observable<HTMLDivElement>;
4
4
  onLoadDocument(): import('rxjs').Observable<never>;
5
- onUnload(): import('rxjs').Observable<never>;
5
+ onUnload(): void;
6
6
  onLayout(): import('rxjs').Observable<{
7
7
  width: number;
8
8
  height: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prose-reader/enhancer-audio",
3
- "version": "1.324.0",
3
+ "version": "1.325.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -20,10 +20,10 @@
20
20
  "test": "vitest run --coverage"
21
21
  },
22
22
  "dependencies": {
23
- "@prose-reader/core": "^1.324.0"
23
+ "@prose-reader/core": "^1.325.0"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "rxjs": "*"
27
27
  },
28
- "gitHead": "70a490808c1db75819d239ccc6ef7933a1156a4f"
28
+ "gitHead": "b450baa3343e03a2d6adb584124325fe9f54ca68"
29
29
  }