More per-file suffixes: old rar volumes, .rev, a part's par2, .bc; a size after an unquoted name
Each file of these uploads was a release of its own: - "BD_NoTHing018.s41", "…CyjI.s00": rar volumes after .r99 (as the file's own lower-case extension only, so "show.s01.rar" keeps its season); - "chappelle1.part02.rev": rar recovery volumes; - "X.part1.par2", "X.part1.vol00+34.par2" beside "X.part1.rar"; - "NZPEC….180.bc": BCArchive volumes; - "[27/70] - 2407.jpg - 28": the size in kB after an unquoted name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
2823d42bfa
commit
d4e53ca0d8
4 files changed
+127
-17
No files matched your search
+26
-8
@@ -25,11 +25,16 @@ export interface ReleaseInput {
|
||||
// Suffixes that differ between the files of one release. Stripped repeatedly,
|
||||
// so "x.mkv.vol03+04.par2" and "x.part07.rar" both reduce to "x".
|
||||
const SUFFIXES = [
|
||||
// A part's par2 files ("X.part1.par2", "X.part1.vol00+34.par2" beside "X.part1.rar": 683 single files in a.b.test).
|
||||
/\.part\d+(?:\.vol\d+[+-]\d+)?\.par2$/i,
|
||||
/\.vol\d+[+-]\d+\.par2$/i,
|
||||
/\.par2$/i,
|
||||
/\.part\d+\.rar$/i,
|
||||
// Recovery volumes too ("chappelle1.part02.rev").
|
||||
/\.part\d+\.(?:rar|rev)$/i,
|
||||
/\.r\d{2,3}$/i,
|
||||
/\.rar$/i,
|
||||
/\.(?:rar|rev)$/i,
|
||||
// BCArchive volumes ("NZPECQUztWOponTpCratvvTPDBXBjvHIULOUPvSH.180.bc").
|
||||
/(?:\.\d{1,4})?\.bc$/i,
|
||||
/\.7z(\.\d{3})?$/i,
|
||||
/\.zip(\.\d{3})?$/i,
|
||||
// Split volumes (x.mkv.001), not codec names (H.264, H.265).
|
||||
@@ -43,7 +48,9 @@ const SUFFIXES = [
|
||||
|
||||
/** Strips per-file suffixes (volume numbers, archive and par2 extensions) from a filename. */
|
||||
export function fileStem(name: string): string {
|
||||
let stem = name.trim()
|
||||
// Old-style rar volumes after .r99 ("BD_NoTHing018.s41", "….s00"): only as the file's own extension, in lower case, so
|
||||
// a season ("show.s01.rar", "Show.S01") stays.
|
||||
let stem = name.trim().replace(/\.[s-z]\d{2}$/, '')
|
||||
let changed = true
|
||||
while (changed) {
|
||||
changed = false
|
||||
@@ -281,6 +288,15 @@ export function randomPoster(from: string): boolean {
|
||||
return looksRandom((at === -1 ? address : address.slice(0, at)).replace(/[-_.]/g, ''))
|
||||
}
|
||||
|
||||
/** The subject without a number after the file's unquoted name at its end (" - 28": a size in kB). */
|
||||
export function sizeAfterName(subject: string, name: string): string {
|
||||
const end = subject.trimEnd()
|
||||
const digits = end.length - end.replace(/\d+$/, '').length
|
||||
if (digits === 0) return subject
|
||||
const before = end.slice(0, end.length - digits).replace(/\s*-\s*$/, '')
|
||||
return before.length < end.length - digits && name.length > 0 && before.endsWith(name) && !before.endsWith(`"${name}"`) ? before : subject
|
||||
}
|
||||
|
||||
export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
const poster = randomPoster(input.poster) ? '' : input.poster
|
||||
const counter = /\[(\d+)\/(\d+)\]/.exec(input.subject)
|
||||
@@ -300,14 +316,16 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
// as the quotes would have been: otherwise every file of a set would name a release of its own.
|
||||
// The subject here has its spaces collapsed; the name may not ("lara_bath 15of19 larabath_2.5.jpg").
|
||||
const collapsed = input.name.replace(/\s+/g, ' ').trim()
|
||||
// A size after a name that isn't quoted, at the end ("[27/70] - 2407.jpg - 28": each image a release of its own).
|
||||
const subject = sizeAfterName(input.subject, input.name)
|
||||
// Other quoted text stays: it names the release (">>> CREATIVE COMMONS NZB <<< "0190 alarm 5.0 " - File 1 of 9:
|
||||
// "creative_commons_nzb_0190_alarm_5.0_.rar"": 12,605 files of 1,400 uploads were one release; LOG.md 2026-10-12 (9)).
|
||||
const named = (
|
||||
input.subject.includes(`"${input.name}"`)
|
||||
? input.subject.replace(`"${input.name}"`, ' ')
|
||||
: input.subject.includes(input.name)
|
||||
? input.subject.replace(input.name, ' ')
|
||||
: input.subject.replace(collapsed, ' ')
|
||||
subject.includes(`"${input.name}"`)
|
||||
? subject.replace(`"${input.name}"`, ' ')
|
||||
: subject.includes(input.name)
|
||||
? subject.replace(input.name, ' ')
|
||||
: subject.replace(collapsed, ' ')
|
||||
)
|
||||
// The name repeated elsewhere ("(NELET11.par2) [01/58] - "NELET11.par2"": each file a release of its own).
|
||||
.split(input.name.length >= 5 ? input.name : '\0')
|
||||
|
||||
Reference in new issue
Block a user