diff --git a/public/music-extras.js b/public/music-extras.js
index 96a1d24..3680672 100644
--- a/public/music-extras.js
+++ b/public/music-extras.js
@@ -97,7 +97,7 @@ function plexPinSheet(u) {
async function plexSwitchTo(u, pin, onError) {
try {
await jfetch('/api/plex/switch', { method: 'POST', body: JSON.stringify({ id: u.id, pin }) });
- closeSheet(); plexHome.data = null; px.secs = null;
+ closeSheet(); plexHome.data = null; px.secs = null; wl.id = ''; try { for (const k of Object.keys(localStorage)) if (k.startsWith('vc:')) localStorage.removeItem(k); } catch {} // (what the last person had on their home screen)
toast('Now using Plex as ' + u.title); route();
} catch (e) { if (onError) onError(e.message); else toast(e.message); }
}
diff --git a/public/video.js b/public/video.js
index d9a8c49..aa34f38 100644
--- a/public/video.js
+++ b/public/video.js
@@ -53,6 +53,19 @@ const vdCard = (m, wide, deck) => {
${esc(m.type === 'episode' && wide ? m.title : m.title)}${m.type === 'episode' ? vdEp(m) + (vdLeft(m) ? ' · ' + vdLeft(m) + ' min left' : '') : esc([m.year, vdLeft(m) ? vdLeft(m) + ' min left' : '', m.type === 'show' && m.leafCount ? (m.leafCount - (m.viewedLeafCount || 0)) + ' unwatched' : ''].filter(Boolean).join(' · '))}`;
return deck ? `
${html}
` : html;
};
+// ---- Watch later: a normal Plex video playlist called "Watch later" (so it follows the Plex person you use, and shows in Plex's own apps too)
+const wl = { id: '' };
+async function wlItems() {
+ if (!wl.id) { const p = ((await px.get('/playlists?playlistType=video')).Metadata || []).find(x => x.title === 'Watch later' && !x.smart); if (!p) return []; wl.id = p.ratingKey; }
+ return (await px.get(`/playlists/${wl.id}/items`)).Metadata || [];
+}
+async function wlAdd(rk) {
+ const uri = await plx.uri([rk]);
+ if (!wl.id) { await wlItems(); }
+ if (wl.id) return plx.call('PUT', `/playlists/${wl.id}/items?uri=${encodeURIComponent(uri)}`);
+ const r = await plx.call('POST', `/playlists?type=video&smart=0&title=${encodeURIComponent('Watch later')}&uri=${encodeURIComponent(uri)}`); wl.id = (((r.MediaContainer || {}).Metadata || [])[0] || {}).ratingKey || '';
+}
+async function wlRemove(rk) { const it = (await wlItems()).find(x => x.ratingKey === String(rk)); if (it) await plx.call('DELETE', `/playlists/${wl.id}/items/${it.playlistItemID}`); }
/** Take an item off Continue watching. Newer Plex servers can just hide it; an older one cannot, so it is marked watched instead (and we say so). */
async function vdRemove(m, quiet) {
try { const r = await fetch('/plex/actions/removeFromContinueWatching?ratingKey=' + encodeURIComponent(m.ratingKey), { method: 'PUT' }); if (!r.ok) throw new Error(r.status); if (!quiet) toast('Removed from Continue watching'); }
@@ -66,10 +79,11 @@ async function videoHome(st, libKey) {
if (!secs.length) { v.innerHTML = '
Movies & TV
This Plex server has no movie or TV libraries (or your Plex user cannot see them).
`;
+ plexHomeBadge(st); // "Watching as …" with the switch, when the Plex Home has more than one person
// Continue watching, Recently added, Unwatched and the library load side by side. The rows are kept on this device (see vcGet) so they draw at once.
const vcK = k => 'vc:' + (ME ? ME.id : '') + ':' + k;
const vcGet = k => { try { return JSON.parse(localStorage.getItem(vcK(k))); } catch { return null; } };
@@ -89,6 +103,8 @@ async function videoHome(st, libKey) {
const items = mc.Metadata || []; vcPut(key, items); const el = $(id); if (el) el.innerHTML = items.length ? `