The junk judge: releases judged by what their probes showed; verdicts hide them
Voyager 1's second filter (D42) for voyager 2. Hourly, in order (the first rule wins): dead (every probed first article gone), fake (a film, episode or music name whose content is a program: archive entries all executables, or the file itself by name or sniffed exe), repeated (the same first 16 KiB and size in 20+ releases), locked (passworded, no password known), unusable (random names, no inner name, no merge, no spot, a day after settling). Verdicts in release_verdicts (rule, version) hide releases from search (judged shows them); a verdict whose rule no longer matches is taken back. Nothing dropped yet. Service judge; releases:judge --once --dry counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
99b72ddd8f
commit
5897d595cf
9 files changed
+324
-3
No files matched your search
@@ -0,0 +1,91 @@
|
||||
import { test } from '@japa/runner'
|
||||
import db from '@adonisjs/lucid/services/db'
|
||||
import testUtils from '@adonisjs/core/services/test_utils'
|
||||
import { ReleaseJudge } from '#services/release_judge'
|
||||
import SearchService from '#services/search_service'
|
||||
|
||||
test.group('ReleaseJudge', (group) => {
|
||||
group.each.setup(() => testUtils.db().withGlobalTransaction())
|
||||
|
||||
test('judges dead, fake, repeated, locked and unusable releases, and takes a verdict back', async ({ assert }) => {
|
||||
let n = 0
|
||||
const settled = new Date(Date.now() - 3 * 86400_000)
|
||||
const release = async (name: string, extra: Record<string, unknown> = {}) => {
|
||||
n++
|
||||
const key = `jj${String(n).padStart(6, '0')}`.padEnd(40, '0')
|
||||
const [row] = await db
|
||||
.table('releases')
|
||||
.insert({ release_key: key, identity_key: key, name, source: 'stem', groups: '[]', group_id: 1, files: 3, posted_at: settled, readable: true, settled_at: settled, ...extra })
|
||||
.returning('id')
|
||||
return Number(row.id)
|
||||
}
|
||||
let candidate = 9000
|
||||
const probe = async (releaseId: number, status: string) => {
|
||||
candidate++
|
||||
await db.table('probe_candidates').insert({ id: candidate, release_id: releaseId, kind: 'lone', message_id: `<${candidate}@x>`, file: 'x', status })
|
||||
return candidate
|
||||
}
|
||||
const inspect = async (releaseId: number, kind: string, result: Record<string, unknown>) => {
|
||||
const id = await probe(releaseId, 'done')
|
||||
await db.table('release_inspections').insert({ release_id: releaseId, candidate_id: id, kind, result: JSON.stringify(result) })
|
||||
}
|
||||
|
||||
const dead = await release('Gone.Movie.2020.1080p')
|
||||
await probe(dead, 'missing')
|
||||
await probe(dead, 'missing')
|
||||
const halfDead = await release('Half.Movie.2020.1080p')
|
||||
await probe(halfDead, 'missing')
|
||||
await probe(halfDead, 'done')
|
||||
|
||||
const fakeArchive = await release('Das.A-Team.S02E23.Das.Spiel.ist.aus')
|
||||
await inspect(fakeArchive, 'archive', { type: 'rar4', names: ['Das.A-Team.S02E23.Das.Spiel.ist.aus.exe'], entries: 1 })
|
||||
const fakeFile = await release('Some.Film.2021.1080p.WEB-DL')
|
||||
await inspect(fakeFile, 'lone', { type: 'exe', yencName: 'Some.Film.2021.1080p.WEB-DL.mkv' })
|
||||
// A program under a program's name is software, not fake.
|
||||
const software = await release('LimeWire 4.19.6')
|
||||
await inspect(software, 'archive', { type: 'rar4', names: ['LimeWire 4.19.6.exe'], entries: 1 })
|
||||
|
||||
const hash = 'a'.repeat(32)
|
||||
const repeatedIds: number[] = []
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const id = await release(`kantodo${i}`)
|
||||
repeatedIds.push(id)
|
||||
await db.table('file_hashes').insert({ candidate_id: 20000 + i, release_id: id, hash16k: hash, size: 1073741824 })
|
||||
}
|
||||
|
||||
const locked = await release('Locked.Thing', { passworded: true })
|
||||
const unlocked = await release('Known.Password', { passworded: true })
|
||||
await db.table('release_passwords').insert({ release_id: unlocked, password: 'pw', source: 'spot-text' })
|
||||
|
||||
const unusable = await release('ab12CD34ef56GH78ij90', { readable: false })
|
||||
const fresh = await release('zz12CD34ef56GH78ij90', { readable: false, settled_at: new Date() })
|
||||
const spotted = await release('yy12CD34ef56GH78ij90', { readable: false })
|
||||
await db.table('release_spots').insert({ release_id: spotted, spot_id: 's@x', files: 1 })
|
||||
|
||||
const round = await new ReleaseJudge().round()
|
||||
const verdicts = new Map((await db.from('release_verdicts').select('release_id', 'rule')).map((v) => [Number(v.release_id), v.rule]))
|
||||
assert.equal(verdicts.get(dead), 'dead')
|
||||
assert.isUndefined(verdicts.get(halfDead))
|
||||
assert.equal(verdicts.get(fakeArchive), 'fake')
|
||||
assert.equal(verdicts.get(fakeFile), 'fake')
|
||||
assert.isUndefined(verdicts.get(software))
|
||||
assert.deepEqual([...new Set(repeatedIds.map((id) => verdicts.get(id)))], ['repeated'])
|
||||
assert.equal(verdicts.get(locked), 'locked')
|
||||
assert.isUndefined(verdicts.get(unlocked))
|
||||
assert.equal(verdicts.get(unusable), 'unusable')
|
||||
assert.isUndefined(verdicts.get(fresh))
|
||||
assert.isUndefined(verdicts.get(spotted))
|
||||
assert.equal(round.rules.repeated.added, 20)
|
||||
|
||||
// Search leaves them out, unless asked.
|
||||
const found = await SearchService.search({ q: 'Locked' })
|
||||
assert.equal(found.total, 0)
|
||||
assert.equal((await SearchService.search({ q: 'Locked', judged: true })).total, 1)
|
||||
|
||||
// A password found: the next round takes the verdict back.
|
||||
await db.table('release_passwords').insert({ release_id: locked, password: 'found', source: 'nzb' })
|
||||
const again = await new ReleaseJudge().round()
|
||||
assert.equal(again.rules.locked.removed, 1)
|
||||
assert.isNull(await db.from('release_verdicts').where('release_id', locked).first())
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user