Compare commits
2
Commits
8cf965af80
...
3f0b5b8be1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f0b5b8be1 | ||
|
|
c6f9409e7f |
No files matched your search
+2
-2
@@ -882,8 +882,8 @@ f. **Nothing enters the catalog without a usable name** (amended
|
||||
chunks) or by XOVER on a connection of its own, through the TypeScript
|
||||
indexer with `partialFiles` (a file given up short of parts stays a
|
||||
partial file of its release, as the Rust window writes it; without it
|
||||
the range path's fragment rule left nothing to find, and 10 of 148k named
|
||||
releases were moved by 2026-10-29), written as a release under the pending key with `source`
|
||||
the range path's fragment rule left nothing to find: by 2026-10-29
|
||||
about 52k of 210k named releases failed, all partial), written as a release under the pending key with `source`
|
||||
weak-stem, the name as `inner_name`, its chunk and its candidates (the
|
||||
prober answers those from `probe_cache`: inspections, par2 sets, hashes,
|
||||
the password flag). Not built: the spot/NZB match on pending keys (step
|
||||
|
||||
+51
-2
@@ -449,6 +449,43 @@ fn finish_remainder(s: &str) -> String {
|
||||
js::trim(&out).to_string()
|
||||
}
|
||||
|
||||
/// displayName: a subject name as shown, brackets the counters left unmatched removed ("… Facialized 002/477] - …"
|
||||
/// gave "… Facialized ]"). Only the name: the key keeps the remainder.
|
||||
pub fn display_name(remainder: &str) -> String {
|
||||
let b = remainder.as_bytes();
|
||||
if !b.iter().any(|c| matches!(c, b'[' | b']' | b'(' | b')')) {
|
||||
return remainder.to_string();
|
||||
}
|
||||
let mut drop = vec![false; b.len()];
|
||||
for (open, close) in [(b'[', b']'), (b'(', b')')] {
|
||||
let mut opens: Vec<usize> = Vec::new();
|
||||
for (i, &c) in b.iter().enumerate() {
|
||||
if c == open {
|
||||
opens.push(i);
|
||||
} else if c == close && opens.pop().is_none() {
|
||||
drop[i] = true;
|
||||
}
|
||||
}
|
||||
for i in opens {
|
||||
drop[i] = true;
|
||||
}
|
||||
}
|
||||
if !drop.contains(&true) {
|
||||
return remainder.to_string();
|
||||
}
|
||||
let kept: Vec<u8> = b.iter().zip(&drop).filter(|(_, &d)| !d).map(|(&c, _)| c).collect();
|
||||
let kept = String::from_utf8(kept).expect("only ASCII brackets removed");
|
||||
let mut out = String::with_capacity(kept.len());
|
||||
for c in kept.chars() {
|
||||
if c == ' ' && out.ends_with(' ') {
|
||||
continue;
|
||||
}
|
||||
out.push(c);
|
||||
}
|
||||
let name = js::trim(&out);
|
||||
if name.is_empty() { remainder.to_string() } else { name.to_string() }
|
||||
}
|
||||
|
||||
/// counterTotal: the subject's file counter total, first form found first.
|
||||
pub fn counter_total(subject: &str) -> Option<String> {
|
||||
let s = replace("ED, subject, " ", true);
|
||||
@@ -841,11 +878,11 @@ pub fn release_identity(poster: &str, subject: &str, name: &str) -> Release {
|
||||
if !image && test(&RANDOM_VOLUME, name) && stem.bytes().all(|c| c.is_ascii_alphanumeric() || c == b'_' || c == b'-') && looks_random(&stripped) {
|
||||
parts.push(&stem);
|
||||
}
|
||||
return Release { key: hash(&parts), name: remainder, source: "subject", file_total };
|
||||
return Release { key: hash(&parts), name: display_name(&remainder), source: "subject", file_total };
|
||||
}
|
||||
if let Some(total) = post_total.filter(|&n| n != 0 && !tag) {
|
||||
if is_post_token(&remainder) {
|
||||
return Release { key: hash(&["post", poster, &remainder, &total.to_string()]), name: remainder, source: "post", file_total };
|
||||
return Release { key: hash(&["post", poster, &remainder, &total.to_string()]), name: display_name(&remainder), source: "post", file_total };
|
||||
}
|
||||
}
|
||||
// A scene music post's index files ("00-ammar-turk_uit_de_kroeg-single-web-nl-2022-cover.jpg") under a bare counter go
|
||||
@@ -1339,6 +1376,18 @@ mod tests {
|
||||
assert_eq!(file_stem_re("Morning Lovers.wmv.058"), "Morning Lovers");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_names() {
|
||||
let porn4you = "webmaster@hdbangvidz.com (Porn4You)";
|
||||
let a = release_identity(porn4you, "Babe with Plump Bubble Butt Fucked and Facialized 002/477] - 0178.jpg", "0178.jpg");
|
||||
let b = release_identity(porn4you, "Babe with Plump Bubble Butt Fucked and Facialized 003/477] - 0179.jpg", "0179.jpg");
|
||||
assert_eq!(a.name, "Babe with Plump Bubble Butt Fucked and Facialized");
|
||||
assert_eq!(a.key, b.key);
|
||||
assert_eq!(display_name("[Group] Show (2020)"), "[Group] Show (2020)");
|
||||
assert_eq!(display_name("( Show [x] name"), "Show [x] name");
|
||||
assert_eq!(display_name("]"), "]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remainders() {
|
||||
assert_eq!(subject_remainder(r#"Some.Release [01/45] - "x.rar" yEnc (1/50) 7168"#), "Some Release");
|
||||
|
||||
+25
-2
@@ -105,6 +105,29 @@ export function subjectRemainder(subject: string): string {
|
||||
.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* A subject name as shown: brackets the counters left unmatched removed ("… Facialized 002/477] - 0177.jpg", the
|
||||
* counter's "[" missing, gave "… Facialized ]"; LOG.md 2026-10-29 (121)). Only the name: the key keeps the remainder.
|
||||
*/
|
||||
export function displayName(remainder: string): string {
|
||||
if (!/[[\]()]/.test(remainder)) return remainder
|
||||
const drop = new Set<number>()
|
||||
for (const [open, close] of [['[', ']'], ['(', ')']]) {
|
||||
const opens: number[] = []
|
||||
for (let i = 0; i < remainder.length; i++) {
|
||||
if (remainder[i] === open) opens.push(i)
|
||||
else if (remainder[i] === close) {
|
||||
if (opens.length > 0) opens.pop()
|
||||
else drop.add(i)
|
||||
}
|
||||
}
|
||||
for (const i of opens) drop.add(i)
|
||||
}
|
||||
if (drop.size === 0) return remainder
|
||||
const name = remainder.split('').filter((_, i) => !drop.has(i)).join('').replace(/ {2,}/g, ' ').trim()
|
||||
return name || remainder
|
||||
}
|
||||
|
||||
/**
|
||||
* Posting tools put random per-file tokens and poster IDs in subjects (e.g.
|
||||
* `elys-de81bcabef5faf6a - [Nyh5Q07Y]`); only text that still reads like a
|
||||
@@ -448,7 +471,7 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
if (!image && RANDOM_VOLUME.test(input.name) && /^[A-Za-z0-9_-]+$/.test(stem) && looksRandom(stem.replace(/[-_]/g, ''))) parts.push(stem)
|
||||
return {
|
||||
key: hash(...parts),
|
||||
name: remainder,
|
||||
name: displayName(remainder),
|
||||
fileTotal,
|
||||
source: 'subject',
|
||||
}
|
||||
@@ -456,7 +479,7 @@ export function releaseIdentity(input: ReleaseInput): ReleaseIdentity | null {
|
||||
// Not a name, but a post id (e.g. `83110629-n [10/18] - "random"`): with the
|
||||
// [i/M] counter it groups the files of one post whose names are random.
|
||||
if (!tag && postTotal && isPostToken(remainder)) {
|
||||
return { key: hash('post', poster, remainder, postTotal), name: remainder, fileTotal, source: 'post' }
|
||||
return { key: hash('post', poster, remainder, postTotal), name: displayName(remainder), fileTotal, source: 'post' }
|
||||
}
|
||||
// A scene music post's index files under a bare counter go with its tracks: the artist and the total ("[01/13] -
|
||||
// "00-ammar-turk_uit_de_kroeg-single-web-nl-2022-cover.jpg"" with "[05/13] - "01-ammar-turk_uit_de_kroeg.mp3"": the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Grouping of files into releases (downloads), independent of AdonisJS.
|
||||
*/
|
||||
export { releaseIdentity, par2ReleaseIdentity, par2ReleaseName, rarReleaseIdentity, isGenericName, fileStem, sizeAfterName, runOn, subjectRemainder, namesRelease, isPostToken, type ReleaseIdentity, type ReleaseInput } from './identity.js'
|
||||
export { releaseIdentity, displayName, par2ReleaseIdentity, par2ReleaseName, rarReleaseIdentity, isGenericName, fileStem, sizeAfterName, runOn, subjectRemainder, namesRelease, isPostToken, type ReleaseIdentity, type ReleaseInput } from './identity.js'
|
||||
export { looksRandom } from './random.js'
|
||||
@@ -1,5 +1,5 @@
|
||||
import { test } from '@japa/runner'
|
||||
import { fileStem, releaseIdentity, runOn, sizeAfterName, subjectRemainder } from '#lib/release/index'
|
||||
import { displayName, fileStem, releaseIdentity, runOn, sizeAfterName, subjectRemainder } from '#lib/release/index'
|
||||
import { looksLikeFilename, weakFileKey } from '#lib/headers/index'
|
||||
|
||||
test.group('release / fileStem', () => {
|
||||
@@ -604,3 +604,15 @@ test.group('release / par2 names', () => {
|
||||
assert.equal(id('[2/6] - "x.jpg" yEnc', 'x.jpg').key, id('[1/6] - "x.part01.rar" yEnc', 'x.part01.rar').key)
|
||||
})
|
||||
})
|
||||
|
||||
test.group('release / displayName', () => {
|
||||
test('brackets the counters left unmatched leave the name, the key keeps them', ({ assert }) => {
|
||||
const subject = (i: number) => `Babe with Plump Bubble Butt Fucked and Facialized ${String(i).padStart(3, '0')}/477] - 0${176 + i}.jpg`
|
||||
const id = (i: number) => releaseIdentity({ poster: 'webmaster@hdbangvidz.com (Porn4You)', weak: false, subject: subject(i), name: `0${176 + i}.jpg` })!
|
||||
assert.equal(id(2).name, 'Babe with Plump Bubble Butt Fucked and Facialized')
|
||||
assert.equal(id(2).key, id(3).key)
|
||||
assert.equal(displayName('[Group] Show (2020)'), '[Group] Show (2020)')
|
||||
assert.equal(displayName('( Show [x] name'), 'Show [x] name')
|
||||
assert.equal(displayName(']'), ']')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user