diff --git a/edge/src/names.rs b/edge/src/names.rs index 397a304..0b7d71a 100644 --- a/edge/src/names.rs +++ b/edge/src/names.rs @@ -417,7 +417,15 @@ static DOCUMENT: LazyLock = LazyLock::new(|| re(r"(?i)\.(pdf|epub|mobi|az /// imageSet (lib/release/identity.ts): the name up to the number that counts the images, digits kept. pub fn image_set(stem: &str) -> String { // .replace(/(\d+)\s*of\s*\d+/gi, '$1') - let mut s = OF_TOTAL.replace_all(stem.as_bytes(), &b"$1"[..]).into_owned(); + // Suffixes that number no image: a copy's ("…[1]"), a resolution ("…_2000x1500"), a constant one ("…_00"). + let mut s = stem.as_bytes().to_vec(); + for re in [&*COPY_MARK, &*RESOLUTION, &*ZERO_SUFFIX] { + s = re.replace(&s, &b""[..]).into_owned(); + } + // A counter in the name counts the images: the set is the text before it ("Set 008 10of63 new0464"). + if let Some(m) = NAME_COUNTER.find(&s) { + return String::from_utf8_lossy(&s[..m.start()]).into_owned(); + } // A camera's timestamp ("kurozo-2006-7-26-15-21"): the date and the time after it number no set. let s_ts = TIMESTAMP.replace(&s, &b""[..]).into_owned(); s = s_ts; @@ -461,7 +469,10 @@ fn trim_zeros(d: &[u8]) -> &[u8] { } static TIMESTAMP: LazyLock = LazyLock::new(|| re(r"[-_. ]?(?:19|20)[0-9]{2}[-._ ](?:0?[1-9]|1[0-2])[-._ ][0-9]{1,2}(?:[-._ ][0-9]{1,2})*$")); -static OF_TOTAL: LazyLock = LazyLock::new(|| re(r"(?i)([0-9]+)SP*ofSP*[0-9]+")); +static COPY_MARK: LazyLock = LazyLock::new(|| re(r"SP*[\[(][0-9]+[\])]$")); +static RESOLUTION: LazyLock = LazyLock::new(|| re(r"(?i)[_-][0-9]{3,4}x[0-9]{3,4}$")); +static ZERO_SUFFIX: LazyLock = LazyLock::new(|| re(r"[_-]0+$")); +static NAME_COUNTER: LazyLock = LazyLock::new(|| re(r"(?i)([0-9]+)SP*ofSP*[0-9]+|[\[(]?SP*[0-9]+SP*/SP*[0-9]+SP*[\])]?")); fn image_prefix(stem: &str) -> String { let b = stem.as_bytes(); @@ -1187,6 +1198,12 @@ mod image_set_tests { ("108 - Schoolwall kate108lg029_28", "108 - Schoolwall kate108lg"), ("kurozo-2006-7-26-15-21", ""), ("bwp2_037_5", "bwp2_"), + ("Set 008 10of63 new0464", "Set 008 "), + ("202/430 princess-079-058", ""), + ("( 098 /500) 002 148", ""), + ("New Folder (17) sexypattycake_0903040445[1]", "New Folder ("), + ("092_081_2000x1500", "092_"), + ("KP0027_197_00", "KP"), ("JimAndJoey Kindly Husband-to-Be", ""), ] { assert_eq!(image_set(stem), want, "{stem}"); diff --git a/lib/release/identity.ts b/lib/release/identity.ts index d01c15c..3722b8c 100644 --- a/lib/release/identity.ts +++ b/lib/release/identity.ts @@ -146,13 +146,21 @@ const DATES = /\b(?:(?:19|20)\d{2}[-._ ](?:0?[1-9]|1[0-2])[-._ ]\d{1,2}|\d{1,2}[ * "stp250x", "cutie-071-032" → "cutie-071-"); empty for a name without numbers. Suffixes after it that don't * count the images go first: a repeat of the number before (katelg010_10), a year (hgrscans1097_2009), a * size of 4+ digits (…_24of40_1600), and an "of" total (24of40) (LOG.md 2026-10-12 (3)); also a renumbering - * right after it (kate108lg029_28) and a camera's timestamp (kurozo-2006-7-26-15-21) (LOG.md 2026-10-12 (5)). + * right after it (kate108lg029_28) and a camera's timestamp (kurozo-2006-7-26-15-21) (LOG.md 2026-10-12 (5)); a + * counter in the name ends the set, and copy markers, resolutions and "_00" go first (LOG.md 2026-10-12 (6)). */ const TIMESTAMP = /[-_. ]?(?:19|20)\d{2}[-._ ](?:0?[1-9]|1[0-2])[-._ ]\d{1,2}(?:[-._ ]\d{1,2})*$/ export function imageSet(stem: string): string { + // Suffixes that number no image: a copy's ("sexypattycake_0903040445[1]"), a resolution ("092_081_2000x1500"), + // a constant one ("KP0027_197_00"). + const plain = stem.replace(/\s*[[(]\d+[\])]$/, '').replace(/[_-]\d{3,4}x\d{3,4}$/i, '').replace(/[_-]0+$/, '') + // A counter in the name counts the images: the set is the text before it ("Set 008 10of63 new0464", + // "202/430 princess-079-058", "( 098 /500) 002 148"; each image was a release of its own). + const counted = /(\d+)\s*of\s*\d+|[[(]?\s*\d+\s*\/\s*\d+\s*[\])]?/i.exec(plain) + if (counted) return plain.slice(0, counted.index) // A camera's timestamp ("kurozo-2006-7-26-15-21"): the date and the time after it number no set. - let s = stem.replace(/(\d+)\s*of\s*\d+/gi, '$1').replace(TIMESTAMP, '') + let s = plain.replace(TIMESTAMP, '') while (true) { const m = /[_-](\d+)$/.exec(s) if (!m) break diff --git a/tests/unit/release/identity.spec.ts b/tests/unit/release/identity.spec.ts index ac9ac12..d8ce05a 100644 --- a/tests/unit/release/identity.spec.ts +++ b/tests/unit/release/identity.spec.ts @@ -238,6 +238,12 @@ test.group('release / par2 names', () => { ['108 - Schoolwall kate108lg029_28', '108 - Schoolwall kate108lg'], ['kurozo-2006-7-26-15-21', ''], ['bwp2_037_5', 'bwp2_'], + ['Set 008 10of63 new0464', 'Set 008 '], + ['202/430 princess-079-058', ''], + ['( 098 /500) 002 148', ''], + ['New Folder (17) sexypattycake_0903040445[1]', 'New Folder ('], + ['092_081_2000x1500', '092_'], + ['KP0027_197_00', 'KP'], ['JimAndJoey Kindly Husband-to-Be', ''], ]) assert.equal(imageSet(stem), set, stem) })