Names: stray quotes, single quotes, hosts, bracketed names; dates, repeats
From the first corpus sweep (183 ranges):
- A stray quote took the last quote pair ('"x.mp3" "Album_BY_WaRLoRD',
'"x.nfo" " yEnc', '"x.nzb" yEnc"'): the last quoted part with an
extension is the name. Each file was a release of its own.
- A single-quoted name at the end ("… [001/106] 'You, Me And Dupree.nfo'"),
not the host before it (www.x2l.nl).
- Text ending in a host ("… www.libranews.nl") is no file name; a name in
brackets comes first ("[01/77][LIBRATEAM_M2219009.par2]",
"-^-[Mnozil Brass - Seven.nzb]-^-").
- A leading date is no item number ("09-30-2008_BAK_JRS.r00": a daily
show's 32,621 files were one release under the poster's tag).
- The file name repeated elsewhere in the subject goes too
("(NELET11.par2) [01/58] - "NELET11.par2"").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
885e4e6541
commit
1a5035d988
6 files changed
+115
-11
No files matched your search
+15
-2
@@ -37,11 +37,17 @@ const EXTENSION = /[^.\s]\.[A-Za-z0-9]{2,4}$/
|
||||
*/
|
||||
export function nameBesideQuotes(quoted: string, beforeCounter: string): string | null {
|
||||
if (EXTENSION.test(quoted)) return null
|
||||
// A stray quote took the last pair ("(01/26) "01 Time Machine.mp3" "Silicon Dream_BY_WaRLoRD yEnc",
|
||||
// "… "x.nfo" " yEnc", "… "x.nzb" yEnc""): the last quoted part that is a file name.
|
||||
const named = [...beforeCounter.matchAll(/"([^"]*)"/g)].map((m) => m[1]).filter((q) => EXTENSION.test(q))
|
||||
if (named.length > 0) return named.at(-1)!
|
||||
const name = unquotedName(beforeCounter.replace(/"[^"]*"/g, ' '))
|
||||
return name && EXTENSION.test(name) ? name : null
|
||||
}
|
||||
|
||||
const IMAGE_NAME = /\.(jpe?g|png|gif|bmp|webp|tiff?)$/i
|
||||
/** A host ("www.libranews.nl", "www.x2l.nl"): its ".nl" is no extension. */
|
||||
const HOST = /www\./i
|
||||
|
||||
export function unquotedName(beforeCounter: string): string | null {
|
||||
let text = beforeCounter.trimEnd()
|
||||
@@ -59,6 +65,9 @@ export function unquotedName(beforeCounter: string): string | null {
|
||||
const word = before.slice(before.lastIndexOf(' ') + 1)
|
||||
return EXTENSION.test(word) ? word : null
|
||||
}
|
||||
// A single-quoted name at the end ("xlady post You, Me And Dupree www.x2l.nl [001/106] 'You, Me And Dupree.nfo'").
|
||||
const single = /'([^']*[^.\s']\.[A-Za-z0-9]{2,4})'$/.exec(text)
|
||||
if (single) return single[1]
|
||||
if (EXTENSION.test(text)) {
|
||||
const bracket = text.lastIndexOf('] ')
|
||||
// A track or part number ("Album - CD2 - 07 - Artist - Title.mp3"): the name starts there, so the rest
|
||||
@@ -76,9 +85,13 @@ export function unquotedName(beforeCounter: string): string | null {
|
||||
const last = name.slice(space + 1)
|
||||
if (/\d$/.test(name.slice(0, space).trimEnd()) && !last.startsWith('(')) return last
|
||||
}
|
||||
return name && EXTENSION.test(name) ? name : null
|
||||
// Ending in a host is no file name: the one in brackets or a word follows.
|
||||
if (!HOST.test(name)) return name && EXTENSION.test(name) ? name : null
|
||||
}
|
||||
for (const word of text.split(' ')) if (EXTENSION.test(word)) return word
|
||||
// A name in brackets ("[01/77][LIBRATEAM_M2219009.par2] … www.libranews.nl", "-^-[Mnozil Brass - Seven.nzb]-^-").
|
||||
const bracketed = [...text.matchAll(/\[([^[\]]*[^.\s[\]]\.[A-Za-z0-9]{2,4})\]/g)].at(-1)
|
||||
if (bracketed) return bracketed[1]
|
||||
for (const word of text.split(' ')) if (EXTENSION.test(word) && !HOST.test(word)) return word
|
||||
return null
|
||||
}
|
||||
const FILE_COUNTER = /\[(\d+)\/(\d+)\]/
|
||||
|
||||
@@ -286,7 +286,11 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
: input.subject.includes(input.name)
|
||||
? input.subject.replace(input.name, ' ')
|
||||
: input.subject.replace(collapsed, ' ')
|
||||
).replace(/"/g, ' ')
|
||||
)
|
||||
// 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')
|
||||
.join(' ')
|
||||
.replace(/"/g, ' ')
|
||||
const remainder = subjectRemainder(named)
|
||||
// Counted without the file name, as quoted names are: "shyla-shy-09-0195.jpg hardcore …" is no "9 of 195"
|
||||
// (each of 866 images was a release of its own; LOG.md 2026-10-12 (5)).
|
||||
@@ -303,7 +307,8 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
// a set ("ai-engineer-complete [401/465] - "003 Defining Tools with JSON Schema.mp4"": a course's 465
|
||||
// lessons were releases of their own; LOG.md 2026-10-11 (7)). Uploads hours apart are apart anyway.
|
||||
// Also after a disc code ("SC8367-12 - Wynette, Tammy - Singing My Song", karaoke discs).
|
||||
const numbered = /^(?:[A-Z]{1,5}\d{2,6}[-_ ])?\d{1,3}[ ._-]/.test(stem)
|
||||
// Not a date ("09-30-2008_BAK_JRS.r00": a daily show's 32,621 files were one release under the poster's tag).
|
||||
const numbered = /^(?:[A-Z]{1,5}\d{2,6}[-_ ])?\d{1,3}[ ._-]/.test(stem) && !/^\d{1,2}[-._]\d{1,2}[-._](?:19|20)\d{2}(?!\d)/.test(stem)
|
||||
// A hashed stem under text without a counter is the upload, the text a tag ("Seit 1753 Tagen - usenet4ever.info -
|
||||
// "055631a1_b6c820bc_a856086c_85112a5-u4e.part01.rar"": a day's uploads were one release; LOG.md 2026-10-12 (11)).
|
||||
const hashed = fileTotal === undefined && HASHED.test(stem)
|
||||
|
||||
Reference in new issue
Block a user