A folder's images posted with their sizes are one release: no image set under the subject
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e154b2896f
commit
7d9a81784e
3 files changed
+73
-3
No files matched your search
+36
-2
@@ -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
@@ -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
|
||||
|
||||
@@ -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 [
|
||||
|
||||
Reference in new issue
Block a user