Search
@@ -41,7 +54,7 @@ function viewSearch() {
const part = (st, off, label) => st === undefined ? off : st.err ? bad('error: ' + st.err) : `✓${st.n !== undefined ? ' ' + st.n : ''}`;
const filesOff = !sf.token() ? link('#/files', 'sign in to search files') : '✓';
const musicOff = !plexSt || !plexSt.signedIn ? link('#/music', 'sign in to search music') : !plexSt.server ? link('#/music', 'choose a Plex server') : '✓';
- el.innerHTML = `Searching: Tasks ${part(r.tasks, '✓')} · Events ${part(r.events, '✓')} · Files ${part(r.files, filesOff)} · Music ${part(r.music, musicOff)}`;
+ el.innerHTML = `Searching: Tasks ${part(r.tasks, '✓')} · Events ${part(r.events, '✓')} · Files ${part(r.files, filesOff)} · Music ${part(r.music, musicOff)} · Movies & TV ${part(r.video, musicOff)} · Podcasts ${part(r.pods, '✓')}`;
};
jfetch('/api/plex/status').then(st => { plexSt = st; showSources(); }).catch(() => showSources());
showSources();
@@ -55,10 +68,12 @@ function viewSearch() {
const enc = encodeURIComponent, lower = term.toLowerCase();
const hasSf = !!sf.token(), plexReady = !!(plexSt && plexSt.signedIn && plexSt.server);
const libsP = hasSf ? sf.req('/api2/repos/') : Promise.reject(new Error('signed out'));
- const [libs, files, music, ddg, hub] = await Promise.allSettled([
+ const [libs, files, music, video, pods, ddg, hub] = await Promise.allSettled([
libsP,
libsP.then(l => searchFiles(term, l)),
plexReady ? searchMusic(term) : Promise.reject(new Error('not connected')),
+ plexReady ? searchVideo(term) : Promise.reject(new Error('not connected')),
+ allowed('podcast') ? searchPodcasts(term) : Promise.reject(new Error('not allowed')),
jfetch('/api/ddg?q=' + enc(term)),
jfetch('/api/search?q=' + enc(term)),
]);
@@ -78,6 +93,8 @@ function viewSearch() {
else if (m.type === 'album') musicHtml += `
${esc(m.title)}Album · ${esc(m.parentTitle || '')}`;
else if (m.type === 'track') { const tr = mapTrack(m, {}); if (tr) musicHtml += `
`; }
}
+ const videoHtml = (video.status === 'fulfilled' ? video.value : []).map(m => vdCard(m)).join('');
+ const podHtml = (pods.status === 'fulfilled' ? pods.value : []).map(s => `
${esc(pdF(s, 'title'))}Podcast${pdF(s, 'author') ? ' · ' + esc(pdF(s, 'author')) : ''}`).join('');
const d = ddg.status === 'fulfilled' ? ddg.value : {};
const card = (d.text || d.answer) ? `
${d.image ? `
})
` : ''}
@@ -96,11 +113,13 @@ function viewSearch() {
events: hub.status === 'fulfilled' ? { n: (h.events || []).length } : { err: hub.reason && hub.reason.message },
files: !hasSf ? undefined : files.status === 'fulfilled' ? { n: found.length + libHtml.split('class="row"').length - 1 } : { err: (files.reason && files.reason.message) || (libs.reason && libs.reason.message) || 'failed' },
music: !plexReady ? undefined : music.status === 'fulfilled' ? { n: music.value.length } : { err: music.reason && music.reason.message },
+ pods: !allowed('podcast') ? undefined : pods.status === 'fulfilled' ? { n: pods.value.length } : { err: pods.reason && pods.reason.message },
+ video: !plexReady ? undefined : video.status === 'fulfilled' ? { n: video.value.length } : { err: video.reason && video.reason.message },
});
const notes = [...(h.errors || [])].map(e => `
${esc(e)}
`).join('');
const linkHtml = allowed('links') ? allLinks().filter(l => (l.title + ' ' + l.url + ' ' + (l.group || '')).toLowerCase().includes(lower)).slice(0, 8).map(l => `
${icon('link')}${esc(l.title || l.url)}${esc(new URL(safeUrl(l.url)).host)}${l.group ? ' · ' + esc(l.group) : ''}`).join('') : '';
- res.innerHTML = card + sec('Tasks', taskHtml) + sec('Links', linkHtml) + sec('Events', eventHtml) + sec('Libraries', libHtml) + sec('Files', fileHtml) + sec('Music', musicHtml) + notes;
- if (!taskHtml && !linkHtml && !eventHtml && !libHtml && !fileHtml && !musicHtml && !card) res.innerHTML = '
No results. The line above shows what was searched.
' + notes;
+ res.innerHTML = card + sec('Tasks', taskHtml) + sec('Links', linkHtml) + sec('Events', eventHtml) + sec('Libraries', libHtml) + sec('Files', fileHtml) + sec('Music', musicHtml) + (podHtml ? sec('Podcasts', podHtml) : '') + (videoHtml ? `
Movies & TV
${videoHtml}
` : '') + notes;
+ if (!taskHtml && !linkHtml && !eventHtml && !libHtml && !fileHtml && !musicHtml && !videoHtml && !podHtml && !card) res.innerHTML = '
No results. The line above shows what was searched.
' + notes;
res.querySelectorAll('button[data-f]').forEach(b => b.onclick = () => { const [r, p, n, dd] = JSON.parse(b.dataset.f); openFile(r, p, n, dd); });
res.querySelectorAll('button[data-t]').forEach(b => b.onclick = () => playQueue([JSON.parse(b.dataset.t)], 0));
res.querySelectorAll('button[data-tl]').forEach(b => b.onclick = () => { store.set('task_list', b.dataset.tl); location.hash = '#/tasks'; });