Content probe: don't download the first segment again for whole files
Par2 sets, NZBs and SFVs were sniffed from their first segment and then read whole, downloading that segment a second time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
23bbdcc5ee
commit
bef68075be
2 files changed
+23
-25
No files matched your search
+21
-24
@@ -93,32 +93,29 @@ export function decodeBody(body: Buffer): DecodedSegment {
|
||||
}
|
||||
}
|
||||
|
||||
/** All of a (small) file whose first segment is already decoded; null when it is too big. */
|
||||
export async function readWhole(first: Buffer, segments: TaskSegment[], fetch: BodyFetcher): Promise<Buffer | null> {
|
||||
/**
|
||||
* All of a (small) file whose first segment is already decoded; null when it is too big.
|
||||
* The first segment isn't downloaded again (it was, for every par2 set, NZB and SFV).
|
||||
*/
|
||||
export async function readWhole(first: DecodedSegment, segments: TaskSegment[], fetch: BodyFetcher): Promise<Buffer | null> {
|
||||
const bytes = segments.reduce((sum, p) => sum + (p.size ?? 0), 0)
|
||||
if (segments.length > WHOLE_MAX_PARTS || bytes > WHOLE_MAX_BYTES) return null
|
||||
if (segments.length === 1) return first
|
||||
let assembled: YencFile | undefined
|
||||
for (const part of segments) {
|
||||
const segment = decodeSegment(await fetch(part.id), { lenientSize: true })
|
||||
assembled ??= YencFile.fromSegment(segment)
|
||||
assembled.add(segment)
|
||||
}
|
||||
return assembled!.getBuffer()
|
||||
if (segments.length === 1) return first.data
|
||||
const assembled = YencFile.fromSegment(first)
|
||||
assembled.add(first)
|
||||
for (const part of segments.slice(1)) assembled.add(decodeSegment(await fetch(part.id), { lenientSize: true }))
|
||||
return assembled.getBuffer()
|
||||
}
|
||||
|
||||
/** A par2 file whose first segment is already decoded, with its other parts if any; null when it is not par2. */
|
||||
export async function readPar2(first: Buffer, segments: TaskSegment[], fetch: BodyFetcher): Promise<Par2Set | null> {
|
||||
if (!isPar2(first)) return null
|
||||
export async function readPar2(first: DecodedSegment, segments: TaskSegment[], fetch: BodyFetcher): Promise<Par2Set | null> {
|
||||
if (!isPar2(first.data)) return null
|
||||
if (segments.length > PAR2_MAX_PARTS) return null
|
||||
if (segments.length === 1) return parsePar2(first)
|
||||
let assembled: YencFile | undefined
|
||||
for (const part of segments) {
|
||||
const segment = decodeSegment(await fetch(part.id))
|
||||
assembled ??= YencFile.fromSegment(segment)
|
||||
assembled.add(segment)
|
||||
}
|
||||
return parsePar2(assembled!.getBuffer())
|
||||
if (segments.length === 1) return parsePar2(first.data)
|
||||
const assembled = YencFile.fromSegment(first)
|
||||
assembled.add(first)
|
||||
for (const part of segments.slice(1)) assembled.add(decodeSegment(await fetch(part.id)))
|
||||
return parsePar2(assembled.getBuffer())
|
||||
}
|
||||
|
||||
export function isNzbEntry(entry: Pick<RarEntry, 'name' | 'encrypted' | 'directory'>): boolean {
|
||||
@@ -151,14 +148,14 @@ export async function probeInspection(
|
||||
return probe
|
||||
}
|
||||
if (/\.sfv$/i.test(task.name)) {
|
||||
probe.text = (await readWhole(segment.data, task.segments, fetch))?.toString('latin1') ?? null
|
||||
probe.text = (await readWhole(segment, task.segments, fetch))?.toString('latin1') ?? null
|
||||
return probe
|
||||
}
|
||||
if (contentType === 'nzb') {
|
||||
probe.text = (await readWhole(segment.data, task.segments, fetch))?.toString('utf8') ?? null
|
||||
probe.text = (await readWhole(segment, task.segments, fetch))?.toString('utf8') ?? null
|
||||
return probe
|
||||
}
|
||||
probe.par2 = await readPar2(segment.data, task.segments, fetch)
|
||||
probe.par2 = await readPar2(segment, task.segments, fetch)
|
||||
if (probe.par2) return probe
|
||||
if (contentType === '7z') {
|
||||
probe.archive = task.sevenZip ? await listSevenZip(segment.data, task.sevenZip.volumes, task.sevenZip.last, fetch, warn) : null
|
||||
@@ -168,7 +165,7 @@ export async function probeInspection(
|
||||
probe.archive = info
|
||||
if (info && hasNzbEntries(info)) {
|
||||
probe.archiveNzbs = []
|
||||
const archive = await readWhole(segment.data, task.segments, fetch)
|
||||
const archive = await readWhole(segment, task.segments, fetch)
|
||||
const archived = archive && parseRarHeaders(archive)
|
||||
const entries = archived ? archived.entries.filter(isNzbEntry) : []
|
||||
probe.archiveNzbEntries = entries.length
|
||||
|
||||
@@ -59,7 +59,8 @@ test.group('content probe', () => {
|
||||
const probe = await probeInspection(task, fetch)
|
||||
assert.equal(probe.contentType, 'nzb')
|
||||
assert.equal(probe.text, xml)
|
||||
assert.lengthOf(fetched, task.segments.length + 1) // the first segment twice: once to sniff, once in the whole
|
||||
// Every segment once: the first, decoded to sniff, is reused for the whole.
|
||||
assert.deepEqual(fetched, task.segments.map((s) => s.id))
|
||||
})
|
||||
|
||||
test('a 7z archive with encrypted headers is known from its tail, without bsdtar', async ({ assert }) => {
|
||||
|
||||
Reference in new issue
Block a user