diff --git a/frontend/app.js b/frontend/app.js index 966ade2..7d748b2 100755 --- a/frontend/app.js +++ b/frontend/app.js @@ -7343,6 +7343,7 @@ const Party = (() => { const st = { ws: null, role: null, code: '', secret: '', name: '', members: [], chat: [], state: null, offset: 0, + dj: { enabled: false, entries: [] }, djPending: false, allowControl: false, quietUntil: 0, detached: false, retry: null, pushTimer: null, driftTimer: null, lastPush: '', voice: { on: false, stream: null, muted: false, pcs: new Map(), audios: new Map() }, minimized: false, @@ -7386,12 +7387,17 @@ const Party = (() => { st.offset = (m.now || Date.now()) - Date.now(); st.members = m.members || []; st.chat = m.chat || []; + st.dj = m.dj || { enabled: false, entries: [] }; st.djPending = false; st.allowControl = !!m.allowControl; setStatus(isHost() ? 'You’re hosting' : 'Synced with the host'); render(); if (isHost()) pushState(true); else if (m.state) applyState(m.state); if (st.voice.on) { send({ type: 'voice', on: true }); connectVoicePeers(); } + } else if (m.type === 'dj') { + st.dj = { enabled: m.enabled, entries: m.entries || [] }; render(); + } else if (m.type === 'dj-play' && isHost()) { + st.djPending = false; if (m.video) { Player._handoff = null; playVideoAt(m.video); } else { st.dj.entries = st.dj.entries.filter(e => !e.approved); advanceQueue(); } } else if (m.type === 'state') { st.state = m.state; if (st.role === 'guest') applyState(m.state); @@ -7681,6 +7687,8 @@ const Party = (() => {
`; + const dj = document.createElement('div'); panel.querySelector('.pt-body').append(dj); + PartyDJ.render(dj, { state: st.dj, host: isHost(), pid, send, search: async q => { const result = await API.search(q); return result.results || result.videos || []; } }); renderChat(); } function renderChat() { @@ -7762,6 +7770,7 @@ const Party = (() => { } return { + nextDJ: () => { if (!isHost() || !st.dj.enabled || !st.dj.entries.some(e => e.approved)) return false; if (!st.djPending) { st.djPending = true; send({ type: 'dj', action: 'take' }); } return true; }, boot, guestActive, intercept, renderLyricsToggle, afterLoad: () => pushState(true), state: () => ({ role: st.role, code: st.code, detached: st.detached, members: st.members.length, voice: st.voice.on, @@ -8013,6 +8022,7 @@ function renderUpNext() { } // Advance to the next track. Wraps to the start when "repeat list" is on. function advanceQueue() { + if (Party.nextDJ()) return true; if (queueIndex >= 0 && queueIndex < queue.length - 1) { queueIndex++; } else if (!playFullMode && data.settings.repeatMode === 'all' && queue.length) { @@ -12431,6 +12441,7 @@ async function boot() { bootSearchFromUrl(); if (WEB) bootShareFromUrl(); Party.boot(); + PartyDJ.register(() => $('navPartyBtn').click()); Remote.boot(); Transition.renderButton(); PiP.render(); diff --git a/frontend/index.html b/frontend/index.html index 360b0fe..980bbf3 100755 --- a/frontend/index.html +++ b/frontend/index.html @@ -655,6 +655,7 @@ +