Numbered items of a set don't take the poster-tag rule
"ai-engineer-complete [401/465] - \"003 Defining Tools with JSON Schema.mp4\"": the subject's text shares no word with the lesson's descriptive name, so it was taken for a poster's constant tag and each of the course's 465 files keyed a release of its own (releases 75941915, 75941916). A stem starting with a 1-3 digit index and a separator is an item of a set the subject names, as audio tracks and images already were; uploads hours apart stay apart by the re-upload rule. Both implementations; tests; fixtures identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
90f30f445d
commit
47b445ffb0
3 files changed
+34
-3
No files matched your search
+21
-1
@@ -524,7 +524,13 @@ pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release {
|
|||||||
}
|
}
|
||||||
let image = test(&IMAGE, name);
|
let image = test(&IMAGE, name);
|
||||||
// The poster's constant tag outside the quotes: the file's stem says which upload it is (not for images and audio).
|
// The poster's constant tag outside the quotes: the file's stem says which upload it is (not for images and audio).
|
||||||
let tag = !image && !test(&AUDIO, name) && describes(&stem) && !looks_random(&stem) && is_poster_tag(&remainder, &stem);
|
// Not for numbered items of a set ("003 Defining Tools with JSON Schema.mp4"): /^\d{1,3}[ ._-]/.
|
||||||
|
let numbered = {
|
||||||
|
let b = stem.as_bytes();
|
||||||
|
let digits = b.iter().take_while(|c| c.is_ascii_digit()).count();
|
||||||
|
(1..=3).contains(&digits) && matches!(b.get(digits), Some(b' ' | b'.' | b'_' | b'-'))
|
||||||
|
};
|
||||||
|
let tag = !image && !numbered && !test(&AUDIO, name) && describes(&stem) && !looks_random(&stem) && is_poster_tag(&remainder, &stem);
|
||||||
let prefix = if image { image_prefix(&stem) } else { String::new() };
|
let prefix = if image { image_prefix(&stem) } else { String::new() };
|
||||||
if !tag && names_release(&remainder) {
|
if !tag && names_release(&remainder) {
|
||||||
let bound = counter_total(subject).unwrap_or_else(|| prefix.clone());
|
let bound = counter_total(subject).unwrap_or_else(|| prefix.clone());
|
||||||
@@ -1004,4 +1010,18 @@ mod tests {
|
|||||||
assert_eq!(upload_tool("b94268bc702040f7b811d5318f727e4f@ngPost"), "ngPost");
|
assert_eq!(upload_tool("b94268bc702040f7b811d5318f727e4f@ngPost"), "ngPost");
|
||||||
assert_eq!(upload_tool("x@y"), "other");
|
assert_eq!(upload_tool("x@y"), "other");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod numbered_tests {
|
||||||
|
use super::release_identity;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn numbered_items_of_a_set_stay_one_release() {
|
||||||
|
let poster = "Poster49118 <Poster49118@generic.mail>";
|
||||||
|
let a = release_identity(poster, r#"ai-engineer-complete [401/465] - "003 Defining Tools with JSON Schema.mp4" yEnc"#, "003 Defining Tools with JSON Schema.mp4");
|
||||||
|
let b = release_identity(poster, r#"ai-engineer-complete [422/465] - "006 ai-lesson-4-5-web-search-tools.zip" yEnc"#, "006 ai-lesson-4-5-web-search-tools.zip");
|
||||||
|
assert_eq!(a.key, b.key);
|
||||||
|
assert_eq!(a.name, "ai engineer complete");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -209,8 +209,11 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
|||||||
// release the file is (LOG.md 2026-10-09 (8)). Only when the stem itself describes something
|
// release the file is (LOG.md 2026-10-09 (8)). Only when the stem itself describes something
|
||||||
// ("Cephalgy.-.Weisses.Fleisch.2003", not a scene abbreviation like "grp-sm" under
|
// ("Cephalgy.-.Weisses.Fleisch.2003", not a scene abbreviation like "grp-sm" under
|
||||||
// "Some.Movie.2008-GRP", nor "file_id.diz"), and not for images and audio tracks, which the
|
// "Some.Movie.2008-GRP", nor "file_id.diz"), and not for images and audio tracks, which the
|
||||||
// subject names as a set or album ("IMG_3086.jpg", "072 - Die Teufelsuhr.MP3").
|
// subject names as a set or album ("IMG_3086.jpg", "072 - Die Teufelsuhr.MP3"), nor for numbered items of
|
||||||
const tag = !image && !AUDIO.test(input.name) && describes(stem) && !looksRandom(stem) && isPosterTag(remainder, stem)
|
// 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.
|
||||||
|
const numbered = /^\d{1,3}[ ._-]/.test(stem)
|
||||||
|
const tag = !image && !numbered && !AUDIO.test(input.name) && describes(stem) && !looksRandom(stem) && isPosterTag(remainder, stem)
|
||||||
// An image set without a counter: the image's name up to its last number, without numbers
|
// An image set without a counter: the image's name up to its last number, without numbers
|
||||||
// ("Kitchen Hottie 1 set233-76-lg" → "Kitchen Hottie set", "bm_jessica_blb_h087" → "bm jessica blb h").
|
// ("Kitchen Hottie 1 set233-76-lg" → "Kitchen Hottie set", "bm_jessica_blb_h087" → "bm jessica blb h").
|
||||||
const prefix = image ? stem.replace(/\d+\D*$/, '').replace(/\d+/g, ' ').replace(/[\s._-]+/g, ' ').trim() : ''
|
const prefix = image ? stem.replace(/\d+\D*$/, '').replace(/\d+/g, ' ').replace(/[\s._-]+/g, ' ').trim() : ''
|
||||||
|
|||||||
@@ -175,6 +175,14 @@ test.group('release / par2 names', () => {
|
|||||||
assert.equal(par2ReleaseName(['Album.Name-2024-GRP.part01.rar', 'Album.Name-2024-GRP.part02.rar']), 'Album.Name-2024-GRP')
|
assert.equal(par2ReleaseName(['Album.Name-2024-GRP.part01.rar', 'Album.Name-2024-GRP.part02.rar']), 'Album.Name-2024-GRP')
|
||||||
assert.equal(par2ReleaseName(['only.one.file.2024.mkv']), 'only.one.file.2024')
|
assert.equal(par2ReleaseName(['only.one.file.2024.mkv']), 'only.one.file.2024')
|
||||||
})
|
})
|
||||||
|
test('numbered items of a set stay one release under the set name (a course, not a poster tag)', ({ assert }) => {
|
||||||
|
const id = (subject: string, name: string) => releaseIdentity({ poster: 'Poster49118 <Poster49118@generic.mail>', subject, name, weak: false })!
|
||||||
|
const a = id('ai-engineer-complete [401/465] - "003 Defining Tools with JSON Schema.mp4" yEnc', '003 Defining Tools with JSON Schema.mp4')
|
||||||
|
const b = id('ai-engineer-complete [422/465] - "006 ai-lesson-4-5-web-search-tools.zip" yEnc', '006 ai-lesson-4-5-web-search-tools.zip')
|
||||||
|
assert.equal(a.key, b.key)
|
||||||
|
assert.equal(a.name, 'ai engineer complete')
|
||||||
|
})
|
||||||
|
|
||||||
test("a poster's constant tag doesn't make one release of all their uploads", ({ assert }) => {
|
test("a poster's constant tag doesn't make one release of all their uploads", ({ assert }) => {
|
||||||
const id = (poster: string, subject: string, name: string) => releaseIdentity({ poster, subject, name, weak: false })!
|
const id = (poster: string, subject: string, name: string) => releaseIdentity({ poster, subject, name, weak: false })!
|
||||||
// [KiLL…] tag on every upload: each album by its stem, its nfo included.
|
// [KiLL…] tag on every upload: each album by its stem, its nfo included.
|
||||||
|
|||||||
Reference in new issue
Block a user