lib/nzb (buildNzb, parseNzb) from the current app; NzbService builds a release's NZB from its chunks; GET /api/v1/releases?q= (every word of the name) and GET /api/v1/releases/:id/nzb behind the api guard. A test stores a batch, finds the release and reads its NZB back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 lines
1.4 KiB
TypeScript
36 lines
1.4 KiB
TypeScript
import db from '@adonisjs/lucid/services/db'
|
|
import { decodeChunk } from '#lib/chunks/index'
|
|
import { buildNzb } from '#lib/nzb/index'
|
|
|
|
/**
|
|
* A release's NZB (REBUILD.md, part 9), from its chunks in the chunk store:
|
|
* every chunk decoded, its files in name order. A file stored twice (the same
|
|
* name and first article, e.g. a range read again) is taken once.
|
|
*/
|
|
class NzbService {
|
|
async build(releaseId: number): Promise<{ name: string; xml: string } | null> {
|
|
const release = await db.from('releases').where('id', releaseId).select('name').first()
|
|
if (!release) return null
|
|
const chunks = await db.from('release_chunks').where('release_id', releaseId).orderBy('id').select('payload')
|
|
const files = new Map<string, ReturnType<typeof decodeChunk>[number]>()
|
|
for (const chunk of chunks) {
|
|
for (const file of decodeChunk(chunk.payload)) files.set(`${file.name}\n${file.segments[0]?.[1] ?? ''}`, file)
|
|
}
|
|
const xml = buildNzb(
|
|
[...files.values()]
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
.map((f) => ({
|
|
name: f.name,
|
|
poster: f.poster,
|
|
date: f.postedAt ?? 0,
|
|
groups: f.groups,
|
|
segments: f.segments.map(([number, id, size]) => ({ number, id, size })),
|
|
})),
|
|
{ title: release.name }
|
|
)
|
|
return { name: release.name, xml }
|
|
}
|
|
}
|
|
|
|
export default new NzbService()
|