Search and the web interface use the prober's findings
- Search matches a word in the name or the inner name (trigram index on inner_name, partial, built concurrently); "Readable names" includes releases with an inner name. - unlocked=1 (chip "No password") leaves out passworded releases. - Results carry passworded and innerName: a Password badge, the inner name under the release name. - The release view: Inside and Inspected facts, a passworded notice, and Contents (per probed article its yEnc name, kind, type, encryption and the names inside) from release_inspections. - Newznab: the password attribute from the prober; an obfuscated release with an inner name is included, titled by it (without its extension). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5ab5399b43
commit
92480174b9
8 files changed
+177
-16
No files matched your search
@@ -395,6 +395,17 @@ kbd {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.release-inner {
|
||||
font-size: 12.5px;
|
||||
color: var(--muted);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.release-inner::before {
|
||||
content: "↳ ";
|
||||
color: var(--faint);
|
||||
}
|
||||
|
||||
.release-meta {
|
||||
font-size: 12.5px;
|
||||
color: var(--faint);
|
||||
@@ -842,3 +853,41 @@ kbd {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.contents {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.inspection {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.inspection-head {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.inspection-head .name {
|
||||
font-family: var(--mono);
|
||||
font-size: 12.5px;
|
||||
overflow-wrap: anywhere;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.inspection-names {
|
||||
margin: 8px 0 0;
|
||||
padding-left: 18px;
|
||||
font-size: 13px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.inspection small {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
color: var(--faint);
|
||||
}
|
||||
+44
-4
@@ -2,9 +2,16 @@
|
||||
// text is rendered with textContent, never as HTML.
|
||||
|
||||
const $ = (selector) => document.querySelector(selector)
|
||||
const FILTERS = ['readable', 'complete']
|
||||
const FILTERS = ['readable', 'complete', 'unlocked']
|
||||
/** Text inputs that narrow a search: size bounds in MB. */
|
||||
const FIELDS = ['min_size', 'max_size']
|
||||
/** What a probed article was (probe_candidates.kind). */
|
||||
const KIND_LABELS = {
|
||||
archive: 'Archive start',
|
||||
par2: 'Par2 index',
|
||||
lone: 'Single file',
|
||||
'7z-tail': '7z end',
|
||||
}
|
||||
const SOURCE_LABELS = {
|
||||
stem: 'Filename',
|
||||
subject: 'Subject',
|
||||
@@ -37,6 +44,7 @@ const state = {
|
||||
max_size: '',
|
||||
readable: false,
|
||||
complete: false,
|
||||
unlocked: false,
|
||||
page: 1,
|
||||
release: null,
|
||||
}
|
||||
@@ -200,12 +208,19 @@ function releaseRow(release) {
|
||||
release.groups?.length ? release.groups[0] + (release.groups.length > 1 ? ` +${release.groups.length - 1}` : '') : '',
|
||||
].filter(Boolean)
|
||||
row.querySelector('.release-meta').textContent = meta.join(' · ')
|
||||
if (release.innerName) {
|
||||
const inner = row.querySelector('.release-inner')
|
||||
inner.textContent = release.innerName
|
||||
inner.title = 'Found inside by reading its articles'
|
||||
inner.hidden = false
|
||||
}
|
||||
|
||||
const badges = row.querySelector('.badges')
|
||||
const files = `${release.fileCount} ${release.fileCount === 1 ? 'file' : 'files'}`
|
||||
if (release.complete) badges.append(badge(files, 'good', 'Complete'))
|
||||
else if (release.fileTotal) badges.append(badge(`${release.fileCount}/${release.fileTotal}`, 'warn', 'Files present / announced'))
|
||||
else badges.append(badge(files))
|
||||
if (release.passworded) badges.append(badge('Password', 'bad', 'Its archive is encrypted: a password is needed'))
|
||||
if (!release.settled) badges.append(badge('Collecting', 'accent', 'Recently seen: more files may still arrive'))
|
||||
if (release.category && release.category !== 8010) badges.append(badge(categoryLabel(release.category), '', 'Category (from the name)'))
|
||||
if (release.source && release.source !== 'stem' && release.source !== 'subject') {
|
||||
@@ -323,15 +338,22 @@ function renderDetail(release) {
|
||||
fact('Posted', release.postedAt ? `${new Date(release.postedAt).toLocaleString()} (${formatAge(release.postedAt)})` : null),
|
||||
fact('Poster', poster),
|
||||
fact('Groups', release.groups?.join(', ')),
|
||||
fact('Indexed', release.firstSeenAt ? `${new Date(release.firstSeenAt).toLocaleString()} (${formatAge(release.firstSeenAt)})` : null)
|
||||
fact('Indexed', release.firstSeenAt ? `${new Date(release.firstSeenAt).toLocaleString()} (${formatAge(release.firstSeenAt)})` : null),
|
||||
fact('Inside', release.innerName),
|
||||
fact('Inspected', release.inspectedAt ? `${new Date(release.inspectedAt).toLocaleString()} (${formatAge(release.inspectedAt)})` : null)
|
||||
)
|
||||
|
||||
const notices = []
|
||||
if (!release.settledAt) {
|
||||
notices.push(el('div', { class: 'notice notice-warn', text: 'Recently seen: more files of this release may still arrive. The NZB has what is indexed so far.' }))
|
||||
}
|
||||
if (!release.readable) {
|
||||
notices.push(el('div', { class: 'notice', text: 'Obfuscated file names: what this release holds is only known once its articles are read (phase 2).' }))
|
||||
if (release.passworded) {
|
||||
notices.push(el('div', { class: 'notice notice-bad', text: 'Passworded: its archive has encrypted entries or headers. The password is not in the index.' }))
|
||||
}
|
||||
if (!release.readable && !release.innerName) {
|
||||
notices.push(el('div', { class: 'notice', text: release.inspectedAt
|
||||
? 'Obfuscated file names, and its articles did not say more.'
|
||||
: 'Obfuscated file names: what this release holds is only known once its articles are read (phase 2).' }))
|
||||
}
|
||||
|
||||
const files = el('table', { class: 'file-table' },
|
||||
@@ -341,10 +363,28 @@ function renderDetail(release) {
|
||||
el('td', { class: 'num', text: formatSize(f.size) }),
|
||||
el('td', { class: 'num', text: `${f.parts} ${f.parts === 1 ? 'part' : 'parts'}` })))))
|
||||
|
||||
const inspections = release.inspections ?? []
|
||||
const contents = inspections.length
|
||||
? el('div', { class: 'contents' }, inspections.map((i) =>
|
||||
el('div', { class: 'inspection' },
|
||||
el('div', { class: 'inspection-head' },
|
||||
el('span', { class: 'name', text: i.yencName || '(no name)' }),
|
||||
badge(KIND_LABELS[i.kind] ?? i.kind),
|
||||
i.type ? badge(i.type) : null,
|
||||
i.passworded ? badge('Encrypted', 'bad') : null),
|
||||
i.names?.length
|
||||
? el('ul', { class: 'inspection-names' }, i.names.map((n) => el('li', { text: n })))
|
||||
: null,
|
||||
i.entries > (i.names?.length ?? 0)
|
||||
? el('small', { text: `${number.format(i.entries)} in all; the largest shown` })
|
||||
: null)))
|
||||
: null
|
||||
|
||||
$('#detail').replaceChildren(
|
||||
actions,
|
||||
...notices,
|
||||
facts,
|
||||
...(contents ? section('Contents', `${inspections.length} ${inspections.length === 1 ? 'article' : 'articles'} read`, contents) : []),
|
||||
...section('Files', `${release.files.length} posted`, files)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<div class="filters" role="group" aria-label="Filters">
|
||||
<button class="chip" type="button" data-filter="readable" aria-pressed="false">Readable names</button>
|
||||
<button class="chip" type="button" data-filter="complete" aria-pressed="false">Complete</button>
|
||||
<button class="chip" type="button" data-filter="unlocked" aria-pressed="false" title="Leave out releases found passworded">No password</button>
|
||||
<label class="select select-small">
|
||||
<span class="visually-hidden">Category</span>
|
||||
<select id="cat">
|
||||
@@ -96,6 +97,7 @@
|
||||
<li class="release">
|
||||
<button type="button" class="release-main">
|
||||
<span class="release-name"></span>
|
||||
<span class="release-inner" hidden></span>
|
||||
<span class="release-meta"></span>
|
||||
</button>
|
||||
<div class="release-side">
|
||||
|
||||
Reference in new issue
Block a user