Images and tracks named by a prefix alone are bounded by the counter's total
"[…/559] - "lop1001.jpg"", "pic004.jpg", "DSC_0002[1].jpg", and tracks
"r86_022 - Gwen Guthrie - ….mp3" ("r"): the stem route keyed them by the
letters before the number alone, so every post of a poster was one release
(5,731 images for 559; 6,433 tracks for 457). Under a counter its total is
part of the key; a cover named as its archive's stem keeps the stem's key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3f6bb8b162
commit
9f255e6ac0
3 files changed
+43
-4
No files matched your search
+23
-2
@@ -847,7 +847,16 @@ pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release {
|
||||
// Under a post token ("MDG005b - (01/90) "IMG_2307.JPG""): the post's too.
|
||||
// A word in it, not a size ("… alexis001.jpg 57295b").
|
||||
let post = is_post_token(&remainder) && remainder.as_bytes().windows(2).any(|w| w[0].is_ascii_alphabetic() && w[1].is_ascii_alphabetic());
|
||||
let key = if post { hash(&["stem", poster, &set, &remainder]) } else { hash(&["stem", poster, &set]) };
|
||||
// Under a counter, bounded by its total ("lop1001.jpg", "pic004.jpg": every post of the poster one release).
|
||||
let mut parts: Vec<&str> = vec!["stem", poster, &set];
|
||||
if post {
|
||||
parts.push(&remainder);
|
||||
}
|
||||
// Not a cover named as its archive's stem ("x.jpg" beside "x.part01.rar").
|
||||
if let Some(t) = total.as_deref().filter(|_| set != stem) {
|
||||
parts.push(t);
|
||||
}
|
||||
let key = hash(&parts);
|
||||
return Release { key, name, source: "stem", file_total };
|
||||
}
|
||||
// An audiobook's or album's tracks without a name around them: by their name without its numbers.
|
||||
@@ -861,7 +870,12 @@ pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release {
|
||||
}
|
||||
let tracks = image_prefix(&stem);
|
||||
if !tracks.is_empty() {
|
||||
return Release { key: hash(&["stem", poster, &tracks]), name: tracks, source: "stem", file_total };
|
||||
// Bounded by a counter's total too ("r86_022 - Gwen Guthrie - … .mp3" → "r").
|
||||
let key = match total.as_deref() {
|
||||
Some(t) => hash(&["stem", poster, &tracks, t]),
|
||||
None => hash(&["stem", poster, &tracks]),
|
||||
};
|
||||
return Release { key, name: tracks, source: "stem", file_total };
|
||||
}
|
||||
}
|
||||
// A season's episodes under a bare counter ("Pokemon - S02E01 - Pallet Party Panic.mkv"): per show, season and total.
|
||||
@@ -1546,6 +1560,13 @@ mod image_set_tests {
|
||||
);
|
||||
let jb = |i: &str, n: &str| super::release_identity("JBinUp", &format!(r#"[{i}/58] - Summer Power Up - "{n}" yEnc"#), n);
|
||||
assert_eq!(jb("01", "www.realmom.info - XVID- Farbiges Krabbeltier.r00").key, jb("54", "www.realmom.info - XVID-Farbiges Krabbeltier.vol03+04.par2").key);
|
||||
let lop = |i: &str, total: &str, n: &str| super::release_identity("p", &format!(r#"[{i}/{total}] - "{n}" yEnc"#), n);
|
||||
assert_eq!(lop("001", "559", "lop1001.jpg").key, lop("002", "559", "lop1002.jpg").key);
|
||||
assert_ne!(lop("001", "559", "lop1001.jpg").key, lop("001", "1018", "lop5001.jpg").key);
|
||||
assert_ne!(
|
||||
lop("022", "457", "r86_022 - Gwen Guthrie - Ain't Nothing Going on but the Rent.mp3").key,
|
||||
lop("022", "333", "r98_333 - Jellie - Roller Coaster.mp3").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);
|
||||
|
||||
+10
-2
@@ -437,7 +437,13 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
const set = imageSet(stem) || prefix || stem
|
||||
// A word in it, not a size ("01of37 ## Alexi … ## alexis001.jpg 57295b": each image was a release of its own).
|
||||
const post = isPostToken(remainder) && /[A-Za-z]{2}/.test(remainder)
|
||||
return { key: post ? hash('stem', poster, set, remainder) : hash('stem', poster, set), name: prefix || stem, fileTotal, source: 'stem' }
|
||||
// Under a counter, bounded by its total: a set named only by its prefix ("lop1001.jpg", "pic004.jpg", "DSC_0002[1].jpg")
|
||||
// made every post of the poster one release (5,731 images for 559; LOG.md 2026-10-13 (50)).
|
||||
const parts: (string | number)[] = ['stem', poster, set]
|
||||
if (post) parts.push(remainder)
|
||||
// Not a cover named as its archive's stem ("x.jpg" beside "x.part01.rar"): that keeps the stem's key.
|
||||
if (fileTotal !== undefined && set !== stem) parts.push(fileTotal)
|
||||
return { key: hash(...parts), name: prefix || stem, fileTotal, source: 'stem' }
|
||||
}
|
||||
// An audiobook's or album's tracks without a name around them: by their name without its numbers, as an
|
||||
// image set ("… Seeing A Large Cat - D11.16-22.mp3", "Candy Dulfer - Candy Store - 11 - everytime.mp3").
|
||||
@@ -447,7 +453,9 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
const scene = postTotal ? SCENE_TRACK.exec(stem) : null
|
||||
if (scene) return { key: hash('stem', poster, scene[1], postTotal), name: scene[1], fileTotal, source: 'stem' }
|
||||
const tracks = stem.replace(/\d+\D*$/, '').replace(/\d+/g, ' ').replace(/[\s._-]+/g, ' ').trim()
|
||||
if (tracks) return { key: hash('stem', poster, tracks), name: tracks, fileTotal, source: 'stem' }
|
||||
// Bounded by a counter's total too ("r86_022 - Gwen Guthrie - Ain't Nothing….mp3" → "r": a chart's 457 tracks a post,
|
||||
// every post one release).
|
||||
if (tracks) return { key: fileTotal !== undefined ? hash('stem', poster, tracks, fileTotal) : hash('stem', poster, tracks), name: tracks, fileTotal, source: 'stem' }
|
||||
}
|
||||
// A season's episodes under a bare counter ("[01/36] "Pokemon - S02E01 - Pallet Party Panic.mkv""): one release per
|
||||
// show, season and counter total (each episode was a release of its own).
|
||||
|
||||
@@ -433,6 +433,16 @@ test.group('release / par2 names', () => {
|
||||
assert.equal(alexi('01', '57295b').key, alexi('02', '32202b').key)
|
||||
})
|
||||
|
||||
test('a set named by its prefix alone is bounded by its counter', ({ assert }) => {
|
||||
const id = (subject: string, name: string) => releaseIdentity({ poster: 'p', subject, name, weak: false })!
|
||||
const lop = (i: string, total: string, n: string) => id(`[${i}/${total}] - "${n}" yEnc`, n)
|
||||
assert.equal(lop('001', '559', 'lop1001.jpg').key, lop('002', '559', 'lop1002.jpg').key)
|
||||
assert.notEqual(lop('001', '559', 'lop1001.jpg').key, lop('001', '1018', 'lop5001.jpg').key)
|
||||
const r = (i: string, total: string, n: string) => id(`[${i}/${total}] - "${n}" yEnc`, n)
|
||||
assert.equal(r('022', '457', "r86_022 - Gwen Guthrie - Ain't Nothing Going on but the Rent.mp3").key, r('085', '457', "r88_085 - Gregory Abbott - I'll Prove It to You.mp3").key)
|
||||
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('an image without words around it is keyed by its set', ({ assert }) => {
|
||||
const id = (name: string, counter: string) => releaseIdentity({ poster: 'p', subject: `##[ "${name}" (${counter})`, name, weak: false })!
|
||||
assert.equal(id('diana-040-001.jpg', '01/68').key, id('diana-040-002.jpg', '02/68').key)
|
||||
|
||||
Reference in new issue
Block a user