'use strict'; // Home: "Pick up where you left off" (Continue watching and Continue listening), shown above the day's agenda in the evening and below it in the morning. async function homeResume(el) { if (!el) return; const bits = await Promise.allSettled([ allowed('video') ? (async () => { const st = await jfetch('/api/plex/status'); if (!st.signedIn || !st.server) return ''; px.serverKey = st.server; const items = ((await px.get('/library/onDeck?X-Plex-Container-Start=0&X-Plex-Container-Size=8')).Metadata || []).filter(m => m.type === 'movie' || m.type === 'episode').slice(0, 6); return items.length ? `

Continue watching

${items.map(m => vdCard(m, true)).join('')}
` : ''; })() : '', allowed('audio') ? (async () => { const [data] = await Promise.all([abApi('/books'), abLoadProg()]), books = Array.isArray(data) ? data : (data.books || Object.values(data)); const cont = books.filter(b => { const p = abProg[abId(b)]; return p && p.pos > 10 && p.dur && p.pos < p.dur - 30; }).sort((a, b) => abProg[abId(b)].t - abProg[abId(a)].t).slice(0, 6); today.books = cont; return cont.length ? `

Continue listening

${cont.map(b => { const p = abProg[abId(b)], pc = Math.min(100, p.pos / p.dur * 100); return `${pc > 1 ? `` : ''}${esc(abTitle(b))}${esc(abAuthor(b))}`; }).join('')}
` : ''; })() : '', allowed('read') ? (async () => { const list = await jfetch('/api/read/list'); const cont = list.filter(b => b.pct > 0.01 && b.pct < 0.98).sort((a, b) => b.read - a.read).slice(0, 6); return cont.length ? `

Continue reading

${cont.map(b => `${b.cover ? `` : ''}${esc(b.title)}${esc(b.title)}${Math.round(b.pct * 100)}%`).join('')}
` : ''; })() : '', ]); const html = bits.map(b => b.status === 'fulfilled' ? b.value : '').join(''); if (!html || !el.isConnected) return; el.innerHTML = `

Pick up where you left off

${html}`; el.querySelectorAll('a[data-book]').forEach(a => a.onclick = e => { e.preventDefault(); const b = (today.books || []).find(x => abId(x) === a.dataset.book); if (b) abSheet(b); }); } const today = { books: [] }; // Home: how much you listened this week (podcasts and audiobooks), the day streak, and a yearly goal for audiobooks finished. The goal is kept in your synced settings. const lsTime = sec => { const m = Math.round(sec / 60); return m < 60 ? m + ' min' : Math.floor(m / 60) + ' h' + (m % 60 ? ' ' + (m % 60) + ' min' : ''); }; async function homeListening(after) { if (!after || !(allowed('podcast') || allowed('audio'))) return; let s; try { s = await jfetch('/api/listening/stats'); } catch { return; } const goal = +store.get('read_goal', 0) || 0; if (!after.isConnected || (!s.week && !goal && !s.finishedYear)) return; const max = Math.max(60, ...s.last14.map(d => d.s)), card = document.createElement('div'); card.id = 'listening'; card.innerHTML = `

Listening

${lsTime(s.week)}this week${s.streak}day${s.streak === 1 ? '' : 's'} in a row${s.finishedYear}${goal ? ' / ' + goal : ''}audiobooks finished this year
${goal ? `
` : ''}
`; after.insertAdjacentElement('afterend', card); card.querySelector('#lsgoal').onclick = () => { const el = openSheet('Audiobooks to finish this year', '', `
`); el.querySelector('#lsf').onsubmit = e => { e.preventDefault(); const n = Math.max(0, Math.min(500, Math.round(+el.querySelector('#lsn').value) || 0)); if (n) store.set('read_goal', n); else store.del('read_goal'); closeSheet(); card.remove(); homeListening(after); }; }; }