A random poster per file is no part of a release key
Releases 78361820 and 78411563 were two files of one FTD upload
("Yusef_Lateef-Alight_Upon_the_Lake_…-WEB-2026-FTD", [1/6] and [3/6], 4 s
apart) under a new random poster each ("N9vplIaavBCz@aCTVoAsk.JSE",
"FVWWHku0fGAg@QfaTz0g0.RVH"): the stem key includes the poster. When the
poster's own name (before the @) looks random, it's left out of the key;
real posters keep theirs (two uploaders of one scene release at the same
time stay apart), and uploads hours apart stay apart by the re-upload
rule. Both implementations; tests; fixtures identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b1213fbea0
commit
df35f4678c
3 files changed
+55
-5
No files matched your search
+20
-5
@@ -184,7 +184,22 @@ export function isPosterTag(remainder: string, stem: string): boolean {
|
||||
* file cannot be grouped from its name alone (random extensionless names in
|
||||
* obfuscated posts; those need par2 matching). See CONCEPT.md (D16).
|
||||
*/
|
||||
/**
|
||||
* A poster whose own name (before the "@") is a random token, as uploaders
|
||||
* that draw a new poster per file do ("N9vplIaavBCz@aCTVoAsk.JSE"): it says
|
||||
* nothing about which upload a file is in, so it's no part of a release key
|
||||
* (two files of one FTD upload were releases apart; LOG.md 2026-10-11 (8)).
|
||||
*/
|
||||
export function randomPoster(from: string): boolean {
|
||||
const open = from.indexOf('<')
|
||||
const close = open === -1 ? -1 : from.indexOf('>', open + 1)
|
||||
const address = close !== -1 ? from.slice(open + 1, close) : from.trim().split(' ')[0]
|
||||
const at = address.indexOf('@')
|
||||
return looksRandom(at === -1 ? address : address.slice(0, at))
|
||||
}
|
||||
|
||||
export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
const poster = randomPoster(input.poster) ? '' : input.poster
|
||||
const counter = /\[(\d+)\/(\d+)\]/.exec(input.subject)
|
||||
// The post rule's key takes the [i/M] total (as when the catalog started); the release's file total any counter's.
|
||||
const postTotal = counter ? Number.parseInt(counter[2], 10) : undefined
|
||||
@@ -203,7 +218,7 @@ 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.
|
||||
const remainder = subjectRemainder(input.subject.includes(`"${input.name}"`) ? input.subject : input.subject.replace(input.name, ' ').replace(/"/g, ' '))
|
||||
// A document or comic is a release of its own (collections post hundreds under one subject).
|
||||
if (DOCUMENT.test(input.name)) return { key: hash('stem', input.poster, stem), name: stem.replace(DOCUMENT, '') || stem, fileTotal, source: 'stem' }
|
||||
if (DOCUMENT.test(input.name)) return { key: hash('stem', poster, stem), name: stem.replace(DOCUMENT, '') || stem, fileTotal, source: 'stem' }
|
||||
const image = IMAGE.test(input.name)
|
||||
// The poster's constant tag ("[KiLL.dA…]---- (01/10) "Album.rar"", "(X Posted)") would make one
|
||||
// release of all their uploads: a readable file name that shares no word with it says which
|
||||
@@ -224,7 +239,7 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
// of one upload (par2 and nfo included) share it (LOG.md 2026-10-09 (9)).
|
||||
const total = counterTotal(input.subject)
|
||||
return {
|
||||
key: hash('subject', input.poster, remainder, total ?? prefix),
|
||||
key: hash('subject', poster, remainder, total ?? prefix),
|
||||
name: remainder,
|
||||
fileTotal,
|
||||
source: 'subject',
|
||||
@@ -233,13 +248,13 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
// Not a name, but a post id (e.g. `83110629-n [10/18] - "random"`): with the
|
||||
// [i/M] counter it groups the files of one post whose names are random.
|
||||
if (!tag && postTotal && isPostToken(remainder)) {
|
||||
return { key: hash('post', input.poster, remainder, postTotal), name: remainder, fileTotal, source: 'post' }
|
||||
return { key: hash('post', poster, remainder, postTotal), name: remainder, fileTotal, source: 'post' }
|
||||
}
|
||||
// An image without words around it: its set by its name (keyed as a stem, so "x.jpg" joins "x.part01.rar").
|
||||
if (image) return { key: hash('stem', input.poster, prefix || stem), name: prefix || stem, fileTotal, source: 'stem' }
|
||||
if (image) return { key: hash('stem', poster, prefix || stem), name: prefix || stem, fileTotal, source: 'stem' }
|
||||
// The [i/M] total is informational, not part of the key: posters often
|
||||
// upload the par2 index without a counter, and it belongs to the same post.
|
||||
return { key: hash('stem', input.poster, stem), name: stem, fileTotal, source: 'stem' }
|
||||
return { key: hash('stem', poster, stem), name: stem, fileTotal, source: 'stem' }
|
||||
}
|
||||
|
||||
/** Release of a par2 recovery set; every file listed in it belongs to it. */
|
||||
|
||||
Reference in new issue
Block a user