Ensure we re-queue pending/failed records
This commit is contained in:
83
src/main.rs
83
src/main.rs
@@ -750,6 +750,20 @@ async fn get_preview_status(
|
||||
|
||||
for path in &body.paths {
|
||||
if let Some(clip) = clip_map.get(path) {
|
||||
// Re-queue generation for stale pending/failed records
|
||||
if clip.status == "pending" || clip.status == "failed" {
|
||||
let full_path = format!(
|
||||
"{}/{}",
|
||||
app_state.base_path.trim_end_matches(['/', '\\']),
|
||||
path.trim_start_matches(['/', '\\'])
|
||||
);
|
||||
app_state
|
||||
.preview_clip_generator
|
||||
.do_send(GeneratePreviewClipMessage {
|
||||
video_path: full_path,
|
||||
});
|
||||
}
|
||||
|
||||
items.push(PreviewStatusItem {
|
||||
path: path.clone(),
|
||||
status: clip.status.clone(),
|
||||
@@ -1653,9 +1667,10 @@ fn process_new_files(
|
||||
for (full_path, relative_path) in &video_files {
|
||||
let status = existing_previews.get(relative_path).map(|s| s.as_str());
|
||||
let needs_preview = match status {
|
||||
None => true, // No record at all
|
||||
Some("failed") => true, // Retry failed
|
||||
_ => false, // pending, processing, or complete
|
||||
None => true, // No record at all
|
||||
Some("failed") => true, // Retry failed
|
||||
Some("pending") => true, // Stale pending from previous run
|
||||
_ => false, // processing or complete
|
||||
};
|
||||
|
||||
if needs_preview {
|
||||
@@ -1851,4 +1866,66 @@ mod tests {
|
||||
assert_eq!(previews[2]["path"], "c.mp4");
|
||||
assert_eq!(previews[2]["status"], "pending");
|
||||
}
|
||||
|
||||
/// Verifies that the status endpoint re-queues generation for stale
|
||||
/// "pending" and "failed" records (e.g., after a server restart or
|
||||
/// when clip files were deleted). The do_send to the actor exercises
|
||||
/// the re-queue code path; the actor runs against temp dirs so it
|
||||
/// won't panic.
|
||||
#[actix_rt::test]
|
||||
async fn test_get_preview_status_requeues_pending_and_failed() {
|
||||
let mut dao = TestPreviewDao::new();
|
||||
let ctx = opentelemetry::Context::new();
|
||||
|
||||
// Simulate stale records left from a previous server run
|
||||
dao.insert_preview(&ctx, "stale/pending.mp4", "pending")
|
||||
.unwrap();
|
||||
dao.insert_preview(&ctx, "stale/failed.mp4", "pending")
|
||||
.unwrap();
|
||||
dao.update_status(
|
||||
&ctx,
|
||||
"stale/failed.mp4",
|
||||
"failed",
|
||||
None,
|
||||
None,
|
||||
Some("ffmpeg error"),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let preview_dao = make_preview_dao(dao);
|
||||
let app_state = Data::new(AppState::test_state());
|
||||
let token = make_token();
|
||||
|
||||
let app = actix_web::test::init_service(
|
||||
App::new()
|
||||
.service(get_preview_status)
|
||||
.app_data(app_state)
|
||||
.app_data(preview_dao),
|
||||
)
|
||||
.await;
|
||||
|
||||
let req = actix_web::test::TestRequest::post()
|
||||
.uri("/video/preview/status")
|
||||
.insert_header(("Authorization", format!("Bearer {}", token)))
|
||||
.set_json(serde_json::json!({
|
||||
"paths": ["stale/pending.mp4", "stale/failed.mp4"]
|
||||
}))
|
||||
.to_request();
|
||||
|
||||
let resp = actix_web::test::call_service(&app, req).await;
|
||||
assert_eq!(resp.status(), 200);
|
||||
|
||||
let body: serde_json::Value = actix_web::test::read_body_json(resp).await;
|
||||
let previews = body["previews"].as_array().unwrap();
|
||||
assert_eq!(previews.len(), 2);
|
||||
|
||||
// Both records are returned with their current status
|
||||
assert_eq!(previews[0]["path"], "stale/pending.mp4");
|
||||
assert_eq!(previews[0]["status"], "pending");
|
||||
assert!(previews[0].get("preview_url").is_none());
|
||||
|
||||
assert_eq!(previews[1]["path"], "stale/failed.mp4");
|
||||
assert_eq!(previews[1]["status"], "failed");
|
||||
assert!(previews[1].get("preview_url").is_none());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user