Nothing enters the catalog without a usable name: the pending store (decision f amended)
An unnamed release (no readable file name) is written to pending_releases (migration 6300, the HDD) with its keys, counts, article span, upload tool and probe targets, instead of releases, release_texts, posters and a chunk. The edge sends it with an empty payload and its tool; the batch store splits a batch by readable. The pending ladder (probes cheapest first, signature verdicts, the move into the catalog) comes next. The replay report shows the pending releases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
6860e6ccaf
commit
9cf85e09d8
8 files changed
+286
-15
No files matched your search
+34
-2
@@ -53,6 +53,11 @@ pub struct ReleaseChunk {
|
||||
pub edge: bool,
|
||||
/// A file misses parts.
|
||||
pub partial: bool,
|
||||
/// The upload tool from the first article's message-id (names::upload_tool), for an unnamed release: with the
|
||||
/// group its signature, what the pending ladder learns verdicts by. None for a named one.
|
||||
pub tool: Option<&'static str>,
|
||||
/// The files' names and segments (codec); empty for an unnamed release (decision f: nothing in the catalog
|
||||
/// without a usable name; the pending store keeps its keys and probe targets until a probe names it).
|
||||
pub payload: Vec<u8>,
|
||||
/// The first files' subjects (at most SUBJECTS): edge-bench's --releases dump for the grouping sweeps.
|
||||
pub subjects: Vec<String>,
|
||||
@@ -1154,17 +1159,26 @@ impl RangeIndexer {
|
||||
let mut chunk = chunk_of(key, &list, upload);
|
||||
chunk.finished = finished;
|
||||
chunk.edge = edge;
|
||||
let album_none = album.is_none();
|
||||
if let Some(name) = album {
|
||||
chunk.name = name;
|
||||
chunk.source = "album";
|
||||
chunk.file_total = None;
|
||||
} else if let Some(total) = chunk.file_total.filter(|&t| t >= BATCH_COUNTER_MIN_TOTAL && (chunk.files as i64) < t) {
|
||||
}
|
||||
if !chunk.readable {
|
||||
// Unnamed: no chunk (its segments are read again once a probe names it), the tool for its signature.
|
||||
chunk.tool = Some(names::upload_tool(&list[0][0].message_id));
|
||||
chunk.payload = encode_chunk(&[]);
|
||||
}
|
||||
if chunk.readable {
|
||||
self.pending_files += chunk.files;
|
||||
}
|
||||
if let Some(total) = chunk.file_total.filter(|&t| album_none && t >= BATCH_COUNTER_MIN_TOTAL && (chunk.files as i64) < t) {
|
||||
// A batch counter: no file total (the release is as complete as its files are).
|
||||
if self.batch_totals.get(&(chunk.poster.clone(), total)).is_some_and(|(n, _)| *n >= BATCH_COUNTER_RELEASES) {
|
||||
chunk.file_total = None;
|
||||
}
|
||||
}
|
||||
self.pending_files += chunk.files;
|
||||
self.batch.releases.push(chunk);
|
||||
self.batch.candidates.extend(candidates);
|
||||
}
|
||||
@@ -1267,6 +1281,7 @@ fn chunk_of(key: &str, files: &[Vec<Seg>], upload: usize) -> ReleaseChunk {
|
||||
finished: true,
|
||||
edge: false,
|
||||
partial: files.iter().any(|f| (f.len() as u32) < f[0].file.total_parts),
|
||||
tool: None,
|
||||
payload: encode_chunk(&chunk_files),
|
||||
subjects: chunk_files.iter().take(SUBJECTS).map(|f| f.subject.to_string()).collect(),
|
||||
}
|
||||
@@ -1411,6 +1426,23 @@ mod tests {
|
||||
assert!(ix.albums.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unnamed_release_keeps_no_chunk_but_its_tool_and_probe_target() {
|
||||
let mut priorities = FxHashMap::default();
|
||||
priorities.insert(GROUP.to_string(), 1);
|
||||
let mut ix = RangeIndexer::new(GROUP, 1, 10_000, 1, Rules::defaults(priorities), 5);
|
||||
let lines: Vec<String> = (0..4).map(|i| line(100 + i, &format!(r#"[1/1] - "q3B9OrpKiCxUnzgtUGnp.part01.rar" yEnc ({}/4)"#, i + 1), i)).collect();
|
||||
ix.add_text(&lines.join("\n"), 10_001);
|
||||
ix.finish();
|
||||
let batch = ix.take();
|
||||
assert_eq!(batch.releases.len(), 1);
|
||||
let r = &batch.releases[0];
|
||||
assert!(!r.readable && r.tool == Some("other") && r.files == 1 && r.segments == 4);
|
||||
assert_eq!(r.payload, encode_chunk(&[]));
|
||||
assert_eq!(batch.candidates.iter().map(|c| c.kind).collect::<Vec<_>>(), vec!["archive"]);
|
||||
assert_eq!(ix.pending(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn album_templates_and_names() {
|
||||
assert_eq!(album_template("1481439079025_014.jpg.data").as_deref(), Some("#_#.jpg.data"));
|
||||
|
||||
@@ -27,6 +27,8 @@ struct WireRelease<'a> {
|
||||
finished: bool,
|
||||
edge: bool,
|
||||
partial: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
tool: Option<&'a str>,
|
||||
payload: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
rule: Option<&'a str>,
|
||||
@@ -87,6 +89,7 @@ fn release<'a>(r: &'a ReleaseChunk, rule: Option<(&'a str, &'a str)>) -> WireRel
|
||||
finished: r.finished,
|
||||
edge: r.edge,
|
||||
partial: r.partial,
|
||||
tool: r.tool,
|
||||
payload: base64::engine::general_purpose::STANDARD.encode(&r.payload),
|
||||
rule: rule.map(|(rule, _)| rule),
|
||||
rule_key: rule.map(|(_, key)| key),
|
||||
|
||||
Reference in new issue
Block a user