A folder's images posted with their sizes are one release: no image set under the subject
Test and deploy / test (push) Successful in 49s
Test and deploy / edge (push) Successful in 1m59s
Test and deploy / deploy (push) Successful in 1m28s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daan MeijerandClaude Opus 5.5 committed 2026-10-16 10:13:20 +02:00
1 parent e154b2896f
commit 7d9a81784e
3 files changed
+73 -3

No files matched your search

+36 -2
View File
@@ -744,8 +744,25 @@ pub fn size_after_name(subject: &str, name: &str) -> String {
}
}
/// A name posted unquoted with its size in bytes right after it, at the subject's end ("… Nude Celebrity Pictures <<<<< -
/// !index0001.jpg 75220 bytes"): a tool that posts a folder under the subject typed for it (sizedName). Not a quoted name.
pub fn sized_name(subject: &str, name: &str) -> bool {
let s = subject.trim_end();
let lower = s.to_ascii_lowercase();
let unit = if lower.ends_with("bytes") { 5 } else if lower.ends_with("byte") { 4 } else { return false };
let s = s[..s.len() - unit].trim_end();
// /\d[\d.,]*$/: from the first digit of the trailing run of digits, dots and commas.
let run = s.len() - s.trim_end_matches(|c: char| c.is_ascii_digit() || c == '.' || c == ',').len();
let tail = &s[s.len() - run..];
let Some(first) = tail.find(|c: char| c.is_ascii_digit()) else { return false };
let s = s[..s.len() - run + first].trim_end();
let s = s.strip_suffix('-').unwrap_or(s).trim_end();
!name.is_empty() && s.ends_with(name) && !s.ends_with(&format!("\"{name}\""))
}
pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release {
let poster = if random_poster(poster) { "" } else { poster };
let sized = sized_name(subject, name);
// The post rule's key takes the [i/M] total; the release's file total any counter's (counterTotal).
let post_total = file_counter_total(subject);
let stem = random_volume_base(file_stem(run_on(name)));
@@ -813,7 +830,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 bound = total.clone().unwrap_or_else(|| if image { image_set(&stem) } else { String::new() });
// A folder's images posted with their sizes: the subject names the folder, the images no set.
let bound = total.clone().unwrap_or_else(|| if image && !sized { image_set(&stem) } else { String::new() });
let mut parts: Vec<&str> = vec!["subject", poster, &remainder, &bound];
// 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();
@@ -1443,7 +1461,7 @@ mod numbered_tests {
#[cfg(test)]
mod image_set_tests {
use super::{release_identity, subject_remainder, subject_remainder_re};
use super::{release_identity, sized_name, subject_remainder, subject_remainder_re};
#[test]
fn random_posters_are_no_part_of_the_key() {
@@ -1502,6 +1520,22 @@ mod image_set_tests {
assert_eq!(mint("JimAndJoey Kindly Husband-to-Be.JPG").key, mint("Oops OnlyOneMore.JPG").key);
}
#[test]
fn a_folders_images_posted_with_their_sizes_are_one_release() {
let celeb = |n: &str, size: u32| release_identity("@_CelebMovie_@", &format!(">>>>> Jamie Lee Curtis - Nude Celebrity Pictures <<<<< - {n} {size} bytes"), n);
let keys: std::collections::HashSet<String> =
["!index0001.jpg", "00-00-02_123_842lo.jpg", "00_00_13.jpg", "bboobs02-curtis-jl.jpg", "0c4_jamie_lee_curtis_15.jpg"].iter().enumerate().map(|(i, n)| celeb(n, 75220 + i as u32).key).collect();
assert_eq!(keys.len(), 1);
assert_eq!(celeb("00_00_13.jpg", 58748).source, "subject");
assert_ne!(celeb("00_00_13.jpg", 1).key, release_identity("@_CelebMovie_@", ">>>>> Carmen Electra - Xmas Nudes <<<<< - 00_00_13.jpg 1 bytes", "00_00_13.jpg").key);
let ctp = |n: &str| release_identity("new@update.org (New CTP update)", &format!("*** NEW *** CTP update & adds - {n} - 18441 bytes"), n);
assert_eq!(ctp("cc_CG2008_v10_01_Stephanie_Emma.jpg").key, ctp("cc_CastingCall_LasVegas_01_Kristin_00_Header1.jpg").key);
assert!(sized_name("x - a.jpg 1,234 Bytes", "a.jpg"));
assert!(!sized_name(r#"x - "a.jpg" yEnc 320794 bytes"#, "a.jpg"));
assert!(!sized_name("x - a.jpg 5 megabytes", "a.jpg"));
assert!(!sized_name("x - a.jpg yEnc", "a.jpg"));
}
#[test]
fn other_quoted_text_and_text_separators_name_the_release() {
let cc = |name: &str, n: &str| release_identity("p", &format!(r#">>> CREATIVE COMMONS NZB <<< "{name} " - File 1 of 9: "creative_commons_nzb_{n}.rar" yEnc"#), &format!("creative_commons_nzb_{n}.rar"));
+21 -1
View File
@@ -332,6 +332,23 @@ export function runOn(name: string): string {
return name
}
/**
* A name posted unquoted with its size in bytes right after it, at the subject's end ("… Nude Celebrity Pictures <<<<< -
* !index0001.jpg 75220 bytes", "*** NEW *** CTP update & adds - cc_CG2008_v10_01_Stephanie_Emma.jpg - 18441 bytes"):
* a tool that posts a folder under the subject typed for it, file by file. Not a quoted name ("The excellent STIRILING
* SILVER - Always popular - "stp250x001.jpg" yEnc 320794 bytes").
*/
export function sizedName(subject: string, name: string): boolean {
let s = subject.trimEnd()
const unit = /bytes?$/i.exec(s)
if (!unit) return false
s = s.slice(0, unit.index).trimEnd()
const digits = /\d[\d.,]*$/.exec(s)
if (!digits) return false
s = s.slice(0, digits.index).trimEnd().replace(/-$/, '').trimEnd()
return name.length > 0 && s.endsWith(name) && !s.endsWith(`"${name}"`)
}
/** 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()
@@ -419,7 +436,10 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
// Without a counter an image's set bounds it: its name up to its last number, digits kept ("stp250x001.jpg"
// → "stp250x": STIRILING SILVER's 300 sets were one release of 44k images); a name without numbers names no
// 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) : ''
// A folder's images posted with their sizes: the subject names the folder, the images no set (a celebrity's
// scraped pictures, "!index0001.jpg", "00_00_13.jpg", "bboobs02-curtis-jl.jpg", were 219 releases of 1,423 images;
// LOG.md 2026-10-16 (63)).
const set = image && !sizedName(input.subject, input.name) ? imageSet(stem) : ''
const parts: (string | number)[] = ['subject', poster, remainder, total ?? set]
// 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
+16
View File
@@ -222,6 +222,22 @@ test.group('release / par2 names', () => {
assert.equal(mint('JimAndJoey Kindly Husband-to-Be.JPG').key, mint('Oops OnlyOneMore.JPG').key)
})
test("a folder's images posted with their sizes are one release: the subject names it", async ({ assert }) => {
const celeb = (n: string, size: number) =>
releaseIdentity({ poster: '@_CelebMovie_@', subject: `>>>>> Jamie Lee Curtis - Nude Celebrity Pictures <<<<< - ${n} ${size} bytes`, name: n, weak: false })!
const keys = new Set(['!index0001.jpg', '00-00-02_123_842lo.jpg', '00_00_13.jpg', 'bboobs02-curtis-jl.jpg', '0c4_jamie_lee_curtis_15.jpg'].map((n, i) => celeb(n, 75220 + i).key))
assert.equal(keys.size, 1)
assert.equal(celeb('00_00_13.jpg', 58748).source, 'subject')
assert.notEqual(celeb('00_00_13.jpg', 1).key, releaseIdentity({ poster: '@_CelebMovie_@', subject: '>>>>> Carmen Electra - Xmas Nudes <<<<< - 00_00_13.jpg 1 bytes', name: '00_00_13.jpg', weak: false })!.key)
const ctp = (n: string) => releaseIdentity({ poster: 'new@update.org (New CTP update)', subject: `*** NEW *** CTP update & adds - ${n} - 18441 bytes`, name: n, weak: false })!
assert.equal(ctp('cc_CG2008_v10_01_Stephanie_Emma.jpg').key, ctp('cc_CastingCall_LasVegas_01_Kristin_00_Header1.jpg').key)
const { sizedName } = await import('#lib/release/identity')
assert.isTrue(sizedName('x - a.jpg 1,234 Bytes', 'a.jpg'))
assert.isFalse(sizedName('x - "a.jpg" yEnc 320794 bytes', 'a.jpg'))
assert.isFalse(sizedName('x - a.jpg 5 megabytes', 'a.jpg'))
assert.isFalse(sizedName('x - a.jpg yEnc', 'a.jpg'))
})
test("an image's set: suffixes that don't count the images go first", async ({ assert }) => {
const { imageSet } = await import('#lib/release/identity')
for (const [stem, set] of [