From c624a624df46d3d137859b2b2bfa811987214e8e Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Wed, 14 Oct 2026 10:14:17 +0200 Subject: [PATCH] Take the image set head back out of the subject key f1c5af5's set head (an image's name up to its first number) split sets whose images aren't named alike: "a1.jpg" beside "a1 (2).jpg", and "468_1000.jpg" whose first number is the image's (1 release became 211 and 161 on the corpus). The random volume stem stays. Tests keep both sets whole. Co-Authored-By: Claude Opus 5.5 --- edge/src/names.rs | 16 +++------------- lib/release/identity.ts | 4 ---- tests/unit/release/identity.spec.ts | 9 +++++---- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/edge/src/names.rs b/edge/src/names.rs index 57fc8a4..2ef04d3 100644 --- a/edge/src/names.rs +++ b/edge/src/names.rs @@ -813,15 +813,8 @@ pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release { let one_word = image && remainder.len() >= 3 && remainder.bytes().all(|c| c.is_ascii_alphabetic()); if !tag && (names_release(&remainder) || one_word) { // Without a counter an image's set: its name up to its last number, digits kept; none without numbers. - let set = if image { image_set(&stem) } else { String::new() }; - let bound = total.clone().unwrap_or_else(|| set.clone()); + let bound = total.clone().unwrap_or_else(|| if image { image_set(&stem) } else { String::new() }); let mut parts: Vec<&str> = vec!["subject", poster, &remainder, &bound]; - // An image's set as well under a counter ("Molly Set" [i/50]: "mo103x003.jpg"): its name up to its first number, - // when another follows (/^(\D*\d+)\D+\d+/). - let head = if image && total.is_some() { SET_HEAD.captures(stem.as_bytes()).map(|c| String::from_utf8(c[1].to_vec()).unwrap()) } else { None }; - if let Some(h) = head.as_deref() { - parts.push(h); - } // An upload's random stem on its volumes ("Z3mMTMPsoT4ikE_Di3fKnNF21.part01.rar" under "[ TOWN ] … [ TV ]"). let stripped: String = stem.chars().filter(|c| !matches!(c, '-' | '_')).collect(); if !image && test(&RANDOM_VOLUME, name) && stem.bytes().all(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'-') && looks_random(&stripped) { @@ -933,8 +926,6 @@ static SCENE_RELEASE: LazyLock = LazyLock::new(|| { }); /// RANDOM_VOLUME (lib/release/identity.ts): a file of a multi-volume upload. static RANDOM_VOLUME: LazyLock = LazyLock::new(|| re(r"(?i)\.(?:part[0-9]+\.rar|vol[0-9]+[+-][0-9]+\.par2|par2|r[0-9]{2,3}|7z\.[0-9]{3}|zip\.[0-9]{3}|[0-9]{3})$")); -/// An image's set head (lib/release/identity.ts): its name up to its first number, when another follows. -static SET_HEAD: LazyLock = LazyLock::new(|| re(r"^([^0-9]*[0-9]+)[^0-9]+[0-9]+")); static VOLUME_NUMBER: LazyLock = LazyLock::new(|| re(r"^(.*[^0-9. ])[. ]([0-9]{1,5})\.?$")); static GENERIC: LazyLock = LazyLock::new(|| { @@ -1589,9 +1580,8 @@ mod image_set_tests { assert_ne!(town("01", "Z3mMTMPsoT4ikE_Di3fKnNF21.part01.rar").key, town("01", "i3DpbaTUJT4L6V48fEBdbVV46.part01.rar").key); let show = |i: &str, n: &str| super::release_identity("p", &format!(r#"Some.Show.S01E01 [{i}/20] - "{n}" yEnc"#), n); assert_eq!(show("01", "The.Big.Bang.Theory.S01E01.part01.rar").key, show("20", "The.Big.Bang.Theory.S01E01.nfo").key); - let molly = |i: &str, n: &str| super::release_identity("p", &format!(r#"Molly Set [{i}/50] - "{n}" yEnc"#), n); - assert_eq!(molly("01", "mo103x003.jpg").key, molly("02", "mo103x004.jpg").key); - assert_ne!(molly("01", "mo103x003.jpg").key, molly("01", "mop120x032.jpg").key); + let tender = |i: &str, n: &str| super::release_identity("p", &format!("Tender TEEN SEX in HD - File {i} of 435 - {n}"), n); + assert_eq!(tender("001", "a1 (2).jpg").key, tender("002", "a1.jpg").key); let mdg = |post: &str, i: &str, n: &str| super::release_identity("p", &format!(r#"{post} - ({i}/90) "{n}" - 16,14 MB - yEnc"#), n); assert_eq!(mdg("MDG005b", "01", "IMG_2307.JPG").key, mdg("MDG005b", "02", "IMG_2308.jpg").key); assert_ne!(mdg("MDG005b", "01", "IMG_2307.JPG").key, mdg("MDG006a", "01", "IMG_4410.JPG").key); diff --git a/lib/release/identity.ts b/lib/release/identity.ts index f301b56..492fe43 100644 --- a/lib/release/identity.ts +++ b/lib/release/identity.ts @@ -408,10 +408,6 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null { // set ("Kindly Husband-to-Be.JPG": a thread of 1,177 releases), so the subject alone (LOG.md 2026-10-12 (2)). const set = image ? imageSet(stem) : '' const parts: (string | number)[] = ['subject', poster, remainder, total ?? set] - // An image's set as well under a counter ("Molly Set" [i/50]: "mo103x003.jpg", "mop120x032.jpg": every set of 50 one release): - // its name up to its first number, when another follows ("mo103", "destiny-001"; not "IMG_2307", one number an image). - const head = image && total !== undefined ? /^(\D*\d+)\D+\d+/.exec(stem) : null - if (head) parts.push(head[1]) // An upload's random stem on its volumes ("[ TOWN ] [ www town ag ] [ TV ] … "Z3mMTMPsoT4ikE_Di3fKnNF21.part01.rar"", // "….vol4+4.par2"): under a constant tag every upload of a total was one release (2,338 files for 20; LOG.md // 2026-10-14 (51)). No dots or spaces: a scene name ("The.Big.Bang.Theory.S01E01") isn't random. diff --git a/tests/unit/release/identity.spec.ts b/tests/unit/release/identity.spec.ts index b784d2d..97266dd 100644 --- a/tests/unit/release/identity.spec.ts +++ b/tests/unit/release/identity.spec.ts @@ -443,7 +443,7 @@ test.group('release / par2 names', () => { assert.notEqual(r('022', '457', "r86_022 - Gwen Guthrie - Ain't Nothing Going on but the Rent.mp3").key, r('022', '333', "r98_333 - Jellie - Roller Coaster.mp3").key) }) - test("under a constant tag an upload's random stem, and an image's set, bound the subject", ({ assert }) => { + test("under a constant tag an upload's random stem bounds the subject", ({ assert }) => { const id = (subject: string, name: string) => releaseIdentity({ poster: 'Kim ', subject, name, weak: false })! const town = (i: string, n: string) => id(`[ TOWN ] [ www.town.ag ] [ TV ] [${i}/20] - "${n}" yEnc`, n) assert.equal(town('01', 'Z3mMTMPsoT4ikE_Di3fKnNF21.part01.rar').key, town('18', 'Z3mMTMPsoT4ikE_Di3fKnNF21.vol4+4.par2').key) @@ -452,9 +452,10 @@ test.group('release / par2 names', () => { // A scene name keeps the subject's key, so its nfo stays with it. const show = (i: string, n: string) => id(`Some.Show.S01E01 [${i}/20] - "${n}" yEnc`, n) assert.equal(show('01', 'The.Big.Bang.Theory.S01E01.part01.rar').key, show('20', 'The.Big.Bang.Theory.S01E01.nfo').key) - const molly = (i: string, n: string) => id(`Molly Set [${i}/50] - "${n}" yEnc`, n) - assert.equal(molly('01', 'mo103x003.jpg').key, molly('02', 'mo103x004.jpg').key) - assert.notEqual(molly('01', 'mo103x003.jpg').key, molly('01', 'mop120x032.jpg').key) + // An image set under one subject stays one release whatever its images are named ("a1.jpg", "a1 (2).jpg"; "468_1000.jpg"). + const tender = (i: string, n: string) => id(`Tender TEEN SEX in HD - File ${i} of 435 - ${n}`, n) + assert.equal(tender('001', 'a1 (2).jpg').key, tender('002', 'a1.jpg').key) + assert.equal(tender('003', 'a2 (2).jpg').key, tender('002', 'a1.jpg').key) }) test('an image without words around it is keyed by its set', ({ assert }) => {