/*! For license information please see icecast-metadata-stats-0.1.11.min.js.LICENSE.txt */ /*! * Copyright 2021-2023 Ethan Halsall * https://github.com/eshaz/icecast-metadata-js * * This file is part of icecast-metadata-stats. * * icecast-metadata-stats free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * icecast-metadata-stats distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ var IcecastMetadataStats;(()=>{"use strict";var t={d:(s,e)=>{for(var i in e)t.o(e,i)&&!t.o(s,i)&&Object.defineProperty(s,i,{enumerable:!0,get:e[i]})},o:(t,s)=>Object.prototype.hasOwnProperty.call(t,s)},s={};(()=>{t.d(s,{default:()=>z});class e{constructor(){this.t=0,this.i=0,this.h=0,this.l=0,this.u=0,this.m=0,this.p=0}get stats(){return{totalBytesRead:this.t,streamBytesRead:this.i,metadataLengthBytesRead:this.h,metadataBytesRead:this.l,currentBytesRemaining:this.u,currentStreamBytesRemaining:this.m,currentMetadataBytesRemaining:this.p}}set currentStreamBytesRemaining(t){this.m+=t}set currentMetadataBytesRemaining(t){this.p=t}addBytes(t){this.t+=t,this.u-=t}addStreamBytes(t){this.i+=t,this.m-=t}addMetadataLengthBytes(t){this.h+=t}addMetadataBytes(t){this.l+=t,this.p-=t}addCurrentBytesRemaining(t){this.u+=t}}const i=()=>{};class a{constructor(t){this.S=0,this.I=0,this.M=new Uint8Array(0),this._=[],this.A=0,this.T=new e,this.C=t.onStream||i,this.R=t.onMetadata||i,this.B=t.onMetadataFailed||i,this.v=t.onError||i,this.$=t.enableLogging||!1,this.D=Promise.resolve(),this.L=Promise.resolve(),this.N=this.O(),this.N.next()}*O(){for(this.S=1/0;;)this.P(yield*this.j()),yield*this.Y()}static k(...t){const s=t.reduce(((t,s)=>t+s.length),0);return this.U(t,s)}static U(t,s){const e=new Uint8Array(s);return t.reduce(((t,s)=>(e.set(s,t),t+s.length)),0),e}*iterator(t){for(let s=this.N.next(t);s.value;s=this.N.next())yield s.value}readAll(t){for(let s=this.N.next(t);s.value;s=this.N.next());}async*asyncIterator(t){for(let s=this.N.next(t);s.value;s=this.N.next())await this.D,await this.L,yield s.value}async asyncReadAll(t){for(let s=this.N.next(t);s.value;s=this.N.next())await this.D,await this.L}V(...t){this.$&&console.warn("icecast-metadata-js",t.reduce(((t,s)=>t+"\n "+s),"")),this.v(...t)}P(t){this._.push(t),this.A+=t.length}*Y(){if(this._.length){const t=a.U(this._,this.A);this._=[],this.A=0,this.T.addStreamBytes(t.length);const s={stream:t,stats:this.T.stats};this.D=this.C(s),yield s}}*F(t){yield*this.Y();const s={metadata:t,stats:this.T.stats};this.L=this.R(s),yield s}*j(t=0){for(this.I===this.M.length&&(this.M=yield*this.G(),this.I=0);this.M.length-this.I[^\0]+?)='(?[^\0]*?)(;$|';|'$|$)/,e={};for(const i of t.match(new RegExp(s,"g"))||[]){const t=i.match(s);t&&(e[t.groups.key]=t.groups.val)}return e}get icyMetaInt(){return this.q}*W(){if(this.q>0)return!0;if(!this.J)return!1;this.V("Passed in Icy-MetaInt is invalid. Attempting to detect ICY Metadata.","See https://github.com/eshaz/icecast-metadata-js for information on how to properly request ICY Metadata.");const t=[null,83,116,114,101,97,109,84,105,116,108,101,61],s=Date.now();let e=0;for(;s+this.J>Date.now();){this.M=a.k(this.M,yield*this.G());t:for(;e65307)return this.V("This stream is not an Ogg stream. No Ogg metadata will be returned.","See https://github.com/eshaz/icecast-metadata-js for information on Ogg metadata."),this.B("ogg"),!1;const s=yield*this.j(27),e=yield*this.j(s[26]);return this.S=e.reduce(((t,s)=>t+s),0),!0}*rt(){const t=yield*this.j(8);return yield*this.X(),this.ht(/\x7fFLAC/,t.subarray(0,5))?{regex:/^[\x84|\x04]/,length:4}:this.ht(/OpusHead/,t.subarray(0,8))?{regex:/OpusTags/,length:8}:this.ht(/\x01vorbis/,t.subarray(0,7))?{regex:/\x03vorbis/,length:7}:void 0}*tt({regex:t,length:s}){this.ht(t,yield*this.j(s))&&(yield*this.F(yield*this.ot()))}*X(){for(;this.S;)yield*this.j()}*j(t){const s=yield*super.j(t);return this.P(s),s}*G(){const t=yield*super.G();return this.T.currentStreamBytesRemaining=t.length,t}*ot(){const t=this.nt(yield*this.j(4));this.T.addMetadataBytes(4);const s=this.H.decode(yield*this.j(t));this.T.addMetadataBytes(t);const e=this.nt(yield*this.j(4));this.T.addMetadataBytes(4);const i=[];for(let t=0;t{const e=s.indexOf(61),i=String.fromCharCode(...s.subarray(0,e)).toUpperCase(),a=this.H.decode(s.subarray(e+1));return t[i]=t[i]?`${t[i]}; ${a}`:a,t}),{VENDOR_STRING:s})}}class h{constructor(t){const{onStream:s,...e}=t;this.ct=new n(t),this.lt=new r(e)}get icyMetaInt(){return this.lt.icyMetaInt}*iterator(t){for(const s of this.lt.iterator(t))s.stream?yield*this.ct.iterator(s.stream):yield s}readAll(t){for(const s of this.lt.iterator(t))s.stream&&this.ct.readAll(s.stream)}async*asyncIterator(t){for await(const s of this.lt.asyncIterator(t))if(s.stream)for await(const t of this.ct.asyncIterator(s.stream))yield t;else yield s}async asyncReadAll(t){for await(const s of this.lt.iterator(t))s.stream&&await this.ct.asyncReadAll(s.stream)}}class o{constructor({metadataTypes:t=["icy"],...s}={}){const e=t.includes("icy"),i=t.includes("ogg");this.yt=e&&i?new h(s):i?new n(s):e?new r(s):new a(s)}static parseIcyMetadata(t){return r.parseIcyMetadata(t)}get icyMetaInt(){return this.yt.icyMetaInt}*iterator(t){yield*this.yt.iterator(t)}readAll(t){this.yt.readAll(t)}async*asyncIterator(t){return yield*this.yt.asyncIterator(t)}async asyncReadAll(t){return this.yt.asyncReadAll(t)}}const c=()=>{};class l{constructor(t,{onStream:s=c,...e}){let i;this.dt=new ReadableStream({async start(a){i=new o({icyMetaInt:parseInt(t.headers.get("Icy-MetaInt")),...e,onStream:async t=>(a.enqueue(t.stream),s(t))});for await(const s of l.asyncIterator(t.body))await i.asyncReadAll(s);a.close()}}),this.ut=i}get icyMetaInt(){return this.ut.icyMetaInt}get readableStream(){return this.dt}async startReading(){try{for await(const t of l.asyncIterator(this.dt));}catch(t){if("AbortError"!==t.name)throw t}}static asyncIterator(t){const s=t.getReader();return{[Symbol.asyncIterator]:()=>({next:()=>s.read()})}}}const y=()=>{},d="stopped",u="fetching",m=new WeakMap,g=Symbol(),f=Symbol(),b=Symbol(),p=Symbol(),S=Symbol(),w=Symbol(),I=Symbol(),M=Symbol(),x=Symbol(),_=Symbol(),A=Symbol(),T=Symbol(),C=Symbol(),R=Symbol(),B=Symbol(),v=Symbol(),E=Symbol(),$=Symbol(),D=Symbol(),L=Symbol(),N=Symbol(),O=Symbol(),P=Symbol(),j=Symbol(),Y=Symbol(),k=Symbol(),U=Symbol(),V=Symbol(),F=t=>{let s=parseInt(t);return isNaN(s)&&(s=null),s};class z{constructor(t,s={}){const e=t.split("/").slice(0,-1).join("/");m.set(this,{[E]:t,[S]:s.icestatsEndpoint||`${e}/status-json.xsl`,[M]:s.statsEndpoint||`${e}/stats`,[A]:s.nextsongsEndpoint||`${e}/nextsongs`,[R]:s.sevenhtmlEndpoint||`${e}/7.html`,[N]:s.sources||[],[O]:1e3*(s.interval||30),[P]:s.onStats||y,[j]:s.onStatsFetch||y,[$]:s.icyMetaInt,[D]:s.icyCharacterEncoding,[L]:s.icyDetectionTimeout,[g]:new AbortController,[b]:new AbortController,[w]:new AbortController,[x]:new AbortController,[T]:new AbortController,[B]:new AbortController,[Y]:d})}static xml2Json(t){const s=t=>{if(!t.children.length)return Number.isNaN(Number(t.innerHTML))?t.innerHTML:Number(t.innerHTML);const e={};for(const i of t.children)i.nodeName in e?Array.isArray(e[i.nodeName])?e[i.nodeName].push(s(i)):e[i.nodeName]=[e[i.nodeName],s(i)]:e[i.nodeName]=s(i);return e};return s((t=>(new DOMParser).parseFromString(t,"application/xml"))(t))}get state(){return m.get(this)[Y]}get icestatsEndpoint(){return m.get(this)[S]}get statsEndpoint(){return m.get(this)[M]}get nextsongsEndpoint(){return m.get(this)[A]}get sevenhtmlEndpoint(){return m.get(this)[R]}start(){m.get(this)[Y]===d&&(m.get(this)[Y]="running",this.fetch().then(m.get(this)[P]),m.get(this)[k]=setInterval((()=>{this.fetch().then(m.get(this)[P])}),m.get(this)[O]))}stop(){m.get(this)[Y]!==d&&(m.get(this)[Y]=d,clearInterval(m.get(this)[k]),m.get(this)[g].abort(),m.get(this)[b].abort(),m.get(this)[w].abort(),m.get(this)[x].abort(),m.get(this)[B].abort())}async fetch(){if(m.get(this)[Y]!==u){const t=m.get(this)[Y];m.get(this)[Y]=u,m.get(this)[j](m.get(this)[N]);const s=[];m.get(this)[N].includes("icestats")&&s.push(this.getIcestats()),m.get(this)[N].includes("sevenhtml")&&s.push(this.getSevenhtml()),m.get(this)[N].includes("stats")&&s.push(this.getStats()),m.get(this)[N].includes("nextsongs")&&s.push(this.getNextsongs()),m.get(this)[N].includes("icy")&&s.push(this.getIcyMetadata()),m.get(this)[N].includes("ogg")&&s.push(this.getOggMetadata());const e=await Promise.all(s).then((t=>t.reduce(((t,s)=>({...t,...s})),{})));return m.get(this)[Y]=m.get(this)[Y]!==u?m.get(this)[Y]:t,e}}async getIcestats(){return this[U]({status:I,endpoint:S,controller:w,mapper:t=>t.json()}).then((t=>({icestats:t&&t.icestats})))}async getSevenhtml(){return this[U]({status:v,endpoint:R,controller:B,mapper:async t=>(await t.text()).match(/(.*?)<\/body>/gi).map((t=>{const s=t.match(/(|,)(?.*)<\/body>/i).groups.stats.split(",");return 7===s.length?{StreamTitle:s[6],currentListeners:F(s[4]),peakListeners:F(s[2]),maxListeners:F(s[3]),bitrate:F(s[5]),status:F(s[1]),serverListeners:F(s[0])}:{StreamTitle:s[4],currentListeners:F(s[2]),peakListeners:F(s[0]),maxListeners:F(s[1]),bitrate:F(s[3])}}))}).then((t=>({sevenhtml:t})))}async getStats(){return this[U]({status:_,endpoint:M,controller:x,mapper:async t=>z.xml2Json(await t.text()).SHOUTCASTSERVER.STREAMSTATS}).then((t=>({stats:t})))}async getNextsongs(){return this[U]({status:C,endpoint:A,controller:T,mapper:async t=>z.xml2Json(await t.text()).SHOUTCASTSERVER.NEXTSONGS}).then((t=>({nextsongs:t})))}async getIcyMetadata(){return this[V]({status:f,endpoint:E,controller:g,metadataType:"icy",headers:{"Icy-MetaData":1}})}async getOggMetadata(){return this[V]({status:p,endpoint:E,controller:b,metadataType:"ogg"})}async[V]({status:t,endpoint:s,controller:e,headers:i,metadataType:a}){return this[U]({status:t,endpoint:s,controller:e,headers:i,mapper:async t=>new Promise((s=>{new l(t,{onMetadata:({metadata:t})=>{m.get(this)[e].abort(),s(t)},onMetadataFailed:()=>{m.get(this)[e].abort(),s()},metadataTypes:a,icyMetaInt:m.get(this)[$],icyCharacterEncoding:m.get(this)[D],icyDetectionTimeout:m.get(this)[L]}).startReading()}))}).then((t=>({[a]:t})))}async[U]({status:t,endpoint:s,controller:e,mapper:i,headers:a={}}){if(!m.get(this)[t])return m.get(this)[t]=!0,fetch(m.get(this)[s],{method:"GET",headers:a,signal:m.get(this)[e].signal}).then((t=>{if(!t.ok)throw new Error(`HTTP Error ${t.status}`);return t})).then(i).catch((t=>{"AbortError"!==t.name&&console.warn(`Failed to fetch ${m.get(this)[s]}`,t)})).finally((()=>{m.get(this)[t]=!1,m.get(this)[e]=new AbortController}))}}})(),IcecastMetadataStats=s.default})(); //# sourceMappingURL=icecast-metadata-stats-0.1.11.min.js.map