A quoted word that isn't a file name; split parts with two digits
Test and deploy / test (push) Successful in 46s
Test and deploy / edge (push) Successful in 1m57s
Test and deploy / deploy (push) Successful in 1m41s

From the re-index's single-file releases:
- A quoted text without an extension ("My \"Official\" remaining set's
  of Inna … 025.jpg [25/74]", "… lr140x16.jpg ( file 046 of 922)
  \"yEnc\"", "\"Dr. Lust\" - … - [12/21] - Ed_DRL_VDR_LM.part09.rar") was
  the file name, the real one stayed in the subject: every file a release.
  It gives way to a file name with an extension beside it
  (nameBesideQuotes); random quoted names with none beside keep theirs.
- Split parts numbered with two digits ("Admiral.EP05.2008.720p.Rus.
  HDCLUB.mkv.21", a.b.movies.divx.russian) share their stem, as .001.
Both implementations; tests; fixtures identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daan MeijerandClaude Opus 5.5 committed 2026-10-11 23:46:43 +02:00
1 parent 625ea23171
commit 2814c77225
8 files changed
+77 -9

No files matched your search

+3 -2
View File
@@ -1,4 +1,4 @@
import { fileKey, unquotedName } from '../headers/index.js'
import { fileKey, nameBesideQuotes, unquotedName } from '../headers/index.js'
import { releaseIdentity, type ReleaseIdentity } from '../release/index.js'
/** A file's details, shared by all of its segments (one object per file, not per article). */
@@ -138,7 +138,8 @@ export class SegmentScanner {
/** A file's details from its first article seen (as parseSubject and segmentFromHeader). */
private describe(subject: string, nameEnd: number, counter: Counter, from: string, xref: string, group: string): FileMeta | null {
const nameStart = nameEnd > 0 ? subject.lastIndexOf('"', nameEnd - 1) : -1
const found = nameStart !== -1 ? subject.slice(nameStart + 1, nameEnd) : unquotedName(subject.slice(0, counter.index))
const quoted = nameStart !== -1 ? subject.slice(nameStart + 1, nameEnd) : null
const found = quoted !== null ? (nameBesideQuotes(quoted, subject.slice(0, counter.index)) ?? quoted) : unquotedName(subject.slice(0, counter.index))
if (!found || counter.total < 1) return null
const name = own(found)
const base = own((subject.slice(0, counter.index) + subject.slice(counter.index + counter.length)).replace(/\s+/g, ' ').trim())
+1 -1
View File
@@ -1,3 +1,3 @@
export { parseSubject, unquotedName, type ParsedSubject } from './subject.js'
export { parseSubject, unquotedName, nameBesideQuotes, type ParsedSubject } from './subject.js'
export { fileKey, weakFileKey, counterFileKey, looksLikeFilename } from './file_key.js'
export { segmentFromHeader, type IngestSegment, type OverviewHeader, type IdentityCache } from './segment.js'
+13 -1
View File
@@ -30,6 +30,17 @@ const EXTENSION = /[^.\s]\.[A-Za-z0-9]{2,4}$/
* (01/24)`). Null when no file name is found: the subject stays obfuscated.
* Words are split on spaces only (both implementations alike).
*/
/**
* The file name of a subject whose quoted text has no extension ("My
* \"Official\" remaining set's of Inna … 025.jpg", "\"Dr. Lust\" - … - Ed_DRL_VDR_LM.part09.rar"):
* a name with one elsewhere, else null (random quoted names keep theirs).
*/
export function nameBesideQuotes(quoted: string, beforeCounter: string): string | null {
if (EXTENSION.test(quoted)) return null
const name = unquotedName(beforeCounter.replace(/"[^"]*"/g, ' '))
return name && EXTENSION.test(name) ? name : null
}
export function unquotedName(beforeCounter: string): string | null {
let text = beforeCounter.trimEnd()
if (/yenc$/i.test(text)) text = text.slice(0, -4).trimEnd()
@@ -78,7 +89,8 @@ export function parseSubject(subject: string): ParsedSubject | null {
if (match.index > nameEnd) counter = match
}
if (!counter) return null
const name = nameStart !== -1 ? subject.slice(nameStart + 1, nameEnd) : unquotedName(subject.slice(0, counter.index))
const quoted = nameStart !== -1 ? subject.slice(nameStart + 1, nameEnd) : null
const name = quoted !== null ? (nameBesideQuotes(quoted, subject.slice(0, counter.index)) ?? quoted) : unquotedName(subject.slice(0, counter.index))
if (!name) return null
const segment = Number.parseInt(counter[1], 10)
+1 -1
View File
@@ -33,7 +33,7 @@ const SUFFIXES = [
/\.7z(\.\d{3})?$/i,
/\.zip(\.\d{3})?$/i,
// Split volumes (x.mkv.001), not codec names (H.264, H.265).
/(?<=\.(7z|zip|rar|tar|mkv|mp4|avi|m4v|ts|m2ts|iso|img|bin|vob))\.\d{3}$/i,
/(?<=\.(7z|zip|rar|tar|mkv|mp4|avi|m4v|ts|m2ts|iso|img|bin|vob))\.\d{2,3}$/i,
/\.(nfo|sfv|nzb|srr|srs|txt|jpg|png|md5|sha1|srt|sub|idx|ass|ssa|m3u|m3u8|cue|log)$/i,
/\.tar$/i,
/\.(zst|gz|xz|bz2)$/i,