import"./modulepreload-polyfill-B5Qt9EMX.js";import{P as $e,a as y,m as Ae,c as oe,e as n,l as Y,i as B}from"./debug-B-FoNBZ5.js";import{R as ke,c as Me,a as _e,s as xe}from"./playGate-BFH7IScH.js";const N=document.getElementById("app");function Ne(){const e=localStorage.getItem("oradio.mode");return e==="solo"||e==="synced"?e:e==="linked"?(localStorage.setItem("oradio.mode","synced"),localStorage.setItem("oradio.syncedAudio","1"),"synced"):e==="remote"||e==="follow-room"?(localStorage.setItem("oradio.mode","synced"),localStorage.setItem("oradio.syncedAudio","0"),"synced"):(e==="play-here"&&localStorage.setItem("oradio.mode","solo"),"solo")}function Pe(){const e=localStorage.getItem("oradio.syncedAudio");return e!=="0"}function pe(){const e=Number(localStorage.getItem("oradio.localVolume"));return Number.isFinite(e)&&e>=0&&e<=1?e:.7}function Z(){try{const e=localStorage.getItem("oradio.lastStation");if(!e)return null;const o=JSON.parse(e);if(o&&Number.isFinite(o.id))return o}catch{}return null}function V(e,{playing:o=!0}={}){if(!(!e||!Number.isFinite(e.id)))try{localStorage.setItem("oradio.lastStation",JSON.stringify({id:e.id,name:e.name,genres:e.genres||[],playing:!!o}))}catch{}}const t={user:null,tab:"favorites",stations:[],categories:[],selectedCategory:null,favorites:[],history:[],query:"",sort:"hot",randomMode:localStorage.getItem("oradio.randomMode")==="favorites"?"favorites":"all",rooms:[],roomSlug:localStorage.getItem("oradio.room")||null,mode:Ne(),syncedAudio:Pe(),roomState:null,roomPeers:[],roomDevices:{list:[],current:null},player:{stationId:null,stationName:null,genres:[],playing:!1,loading:!1,volume:pe(),votes:null},sync:{status:"off",error:0,rtt:null,bufferMs:8e3,delay:0},pendingCommand:null,session:null};let A=null;function x(e){t.pendingCommand=e,A&&(clearTimeout(A),A=null),e&&(A=setTimeout(()=>{t.pendingCommand=null,A=null,l()},2e3))}function me(){t.pendingCommand==null&&A==null||(t.pendingCommand=null,A&&(clearTimeout(A),A=null))}const D=new ke,u=new $e({onState:e=>{if(t.player={...t.player,...e},typeof e.volume=="number")try{localStorage.setItem("oradio.localVolume",String(e.volume))}catch{}ye()}});let j=!1;function ye(){j||(j=!0,requestAnimationFrame(()=>{j=!1,l()}))}let q=0;function le(e){return e instanceof HTMLInputElement&&e.type==="range"}if(typeof window<"u"){document.addEventListener("pointerdown",o=>{le(o.target)&&q++},!0);const e=o=>{le(o.target)&&q>0&&(q--,ye())};document.addEventListener("pointerup",e,!0),document.addEventListener("pointercancel",e,!0)}let J=null,K=null;function Le(e){K=e,!J&&(J=setTimeout(()=>{J=null;const o=K;K=null,b==null||b.send({type:"command",action:"volume",value:o})},80))}let z=null,X=null;function Te(e){X=e,!z&&(z=setTimeout(()=>{z=null;const o=X;X=null,b==null||b.send({type:"command",action:"setSyncBuffer",value:o})},150))}function T(e){const o=!!(b!=null&&b.send({type:"command",...e}));return o||(me(),t.player={...t.player,error:"Disconnected β€” reconnecting…"},l()),o}u.setLocalVolume(pe());u.onSyncChange=e=>{t.sync={status:e.status,error:e.error,rtt:e.clockRtt,bufferMs:e.bufferMs,delay:e.delay},l()};let b,R=!1,Q=!1;function ee(){R=!0,Q=!1}async function fe(e,o){if(R)return!0;if(_e())return R=!0,!0;if(Q)return!1;try{return await xe({stationName:e||"Radio",subtitle:o||"Tap Start to enable audio.",onStart:()=>{R=!0}}),R}catch{return Q=!0,!1}}let E=null;function ne(){if(E)try{E.abort()}catch{}return E=typeof AbortController<"u"?new AbortController:null,E}async function ge(){try{t.user=await y.get("/api/auth/me")}catch{Ve();return}await ae();try{t.rooms=await y.get("/api/rooms"),(!t.roomSlug||!t.rooms.find(e=>e.slug===t.roomSlug))&&(t.roomSlug=t.rooms[0]&&t.rooms[0].slug||`u-${t.user.id}`,localStorage.setItem("oradio.room",t.roomSlug))}catch{t.roomSlug=t.roomSlug||`u-${t.user.id}`}if(W(),Ae({player:u,clock:D,getWs:()=>b,role:"kiosk"}),l(),Ye(),t.mode==="solo"){const e=Z();if(e&&Number.isFinite(e.id))try{const o=t.favorites.find(s=>s.id===e.id)||t.stations.find(s=>s.id===e.id)||await y.get(`/api/stations/${e.id}`).catch(()=>null);o&&(e.playing!==!1?await Re(o):(t.player={...t.player,stationId:o.id,stationName:o.name,genres:o.genres||[],playing:!1,loading:!1,error:null},l()))}catch{}}}async function Re(e){const o=ne();if(t.player={...t.player,stationId:e.id,stationName:e.name,genres:e.genres||[],playing:!1,loading:!0,error:null},l(),!await fe(e.name,"Tap Start to resume your last station.")){t.player.loading=!1,t.player.playing=!1,l();return}await u.play(e),u.audio.paused&&u.audio.play().catch(()=>{}),se(e.id);try{const a=await y.post(`/api/stations/${e.id}/play`,null,o?{signal:o.signal}:void 0);t.player.stationId===e.id&&(t.player.votes=a,a.sessionId&&(t.session={id:a.sessionId,stationId:e.id,startedAt:Date.now()}),P(e.id,a),l())}catch(a){B(a)}}function W(){if(b)try{b.close()}catch{}D.detach();const e=t.mode==="synced"&&!t.syncedAudio?"panel":"controller";Y("openWs",{mode:t.mode,syncedAudio:t.syncedAudio,kind:e,room:t.roomSlug}),b=Me(Be,{room:t.roomSlug,kind:e}),D.attachWs(b)}function De(e){if(e!=="solo"&&e!=="synced"||t.mode===e)return;const o=t.mode;t.mode=e,localStorage.setItem("oradio.mode",e),e==="synced"&&!t.syncedAudio&&t.player.stationId&&(u.stop(),M()),U();const s=o==="synced"&&!t.syncedAudio?"panel":"controller",a=e==="synced"&&!t.syncedAudio?"panel":"controller";s!==a&&W(),e==="synced"&&O(),l()}function Fe(e){if(e=!!e,t.syncedAudio===e)return;const o=t.mode==="synced"&&!t.syncedAudio?"panel":"controller";t.syncedAudio=e;try{localStorage.setItem("oradio.syncedAudio",e?"1":"0")}catch{}t.mode==="synced"&&(!e&&t.player.stationId&&(u.stop(),M()),U(),o!==(e?"controller":"panel")&&W(),e&&O()),l()}function O(){var s;const e=t.roomState;if(!e||!e.station_id||!e.playing||t.mode!=="synced")return;if(!t.syncedAudio){te(),l();return}if(((s=u.station)==null?void 0:s.id)===e.station_id&&!u.audio.paused)return;const o=t.stations.find(a=>a.id===e.station_id)||t.favorites.find(a=>a.id===e.station_id)||(e.station&&e.station.id===e.station_id?e.station:null);o&&ve(o)}async function ve(e){if(t.player.loading&&t.player.stationId===e.id)return;const o=ne();if(t.player.votes=null,M(),V(e),t.player.stationId=e.id,t.player.stationName=e.name,t.player.genres=e.genres||[],t.player.loading=!0,l(),!await fe(e.name,"Tap Start to join the group audio.")){t.player.loading=!1,l();return}await u.play(e),u.audio.paused&&u.audio.play().catch(()=>{}),se(e.id);try{const a=await y.post(`/api/stations/${e.id}/play`,null,o?{signal:o.signal}:void 0);t.player.stationId===e.id&&(t.player.votes=a,a.sessionId&&(t.session={id:a.sessionId,stationId:e.id,startedAt:Date.now()}),P(e.id,a),l())}catch(a){B(a)}}function U(){const e=t.roomState;t.mode==="synced"&&t.syncedAudio?u.enableSync({clock:D,startedAt:(e==null?void 0:e.started_at)||null}):u.disableSync()}function Ee(e){!e||t.roomSlug===e||(t.roomSlug=e,localStorage.setItem("oradio.room",e),t.roomPeers=[],t.roomState=null,W(),l())}async function ae(){const[e,o,s,a]=await Promise.all([y.get(`/api/stations?sort=${encodeURIComponent(t.sort)}`),y.get("/api/me/favorites").catch(()=>[]),y.get("/api/me/history").catch(()=>[]),y.get("/api/v1/categories").catch(()=>[])]);t.stations=e,t.favorites=o,t.history=s,t.categories=a}async function qe(){t.stations=await y.get(`/api/stations?sort=${encodeURIComponent(t.sort)}`)}function Be(e){if(!(!e||!e.type)){if(e.type==="clock-pong"){D.handlePong(e);return}switch(e.type){case"hello":if(t.roomState=e.state||null,t.roomPeers=e.peers||[],Y("hello",{mode:t.mode,syncedAudio:t.syncedAudio,roomState:t.roomState}),t.mode==="synced"&&te(),U(),e.last_sync_pos&&t.mode==="synced"&&t.syncedAudio)try{u.acceptMasterPos(e.last_sync_pos)}catch{}t.mode==="synced"&&O(),l();return;case"sync-pos":if(t.mode==="synced"&&t.syncedAudio)try{u.acceptMasterPos(e)}catch{}return;case"presence":t.roomPeers=e.peers||[],l();return;case"devices":t.roomDevices={list:e.list||[],current:e.current||null},l();return;case"state":if(t.roomState={...t.roomState,...e},Y("state",e),(e.started_at||e.playing===!1||!e.station_id)&&me(),t.mode==="synced"&&te(),t.mode==="synced"&&t.syncedAudio&&e.started_at&&(u.updateSyncTarget(e.started_at),U()),l(),t.mode==="synced"&&O(),t.mode==="synced"&&t.syncedAudio){const o=t.roomState;o&&o.playing===!1&&!u.audio.paused&&u.togglePause(),o&&o.playing&&o.station_id&&o.station_id===t.player.stationId&&u.audio.paused&&u.togglePause(),o&&!o.station_id&&t.player.stationId&&(u.stop(),t.player={...t.player,stationId:null,stationName:null,genres:[],playing:!1,loading:!1},l())}return;case"vote":{const o=e.stationId,s=e.stats||{};for(const a of[t.stations,t.favorites]){const i=a.find(c=>c.id===o);i&&("up"in s&&(i.up=s.up),"down"in s&&(i.down=s.down),"score"in s&&(i.score=s.score))}t.player.votes&&t.player.stationId===o&&(t.player.votes={...t.player.votes,...s}),l();return}case"plays":{const o=e.stationId;for(const s of[t.stations,t.favorites]){const a=s.find(i=>i.id===o);a&&(a.plays=e.plays)}t.player.votes&&t.player.stationId===o&&(t.player.votes={...t.player.votes,plays:e.plays}),l();return}case"peerVolume":{typeof e.value=="number"&&(u.setLocalVolume(Math.max(0,Math.min(1,e.value))),l());return}case"command":{if(e.action==="setSyncBuffer"&&Number.isFinite(e.value)){u.setSyncBufferMs(e.value);return}return}default:return}}}function te(){var o,s,a,i,c,d,f;const e=t.roomState;if(e){if(t.mode==="synced"&&t.syncedAudio){t.player.stationId&&e.station_id===t.player.stationId?t.player={...t.player,stationName:((o=e.station)==null?void 0:o.name)||t.player.stationName,genres:((s=e.station)==null?void 0:s.genres)||t.player.genres||[]}:!t.player.stationId&&e.station_id&&(t.player={...t.player,stationId:e.station_id,stationName:((a=e.station)==null?void 0:a.name)||null,genres:((i=e.station)==null?void 0:i.genres)||[]});return}t.player={...t.player,stationId:e.station_id??((c=e.station)==null?void 0:c.id)??null,stationName:((d=e.station)==null?void 0:d.name)||null,genres:((f=e.station)==null?void 0:f.genres)||[],playing:!!e.playing,loading:!1,volume:typeof e.volume=="number"?e.volume:t.player.volume,error:null}}}function Ve(){oe(N);const e=n("div",{class:"login"},n("form",{onSubmit:async o=>{o.preventDefault();const s=new FormData(o.target);try{t.user=await y.post("/api/auth/login",{username:s.get("username"),password:s.get("password")}),await ge()}catch(a){o.target.querySelector(".err").textContent=a.message}}},n("h1",{},"Sign in"),n("input",{name:"username",placeholder:"Username",autocomplete:"username",required:!0}),n("input",{name:"password",type:"password",placeholder:"Password",autocomplete:"current-password",required:!0}),n("div",{class:"err"}),n("button",{type:"submit"},"Continue")));N.appendChild(e)}let $=0;function Oe(e,o,s,a){const i=e==="synced",c=e==="solo"||e==="synced"&&o,d=[];if(i){const f="Group (master) volume β€” broadcasts to the room";d.push(n("div",{class:"vol vol-master",title:f},n("span",{class:"vol-icon"},"πŸ“‘"),n("input",{type:"range",min:0,max:1,step:.05,value:s,"aria-label":f,title:f,onInput:r=>{var S;const p=Number(r.target.value);t.roomState&&(t.roomState.volume=p),Le(p);const g=(S=r.target.parentNode)==null?void 0:S.querySelector(".val");g&&(g.textContent=String(Math.round(p*100)))}}),n("span",{class:"val"},Math.round(s*100))))}if(c){const f=e==="synced"?"Local volume β€” this device only":"Local volume";d.push(n("div",{class:"vol vol-local",title:f},n("span",{class:"vol-icon"},a===0?"πŸ”‡":a<.5?"πŸ”ˆ":"πŸ”Š"),n("input",{type:"range",min:0,max:1,step:.05,value:a,"aria-label":f,title:f,onInput:r=>{var S;const p=Number(r.target.value);u.setLocalVolume(p);const g=(S=r.target.parentNode)==null?void 0:S.querySelector(".val");g&&(g.textContent=String(Math.round(p*100)))}}),n("span",{class:"val"},Math.round(a*100))))}return d}function l(){var ie,re;if(!t.user||q>0)return;const e=N.querySelector(".grid");e&&e.scrollTop>0&&($=e.scrollTop),F(),oe(N);const o=t.player,s=new Set(t.favorites.map(v=>v.id)),a=o.votes,i=t.mode,c=t.syncedAudio,d=t.roomState,f=!!(d&&d.station_id&&d.playing);let r;i==="synced"&&d?r={stationId:d.station_id??null,stationName:((ie=d.station)==null?void 0:ie.name)??o.stationName,playing:!!d.playing}:r={stationId:o.stationId,stationName:o.stationName,playing:o.playing},t.pendingCommand&&(r={...r,playing:t.pendingCommand.playing,stationId:t.pendingCommand.stationId??r.stationId});const p=i==="synced"&&c&&f&&(!o.stationId||o.stationId!==d.station_id),g=p?"Join group audio":r.playing?i==="synced"?c?"Pause here & group":"Pause group":"Pause":i==="synced"?c?"Play here & group":"Play on group":"Play",S=i==="synced"?c?"Stop here & group":"Stop group":"Stop",m=f?((re=d.station)==null?void 0:re.name)||"group audio":"",L=p?`Tap β–Ά to join β€œ${m}”`:i==="synced"?c?"Idle β€” pick a station to play here and on the group":"Idle β€” pick a station to play it on the group":"Idle";let w;o.loading?w="Connecting…":r.playing?i==="synced"&&c&&o.stationId===r.stationId&&!o.playing?w="On air β€” local audio loading…":i==="synced"&&!c?w="On air (silent observer)":w="On air":o.error?w=o.error:r.stationId?w="Paused":w=L;const be=typeof(d==null?void 0:d.volume)=="number"?d.volume:.7,Se=o.volume,we=n("section",{class:"now"},n("div",{class:"meta"},n("div",{class:"name"},r.stationName||"Select a station"),n("div",{class:"sub"},w),n("div",{class:"tags"},...(o.genres||[]).slice(0,4).map(v=>n("span",{class:"tag"},v)))),n("div",{class:"controls"},n("div",{class:"vote-group",title:"Vote on current station"},n("button",{class:`vote up ${(a==null?void 0:a.myVote)===1?"on":""}`,disabled:!r.stationId,title:"Upvote",onClick:()=>de(1)},n("span",{class:"vote-icon"},"β–²"),n("span",{class:"vote-count"},String((a==null?void 0:a.up)??0))),n("button",{class:`vote down ${(a==null?void 0:a.myVote)===-1?"on":""}`,disabled:!r.stationId,title:"Downvote",onClick:()=>de(-1)},n("span",{class:"vote-icon"},"β–Ό"),n("span",{class:"vote-count"},String((a==null?void 0:a.down)??0)))),n("button",{class:`btn-play ${o.loading?"loading":""} ${p?"cta-join":""}`,title:g,"aria-label":g,onClick:()=>{var v;if(ee(),t.mode==="synced"&&!t.syncedAudio){const h=r.stationId||((v=t.favorites[0])==null?void 0:v.id);if(!h)return;x({stationId:h,playing:!r.playing}),T({action:r.playing?"pause":"play",stationId:h}),l();return}if(p){const h=t.stations.find(I=>I.id===d.station_id)||t.favorites.find(I=>I.id===d.station_id);if(h){x({stationId:h.id,playing:!0}),ve(h);return}}if(r.stationId){const h=r.playing;if(t.mode==="synced"&&t.syncedAudio){if(x({stationId:r.stationId,playing:!h}),!T({action:h?"pause":"play",stationId:r.stationId}))return;u.togglePause()}else{u.togglePause();const I=Z();I&&I.id===r.stationId&&V({id:I.id,name:I.name,genres:I.genres},{playing:!h})}}else t.favorites[0]&&G(t.favorites[0])}},r.playing?"❚❚":"β–Ά"),n("button",{class:"btn-stop",title:S,"aria-label":S,disabled:!r.stationId,onClick:()=>{if(ee(),t.mode==="synced"&&!t.syncedAudio){x({stationId:null,playing:!1}),T({action:"stop"}),l();return}if(t.mode==="synced"&&t.syncedAudio){if(x({stationId:null,playing:!1}),!T({action:"stop"}))return;u.stop(),M();return}const v=r.stationId;u.stop(),M();const h=Z();h&&h.id===v&&V({id:h.id,name:h.name,genres:h.genres},{playing:!1})}},"β– "),...Oe(i,c,be,Se))),Ie=t.user.role==="admin",Ce=n("div",{class:"header"},n("div",{class:"tabs"},...["favorites","browse","recent"].map(v=>n("button",{class:`tab ${t.tab===v?"active":""}`,onClick:()=>{t.tab=v,$=0,l()}},v==="favorites"?"β˜… Favorites":v==="browse"?"🌐 Browse":"⏱ Recent"))),n("div",{class:"header-tools"},Ue(),t.tab==="browse"?n("select",{class:"sort",title:"Sort browse list",onChange:v=>{t.sort=v.target.value,$=0,qe().then(l)}},n("option",{value:"hot",selected:t.sort==="hot"},"πŸ”₯ Hot (smart)"),n("option",{value:"top",selected:t.sort==="top"},"β–² Top voted"),n("option",{value:"plays",selected:t.sort==="plays"},"β–Ά Most played"),n("option",{value:"controversial",selected:t.sort==="controversial"},"⚑ Controversial"),n("option",{value:"name",selected:t.sort==="name"},"A β†’ Z")):null,n("input",{class:"search",type:"search",placeholder:"Search…",value:t.query,onInput:v=>{t.query=v.target.value,He()}}),n("button",{class:"btn-random",title:`Play random station (mode: ${t.randomMode}). Right-click to switch mode.`,onClick:Ke,onContextMenu:v=>{v.preventDefault(),Je()}},n("span",{class:"rand-icon"},"🎲"),n("span",{class:"rand-mode"},t.randomMode==="favorites"?"β˜…":"All")),n("a",{class:"btn-docs",href:"/docs/",target:"_blank",rel:"noopener",title:"Open API reference"},"API"),Ie?n("button",{class:"btn-add",title:"Add station",onClick:Xe},"+"):null)),H=n("section",{class:"lib"},Ce);t.tab==="browse"&&t.categories.length&&H.appendChild(Ge());const _=n("div",{class:"grid"});_.id="grid",_.addEventListener("scroll",()=>{$=_.scrollTop},{passive:!0}),H.appendChild(_),N.appendChild(we),N.appendChild(H),he(_,s),$&&(_.scrollTop=$,requestAnimationFrame(()=>{$&&(_.scrollTop=$)}))}function Ue(){var p,g,S;const e=t.roomPeers||[],o=e.some(m=>m.kind==="display"),s=((p=t.rooms.find(m=>m.slug===t.roomSlug))==null?void 0:p.name)||"My group",a=[{id:"solo",icon:"🎧",label:"Solo",title:"Local audio only. Controls do not affect the group."},{id:"synced",icon:"πŸ”—",label:"Synced",title:`Mirror the ${s} group's UI. Controls target the group. Local audio is optional.`}],i=t.sync||{status:"off",error:0,rtt:null,bufferMs:8e3,delay:0},c=t.mode==="synced"&&t.syncedAudio&&i.status!=="off",d=((i.bufferMs??8e3)/1e3).toFixed(1),f=(i.delay??0).toFixed(2),r={"no-anchor":{dot:"βšͺ",label:"wait",title:"Waiting for the room to start playback"},measuring:{dot:"🟑",label:"…",title:`Measuring clock (buffer ${d}s)`},"in-sync":{dot:"🟒",label:"sync",title:`In sync β€” holding ${f}s of ${d}s buffer (RTT ${((S=(g=i.rtt)==null?void 0:g.toFixed)==null?void 0:S.call(g,0))??"?"} ms)`},lagging:{dot:"🟑",label:"lag",title:`Joined too late by ${i.error.toFixed(1)}s β€” increase the buffer to catch up`},"no-buffer":{dot:"⚠️",label:"cors",title:"Stream is cross-origin without CORS β€” sync buffer disabled."},off:{dot:"",label:"",title:""}}[i.status]||{dot:"",label:"",title:""};return n("div",{class:`room-pill mode-${t.mode}`,title:"Listening mode & group"},n("div",{class:"rp-group",title:o?"Group display online":"Group"},n("span",{class:"rp-group-icon"},o?"πŸ“»":"🏠"),n("select",{class:"rp-group-select",onChange:m=>Ee(m.target.value),"aria-label":"Group"},...(t.rooms.length?t.rooms:[{slug:t.roomSlug||"",name:"My group"}]).map(m=>n("option",{value:m.slug,selected:m.slug===t.roomSlug},m.name))),n("span",{class:"rp-peers",title:`${e.length} client(s) connected${o?" β€’ display online":""}`},`${e.length}`)),n("div",{class:"mode-pill",role:"radiogroup","aria-label":"Listening mode"},n("span",{class:`mode-indicator pos-${t.mode}`,"aria-hidden":"true"}),...a.map(m=>n("button",{class:`mode-seg ${t.mode===m.id?"on":""}`,role:"radio","aria-checked":t.mode===m.id?"true":"false",title:m.title,onClick:()=>De(m.id)},n("span",{class:"mode-seg-icon"},m.icon),n("span",{class:"mode-seg-label"},m.label)))),t.mode==="synced"?n("label",{class:`synced-audio-toggle ${t.syncedAudio?"on":"off"}`,title:t.syncedAudio?"Local synced audio ON β€” this device plays the group stream (aligned to the room).":"Local synced audio OFF β€” silent observer of the group."},n("input",{type:"checkbox",checked:t.syncedAudio,onChange:m=>Fe(m.target.checked),"aria-label":"Local synced audio"}),n("span",{class:"sa-icon"},t.syncedAudio?"πŸ”Š":"πŸ”‡"),n("span",{class:"sa-label"},t.syncedAudio?"Audio":"Silent")):null,c?n("span",{class:`sync-chip sync-${i.status}`,title:r.title},n("span",{class:"sync-dot"},r.dot),n("span",{class:"sync-label"},r.label),n("input",{class:"sync-buffer",type:"range",min:"2",max:"20",step:"0.5",value:String((i.bufferMs??8e3)/1e3),title:`Sync buffer: ${d}s. Bigger = easier for slow devices to align (more startup lag).`,"aria-label":"Sync buffer seconds",onInput:m=>{const L=Number(m.target.value);if(!Number.isFinite(L))return;const w=L*1e3;u.setSyncBufferMs(w),t.mode==="synced"&&Te(w),m.target.title=`Sync buffer: ${L.toFixed(1)}s. Bigger = easier for slow devices to align (more startup lag).`}})):null)}function Ge(){return n("div",{class:"chips"},n("button",{class:`chip ${t.selectedCategory?"":"active"}`,onClick:()=>{t.selectedCategory=null,$=0,l()}},`All (${t.stations.length})`),...t.categories.filter(e=>e.count>0).map(e=>n("button",{class:`chip ${t.selectedCategory===e.id?"active":""}`,onClick:()=>{t.selectedCategory=e.id,$=0,l()}},`${e.icon||""} ${e.label} (${e.count})`.trim())))}function We(){let e=[];if(t.tab==="favorites")e=t.favorites;else if(t.tab==="browse")e=t.stations,t.selectedCategory&&(e=e.filter(s=>s.category===t.selectedCategory));else if(t.tab==="recent"){const s=new Set;e=t.history.filter(a=>!s.has(a.station_id)&&s.add(a.station_id)).map(a=>t.stations.find(i=>i.id===a.station_id)).filter(Boolean)}const o=t.query.trim().toLowerCase();return o&&(e=e.filter(s=>s.name.toLowerCase().includes(o)||(s.country||"").toLowerCase().includes(o)||(s.genres||[]).some(a=>a.toLowerCase().includes(o)))),e}function He(){const e=document.getElementById("grid");if(!e)return;const o=new Set(t.favorites.map(s=>s.id));he(e,o)}function he(e,o){oe(e);const s=We();if(!s.length){e.appendChild(n("div",{class:"empty"},t.tab==="favorites"?"No favorites yet β€” long-press or tap β˜… on a station.":t.query?"No matches.":"Nothing here yet."));return}const a=t.mode==="synced"&&t.roomState?t.roomState.station_id??null:t.player.stationId;for(const i of s){const c=typeof i.score=="number"?i.score:0,d=(i.up??0)-(i.down??0),f=d>0?"pos":d<0?"neg":"neu",r=n("div",{class:`card ${a===i.id?"playing":""}`,role:"button",tabindex:0,onClick:()=>G(i),onContextMenu:p=>{p.preventDefault(),ce(p.clientX,p.clientY,i)}},n("div",{class:"art"},i.image_display_url||i.image_url?n("img",{class:"art-img",src:i.image_display_url||i.image_url,alt:"",loading:"lazy",referrerpolicy:"no-referrer",onError:p=>{const g=p.target.parentNode;p.target.remove(),g&&g.appendChild(n("span",{class:"art-glyph"},"β™ͺ"))}}):n("span",{class:"art-glyph"},"β™ͺ")),n("div",{class:"card-body"},n("div",{class:"n"},i.name),n("div",{class:"g"},(i.genres||[]).slice(0,3).join(" Β· ")||i.country||"β€”")),n("div",{class:`score-badge ${f}`,title:`β–²${i.up??0} Β· β–Ό${i.down??0} Β· β–Ά${i.plays??0} Β· score ${c.toFixed(2)}`},d>0?`+${d}`:String(d)),n("button",{class:`fav ${o.has(i.id)?"on":""}`,title:o.has(i.id)?"Remove favorite":"Add favorite",onClick:p=>{p.stopPropagation(),je(i)}},o.has(i.id)?"β˜…":"β˜†"),n("button",{class:"more",title:"API endpoints",onClick:p=>{p.stopPropagation();const g=p.currentTarget.getBoundingClientRect();ce(g.right,g.bottom,i)}},"β‹―"));e.appendChild(r)}}async function je(e){t.favorites.some(s=>s.id===e.id)?await y.del(`/api/me/favorites/${e.id}`):await y.put(`/api/me/favorites/${e.id}`,{position:t.favorites.length}),t.favorites=await y.get("/api/me/favorites"),l()}function Je(){t.randomMode=t.randomMode==="favorites"?"all":"favorites",localStorage.setItem("oradio.randomMode",t.randomMode),k(`Random mode: ${t.randomMode==="favorites"?"favorites only":"all stations"}`),l()}async function Ke(){try{const e=t.randomMode==="favorites"?"/api/me/favorites/random":"/api/v1/stations/random",o=await y.get(e);let s=o;if(s.id==null&&(s=t.stations.find(a=>a.uuid===o.uuid)||null),!s){k("Random station not in cache");return}G(s)}catch(e){const o=t.randomMode==="favorites"?t.favorites:t.stations;if(!o.length){k(e.message||"No stations available");return}G(o[Math.floor(Math.random()*o.length)])}}function se(e){t.history.unshift({station_id:e,started_at:new Date().toISOString()})}async function G(e){ee();const o=ne();if(t.player.votes=null,M(),t.mode==="solo"&&V(e,{playing:!0}),t.mode==="synced"){if(x({stationId:e.id,playing:!0}),!T({action:"play",stationId:e.id}))return;t.player={...t.player,stationId:e.id,stationName:e.name,genres:e.genres||[],playing:!0,loading:!1,error:null},l();try{const a=await y.get(`/api/stations/${e.id}/votes`,o?{signal:o.signal}:void 0);t.player.stationId===e.id&&(t.player.votes=a,P(e.id,a),l())}catch(a){B(a)}return}u.play(e),se(e.id);try{const s=await y.post(`/api/stations/${e.id}/play`,null,o?{signal:o.signal}:void 0);t.player.stationId===e.id?(t.player.votes=s,s.sessionId&&(t.session={id:s.sessionId,stationId:e.id,startedAt:Date.now()}),P(e.id,s),l()):s.sessionId&&y.post(`/api/stations/${e.id}/play/end`,{sessionId:s.sessionId,duration_ms:0}).catch(()=>{})}catch(s){if(B(s))return;try{const a=await y.get(`/api/stations/${e.id}/votes`,o?{signal:o.signal}:void 0);t.player.stationId===e.id&&(t.player.votes=a,P(e.id,a),l())}catch{}}}function M({beacon:e=!1}={}){const o=t.session;if(!o||!o.id)return;t.session=null;const s={sessionId:o.id,duration_ms:Math.max(0,Date.now()-o.startedAt)},a=`/api/stations/${o.stationId}/play/end`;if(e&&typeof navigator<"u"&&navigator.sendBeacon)try{navigator.sendBeacon(a,new Blob([JSON.stringify(s)],{type:"application/json"}));return}catch{}y.post(a,s).catch(()=>{})}typeof window<"u"&&(window.addEventListener("pagehide",()=>M({beacon:!0})),window.addEventListener("beforeunload",()=>M({beacon:!0})));async function de(e){var i;const o=t.player.stationId;if(!o)return;const a=(((i=t.player.votes)==null?void 0:i.myVote)||0)===e?0:e;try{const c=await y.post(`/api/stations/${o}/vote`,{value:a});t.player.votes=c,P(o,c),l()}catch(c){k(c.message||"Vote failed")}}function P(e,o){const s=[t.stations,t.favorites];for(const a of s){const i=a.find(c=>c.id===e);i&&(i.up=o.up,i.down=o.down,i.plays=o.plays,i.score=o.score,i.my_vote=o.myVote)}}let C=null;function F(){C&&(C.remove(),C=null)}function ze(e){const o=location.origin,s=`${o}/api/v1`,a=[];return e.id!=null&&a.push({label:"Station (original)",url:`${o}/api/stations/${e.id}`}),e.uuid&&a.push({label:"Station detail",url:`${s}/stations/${e.uuid}`},{label:"Stream redirect",url:`${s}/stations/${e.uuid}/stream`},{label:"MP3 stream",url:`${s}/stations/${e.uuid}/stream?format=mp3`},{label:"AAC stream",url:`${s}/stations/${e.uuid}/stream?format=aac`},{label:"HLS stream",url:`${s}/stations/${e.uuid}/stream?format=hls`}),a.push({label:"All stations",url:`${s}/stations`},{label:"Health",url:`${s}/health`}),a}function ce(e,o,s){F();const a=ze(s);C=n("div",{class:"ctx-menu",role:"menu"},n("div",{class:"ctx-title"},s.name),n("div",{class:"ctx-sub"},s.uuid?`uuid Β· ${s.uuid}`:s.id!=null?`id Β· ${s.id} (no uuid β€” public v1 hidden)`:"no identifier"),...a.length?a.map(r=>n("div",{class:"ctx-row"},n("div",{class:"ctx-row-text"},n("div",{class:"ctx-label"},r.label),n("div",{class:"ctx-url"},r.url)),n("button",{class:"ctx-btn",title:"Copy",onClick:async p=>{p.stopPropagation();try{await navigator.clipboard.writeText(r.url),k("Copied")}catch{k("Copy failed")}}},"⧉"),n("button",{class:"ctx-btn",title:"Open",onClick:p=>{p.stopPropagation(),window.open(r.url,"_blank","noopener")}},"β†—"))):[n("div",{class:"ctx-empty"},"No public API for this station yet (missing uuid).")],t.user.role==="admin"?n("button",{class:"ctx-danger",onClick:async()=>{if(F(),!!confirm(`Delete ${s.name}?`))try{await y.del(`/api/stations/${s.id}`),await ae(),l(),k("Deleted")}catch(r){k(r.message||"Delete failed")}}},"πŸ—‘ Delete"):null),document.body.appendChild(C);const i=C.offsetWidth,c=C.offsetHeight,d=Math.min(e,window.innerWidth-i-8),f=Math.min(o,window.innerHeight-c-8);C.style.left=`${Math.max(8,d)}px`,C.style.top=`${Math.max(8,f)}px`}document.addEventListener("click",e=>{C&&!C.contains(e.target)&&F()});document.addEventListener("keydown",e=>{e.key==="Escape"&&F()});async function Xe(){const e=document.createElement("dialog");e.className="add-station";const o={name:"",country:"",genres:"",image_url:"",homepage:"",streamUrl:"",streamFormat:"mp3"},s=n("div",{class:"err"});e.appendChild(n("form",{method:"dialog",onSubmit:async a=>{a.preventDefault(),s.textContent="";const i={name:o.name.trim(),country:o.country.trim()||null,homepage:o.homepage.trim()||null,image_url:o.image_url.trim()||null,genres:o.genres.split(",").map(c=>c.trim()).filter(Boolean),streams:o.streamUrl.trim()?[{url:o.streamUrl.trim(),format:o.streamFormat,priority:0}]:[]};if(!i.name){s.textContent="Name is required.";return}try{await y.post("/api/stations",i),e.close(),await ae(),l(),k("Station added")}catch(c){s.textContent=c.message||"Failed to add station"}}},n("h2",{},"Add station"),n("label",{},"Name",n("input",{required:!0,autofocus:!0,onInput:a=>o.name=a.target.value})),n("div",{class:"row2"},n("label",{},"Country",n("input",{maxlength:4,placeholder:"NL",onInput:a=>o.country=a.target.value})),n("label",{},"Genres",n("input",{placeholder:"jazz, electronic",onInput:a=>o.genres=a.target.value}))),n("label",{},"Homepage",n("input",{type:"url",placeholder:"https://…",onInput:a=>o.homepage=a.target.value})),n("label",{},"Image URL",n("input",{type:"url",placeholder:"https://…/logo.png",onInput:a=>o.image_url=a.target.value})),n("div",{class:"row2"},n("label",{},"Stream URL",n("input",{type:"url",placeholder:"https://…/stream",onInput:a=>o.streamUrl=a.target.value})),n("label",{},"Format",n("select",{onChange:a=>o.streamFormat=a.target.value},...["mp3","aac","ogg","hls","m3u","pls","unknown"].map(a=>n("option",{value:a,selected:a==="mp3"},a))))),s,n("div",{class:"actions"},n("button",{class:"btn-ghost",type:"button",onClick:()=>e.close()},"Cancel"),n("button",{class:"btn-primary",type:"submit"},"Add")))),document.body.appendChild(e),e.showModal(),e.addEventListener("close",()=>e.remove())}let ue=null;function k(e){const o=document.querySelector(".toast");o&&o.remove();const s=n("div",{class:"toast"},e);document.body.appendChild(s),clearTimeout(ue),ue=setTimeout(()=>s.remove(),2200)}async function Ye(){var e;try{await((e=navigator.wakeLock)==null?void 0:e.request("screen"))}catch{}document.addEventListener("visibilitychange",()=>{var o;document.visibilityState==="visible"&&((o=navigator.wakeLock)==null||o.request("screen").catch(()=>{}))})}document.addEventListener("contextmenu",e=>{window.matchMedia("(display-mode: fullscreen)").matches&&e.preventDefault()});ge();