video search: per-source result panels + redesigned, readable cards
- Each source now has its OWN results panel under its row (movie + episode), and the search passes the source through — so Soulseek / Torrent / Usenet each show their own distinct hits instead of one shared identical list. - Card redesign for readability: a big colour-coded RESOLUTION tile (4K gold / 1080p accent / 720p blue / SD) anchors each row; a plain-English quality summary leads (e.g. 'BluRay · X265 · DTS-HD' + an HDR/REPACK tag); the raw release name is demoted to a single muted truncated line; size · seeders · group sit below; the verdict pill (✓ Meets profile / ✕ reason) and ⤓ Grab stay on the right. Dropped the old break-all mono title + badge-row layout. JS/CSS balance clean.
This commit is contained in:
parent
3490092e3d
commit
bf17ef3fab
2 changed files with 97 additions and 75 deletions
|
|
@ -58,20 +58,42 @@
|
|||
'<button class="vdl-search-all" type="button" data-vdl-search-all>⌕ Search all</button>' +
|
||||
'</div>' +
|
||||
'<div class="vdl-sources" data-vdl-sources><div class="vdl-src-empty">Loading sources…</div></div>' +
|
||||
'<div class="vdl-results" data-vdl-results hidden></div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
// One source = a row + its OWN results panel (so each source shows its own hits).
|
||||
function srcRowHTML(s, mini) {
|
||||
var m = SRC_META[s];
|
||||
return '<div class="vdl-src' + (mini ? ' vdl-src--mini' : '') + '" data-vdl-src="' + s + '">' +
|
||||
'<span class="vdl-src-icon"><span class="vdl-src-emoji">' + m.emoji + '</span></span>' +
|
||||
'<span class="vdl-src-main"><span class="vdl-src-name">' + esc(m.name) + '</span>' +
|
||||
'<span class="vdl-src-meta"><span class="vdl-src-dot"></span><span class="vdl-src-status" data-vdl-status>Ready</span></span></span>' +
|
||||
'<button class="vdl-src-search" type="button" data-vdl-search="' + s + '">⌕ Search</button>' +
|
||||
'</div>';
|
||||
}
|
||||
function srcBlockHTML(s, mini) {
|
||||
return '<div class="vdl-src-block" data-vdl-src-block="' + s + '">' +
|
||||
srcRowHTML(s, mini) +
|
||||
'<div class="vdl-results" data-vdl-results-for="' + s + '" hidden></div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function _movieSearch(container, block) {
|
||||
var o = container._opts || {};
|
||||
var s = block.getAttribute('data-vdl-src-block');
|
||||
searchInto(container, block.querySelector('[data-vdl-results-for="' + s + '"]'),
|
||||
{ scope: 'movie', title: o.title || '', year: o.year || null, source: s },
|
||||
[block.querySelector('.vdl-src')]);
|
||||
}
|
||||
|
||||
function onClick(e) {
|
||||
var container = e.currentTarget;
|
||||
if (e.target.closest('[data-vdl-grab]')) { toast('Grabbing isn’t wired up yet — coming soon', 'info'); return; }
|
||||
var o = container._opts || {};
|
||||
var params = { scope: 'movie', title: o.title || '', year: o.year || null };
|
||||
var results = container.querySelector('[data-vdl-results]');
|
||||
var sb = e.target.closest('[data-vdl-search]');
|
||||
if (sb) { searchInto(container, results, params, [sb.closest('.vdl-src')]); return; }
|
||||
if (sb) { _movieSearch(container, sb.closest('[data-vdl-src-block]')); return; }
|
||||
if (e.target.closest('[data-vdl-search-all]')) {
|
||||
searchInto(container, results, params, Array.prototype.slice.call(container.querySelectorAll('.vdl-src')));
|
||||
Array.prototype.forEach.call(container.querySelectorAll('[data-vdl-src-block]'),
|
||||
function (block) { _movieSearch(container, block); });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,18 +187,7 @@
|
|||
box.innerHTML = '<div class="vdl-src-empty">No download source configured — pick one on Settings → Downloads.</div>';
|
||||
return;
|
||||
}
|
||||
box.innerHTML = list.map(function (s) {
|
||||
var m = SRC_META[s];
|
||||
return '<div class="vdl-src" data-vdl-src="' + s + '">' +
|
||||
'<span class="vdl-src-icon"><span class="vdl-src-emoji">' + m.emoji + '</span></span>' +
|
||||
'<span class="vdl-src-main">' +
|
||||
'<span class="vdl-src-name">' + esc(m.name) + '</span>' +
|
||||
'<span class="vdl-src-meta"><span class="vdl-src-dot"></span>' +
|
||||
'<span class="vdl-src-status" data-vdl-status>Ready</span></span>' +
|
||||
'</span>' +
|
||||
'<button class="vdl-src-search" type="button" data-vdl-search="' + s + '">⌕ Search</button>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
box.innerHTML = list.map(function (s) { return srcBlockHTML(s, false); }).join('');
|
||||
}
|
||||
|
||||
// Scaffold: a satisfying faux-scan (animated) that resolves to "coming soon".
|
||||
|
|
@ -222,30 +233,37 @@
|
|||
: s === 'episode' ? 'this episode' : 'for the movie';
|
||||
}
|
||||
|
||||
function rb(text, mod) { return '<span class="vdl-rb' + (mod ? ' vdl-rb--' + mod : '') + '">' + esc(text) + '</span>'; }
|
||||
function resKind(res) {
|
||||
return res === '2160p' ? '4k' : res === '1080p' ? '1080' : res === '720p' ? '720' : 'sd';
|
||||
}
|
||||
|
||||
// Readable card: a big resolution tile anchors it, a plain-English quality summary
|
||||
// leads, the raw release name is demoted to a muted one-liner, then size/seeders.
|
||||
function resultCardHTML(r) {
|
||||
var badges = [];
|
||||
if (r.resolution) badges.push(rb(RES_LABEL[r.resolution] || r.resolution, 'res'));
|
||||
if (r.source) badges.push(rb(SRC_LABEL[r.source] || r.source, 'src'));
|
||||
if (r.codec) badges.push(rb(String(r.codec).toUpperCase()));
|
||||
if (r.hdr) badges.push(rb(String(r.hdr).toUpperCase(), 'hdr'));
|
||||
if (r.audio) badges.push(rb(String(r.audio).toUpperCase().replace('-', ' ')));
|
||||
if (r.repack) badges.push(rb('REPACK'));
|
||||
var summary = [SRC_LABEL[r.source] || r.source,
|
||||
r.codec ? String(r.codec).toUpperCase() : '',
|
||||
r.audio ? String(r.audio).toUpperCase().replace('-', ' ') : ''].filter(Boolean).join(' · ');
|
||||
var tags = '';
|
||||
if (r.hdr) tags += '<span class="vdl-res-tag vdl-res-tag--hdr">' + esc(String(r.hdr).toUpperCase()) + '</span>';
|
||||
if (r.repack) tags += '<span class="vdl-res-tag">REPACK</span>';
|
||||
var verdict = r.accepted
|
||||
? '<span class="vdl-res-verdict vdl-res-verdict--ok">✓ Meets profile</span>'
|
||||
: '<span class="vdl-res-verdict vdl-res-verdict--no">✕ ' + esc(r.rejected || 'Filtered') + '</span>';
|
||||
: '<span class="vdl-res-verdict vdl-res-verdict--no" title="' + esc(r.rejected || '') + '">✕ ' + esc(r.rejected || 'Filtered') + '</span>';
|
||||
return '<div class="vdl-res' + (r.accepted ? '' : ' vdl-res--rejected') + '">' +
|
||||
'<div class="vdl-res-top">' +
|
||||
'<div class="vdl-res-title" title="' + esc(r.title) + '">' + esc(r.title) + '</div>' +
|
||||
(r.accepted ? '<button class="vdl-res-grab" type="button" data-vdl-grab title="Grab this release">⤓</button>' : '') +
|
||||
'</div>' +
|
||||
'<div class="vdl-res-badges">' + badges.join('') + '</div>' +
|
||||
'<div class="vdl-res-foot">' +
|
||||
'<span class="vdl-res-stat">' + r.size_gb + ' GB</span>' +
|
||||
'<span class="vdl-res-stat vdl-res-seed">▲ ' + (r.seeders || 0) + '</span>' +
|
||||
verdict +
|
||||
'<div class="vdl-res-res vdl-res-res--' + resKind(r.resolution) + '">' + esc(RES_LABEL[r.resolution] || r.resolution || '?') + '</div>' +
|
||||
'<div class="vdl-res-body">' +
|
||||
'<div class="vdl-res-line1">' +
|
||||
'<span class="vdl-res-summary">' + esc(summary) + '</span>' + tags +
|
||||
verdict +
|
||||
'</div>' +
|
||||
'<div class="vdl-res-name" title="' + esc(r.title) + '">' + esc(r.title) + '</div>' +
|
||||
'<div class="vdl-res-meta">' +
|
||||
'<span class="vdl-res-stat"><span class="vdl-res-ico">💾</span>' + r.size_gb + ' GB</span>' +
|
||||
'<span class="vdl-res-stat vdl-res-seed">▲ ' + (r.seeders || 0) + ' seeders</span>' +
|
||||
(r.group ? '<span class="vdl-res-stat vdl-res-grp">' + esc(r.group) + '</span>' : '') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
(r.accepted ? '<button class="vdl-res-grab" type="button" data-vdl-grab title="Grab this release">⤓</button>' : '') +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
|
|
@ -384,13 +402,15 @@
|
|||
function onShowClick(e) {
|
||||
var container = e.currentTarget; var st = container._dl; if (!st) return;
|
||||
if (e.target.closest('[data-vdl-grab]')) { toast('Grabbing isn’t wired up yet — coming soon', 'info'); return; }
|
||||
// Episode-scope search (a source row inside an expanded episode).
|
||||
// Episode-scope search (a source row inside an expanded episode → its own panel).
|
||||
var srch = e.target.closest('[data-vdl-search]');
|
||||
if (srch) {
|
||||
var epEl = srch.closest('.vdl-ep'); if (!epEl) return;
|
||||
var parts = (epEl.getAttribute('data-vdl-ep') || '').split('_');
|
||||
searchInto(container, epEl.querySelector('[data-vdl-ep-results]'),
|
||||
{ scope: 'episode', title: st.title, season: +parts[0], episode: +parts[1] },
|
||||
var s = srch.getAttribute('data-vdl-search');
|
||||
var block = srch.closest('[data-vdl-src-block]');
|
||||
searchInto(container, block.querySelector('[data-vdl-results-for="' + s + '"]'),
|
||||
{ scope: 'episode', title: st.title, season: +parts[0], episode: +parts[1], source: s },
|
||||
[srch.closest('.vdl-src')]);
|
||||
return;
|
||||
}
|
||||
|
|
@ -444,16 +464,7 @@
|
|||
var panel = epEl.querySelector('[data-vdl-ep-search]'); if (!panel) return;
|
||||
var srcs = (container._dl.sources || []).filter(function (s) { return SRC_META[s]; });
|
||||
if (!srcs.length) { panel.innerHTML = '<div class="vdl-ep-srcs"><div class="vdl-src-empty">No source configured.</div></div>'; return; }
|
||||
panel.innerHTML = '<div class="vdl-ep-srcs">' + srcs.map(function (s) {
|
||||
var m = SRC_META[s];
|
||||
return '<div class="vdl-src vdl-src--mini" data-vdl-src="' + s + '">' +
|
||||
'<span class="vdl-src-icon"><span class="vdl-src-emoji">' + m.emoji + '</span></span>' +
|
||||
'<span class="vdl-src-main"><span class="vdl-src-name">' + esc(m.name) + '</span>' +
|
||||
'<span class="vdl-src-meta"><span class="vdl-src-dot"></span><span class="vdl-src-status" data-vdl-status>Ready</span></span></span>' +
|
||||
'<button class="vdl-src-search" type="button" data-vdl-search="' + s + '">⌕ Search</button>' +
|
||||
'</div>';
|
||||
}).join('') + '</div>' +
|
||||
'<div class="vdl-results" data-vdl-ep-results hidden></div>';
|
||||
panel.innerHTML = '<div class="vdl-ep-srcs">' + srcs.map(function (s) { return srcBlockHTML(s, true); }).join('') + '</div>';
|
||||
}
|
||||
|
||||
function setSeasonSel(container, sn, on) {
|
||||
|
|
|
|||
|
|
@ -3253,8 +3253,7 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
.vdl-results { margin-top: 12px; }
|
||||
.vdl-results[hidden] { display: none; }
|
||||
.vdl-results--season { margin: 2px 14px 12px 14px; }
|
||||
.vdl-ep-search .vdl-results { padding: 0 14px 12px 42px; margin-top: 2px; }
|
||||
.vdl-ep--open .vdl-ep-search { max-height: 1800px; } /* room for result cards */
|
||||
.vdl-ep--open .vdl-ep-search { max-height: 2400px; } /* room for result cards */
|
||||
.vdl-res-loading { display: flex; align-items: center; gap: 9px; font-size: 12.5px; font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.6); padding: 9px 2px; }
|
||||
.vdl-res-spin { width: 14px; height: 14px; border-radius: 50%; flex-shrink: 0;
|
||||
|
|
@ -3264,32 +3263,44 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
.vdl-res-head { font-size: 11.5px; font-weight: 700; color: rgba(255, 255, 255, 0.5); margin: 4px 0 10px; }
|
||||
.vdl-res-head strong { color: #fff; }
|
||||
.vdl-res-okn { color: #8fe7af; }
|
||||
.vdl-res { border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.03);
|
||||
padding: 11px 13px; margin-bottom: 8px; animation: vdlRise 0.4s both;
|
||||
/* result row: [resolution tile] [body: summary + name + meta] [grab] */
|
||||
.vdl-res { display: flex; align-items: stretch; gap: 12px; border-radius: 13px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.035);
|
||||
padding: 11px 12px; margin-bottom: 8px; animation: vdlRise 0.4s both;
|
||||
transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease; }
|
||||
.vdl-res:hover { border-color: rgba(var(--accent-rgb), 0.4); background: rgba(255, 255, 255, 0.055); transform: translateY(-1px); }
|
||||
.vdl-res:hover { border-color: rgba(var(--accent-rgb), 0.4); background: rgba(255, 255, 255, 0.06); transform: translateY(-1px); }
|
||||
.vdl-res--rejected { opacity: 0.5; }
|
||||
.vdl-res--rejected:hover { opacity: 0.78; }
|
||||
.vdl-res-top { display: flex; align-items: flex-start; gap: 10px; }
|
||||
.vdl-res-title { flex: 1; min-width: 0; font: 600 11.5px/1.45 'JetBrains Mono', ui-monospace, monospace;
|
||||
color: rgba(255, 255, 255, 0.9); word-break: break-all; }
|
||||
.vdl-res-grab { flex-shrink: 0; width: 30px; height: 30px; border-radius: 9px; cursor: pointer; font-size: 15px;
|
||||
font-weight: 900; background: rgb(var(--accent-rgb)); color: #06210f; border: none;
|
||||
transition: filter 0.15s ease, transform 0.15s ease; }
|
||||
.vdl-res-grab:hover { filter: brightness(1.12); transform: translateY(-1px); }
|
||||
.vdl-res-badges { display: flex; flex-wrap: wrap; gap: 6px; margin: 9px 0; }
|
||||
.vdl-rb { font-size: 10px; font-weight: 800; letter-spacing: 0.02em; padding: 2px 8px; border-radius: 6px;
|
||||
text-transform: uppercase; background: rgba(255, 255, 255, 0.07); border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.8); }
|
||||
.vdl-rb--res { background: rgba(var(--accent-rgb), 0.2); border-color: rgba(var(--accent-rgb), 0.42); color: #fff; }
|
||||
.vdl-rb--src { background: rgba(255, 255, 255, 0.12); color: #fff; }
|
||||
.vdl-rb--hdr { background: linear-gradient(100deg, rgba(245, 158, 11, 0.28), rgba(168, 85, 247, 0.28));
|
||||
border-color: rgba(245, 158, 11, 0.4); color: #fde2b3; }
|
||||
.vdl-res-foot { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
|
||||
.vdl-res-stat { font-size: 11.5px; font-weight: 700; color: rgba(255, 255, 255, 0.55); }
|
||||
.vdl-res--rejected:hover { opacity: 0.82; }
|
||||
/* resolution tile — the quick-scan anchor */
|
||||
.vdl-res-res { flex-shrink: 0; width: 52px; display: flex; align-items: center; justify-content: center;
|
||||
border-radius: 10px; font-size: 14px; font-weight: 900; letter-spacing: -0.02em; color: #06210f; }
|
||||
.vdl-res-res--4k { background: linear-gradient(140deg, #ffd76a, #f0a830); }
|
||||
.vdl-res-res--1080 { background: linear-gradient(140deg, rgba(var(--accent-rgb), 1), rgba(var(--accent-rgb), 0.7)); }
|
||||
.vdl-res-res--720 { background: linear-gradient(140deg, #8fb3ff, #5f86d6); }
|
||||
.vdl-res-res--sd { background: linear-gradient(140deg, rgba(255,255,255,0.4), rgba(255,255,255,0.22)); color: #11141a; }
|
||||
.vdl-res-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
|
||||
.vdl-res-line1 { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.vdl-res-summary { font-size: 13.5px; font-weight: 800; color: #fff; letter-spacing: -0.01em; }
|
||||
.vdl-res-tag { font-size: 9.5px; font-weight: 800; letter-spacing: 0.03em; padding: 2px 7px; border-radius: 5px;
|
||||
text-transform: uppercase; background: rgba(255, 255, 255, 0.1); color: rgba(255, 255, 255, 0.85); }
|
||||
.vdl-res-tag--hdr { background: linear-gradient(100deg, rgba(245, 158, 11, 0.4), rgba(168, 85, 247, 0.4)); color: #fff; }
|
||||
.vdl-res-verdict { margin-left: auto; font-size: 10.5px; font-weight: 800; padding: 3px 9px; border-radius: 999px; white-space: nowrap; }
|
||||
.vdl-res-verdict--ok { background: rgba(108, 211, 145, 0.18); color: #8fe7af; }
|
||||
.vdl-res-verdict--no { background: rgba(245, 158, 11, 0.14); color: #fbcd7a; max-width: 60%; overflow: hidden; text-overflow: ellipsis; }
|
||||
.vdl-res-name { font: 500 11px/1.3 'JetBrains Mono', ui-monospace, monospace; color: rgba(255, 255, 255, 0.42);
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.vdl-res-meta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-top: 1px; }
|
||||
.vdl-res-stat { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; font-weight: 700;
|
||||
color: rgba(255, 255, 255, 0.6); }
|
||||
.vdl-res-ico { font-size: 11px; opacity: 0.8; }
|
||||
.vdl-res-seed { color: #8fe7af; }
|
||||
.vdl-res-verdict { margin-left: auto; font-size: 10.5px; font-weight: 800; padding: 3px 9px; border-radius: 999px; }
|
||||
.vdl-res-verdict--ok { background: rgba(108, 211, 145, 0.16); color: #8fe7af; }
|
||||
.vdl-res-verdict--no { background: rgba(255, 255, 255, 0.06); color: rgba(255, 255, 255, 0.5); }
|
||||
.vdl-res-grp { font-family: 'JetBrains Mono', ui-monospace, monospace; color: rgba(255, 255, 255, 0.4); font-weight: 600; }
|
||||
.vdl-res-grab { flex-shrink: 0; align-self: center; width: 34px; height: 34px; border-radius: 10px; cursor: pointer;
|
||||
font-size: 16px; font-weight: 900; background: rgb(var(--accent-rgb)); color: #06210f; border: none;
|
||||
transition: filter 0.15s ease, transform 0.15s ease; }
|
||||
.vdl-res-grab:hover { filter: brightness(1.12); transform: translateY(-1px) scale(1.04); }
|
||||
.vdl-btn--busy { opacity: 0.7; cursor: progress; }
|
||||
/* per-source block (row + its own results) */
|
||||
.vdl-src-block { display: flex; flex-direction: column; }
|
||||
.vdl-src-block .vdl-results { margin: 8px 0 2px; }
|
||||
@media (prefers-reduced-motion: reduce) { .vdl-res, .vdl-res-spin { animation: none !important; } }
|
||||
|
|
|
|||
Loading…
Reference in a new issue