Add host-approved party suggestions and live upvotes

This commit is contained in:
Jonathan Sykes
2026-10-03 14:52:10 +08:00
parent 75dbc468cc
commit d7721045fb
10 changed files with 101 additions and 3 deletions

View File

@@ -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 = (() => {
<div class="pt-chat" id="ptChat"></div>
<form class="pt-send"><input class="pt-msg" maxlength="500" placeholder="Message…" autocomplete="off" /><button class="np-btn" type="submit">Send</button></form>
<button class="pt-leave" type="button" data-act="leave">${isHost() ? 'End the party' : 'Leave the party'}</button>`;
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();