fix: 修复 video 中分 p 下载状态的设置 (#272)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-02-19 19:04:51 +08:00
committed by GitHub
parent 395ef0013a
commit a6425f11a2
3 changed files with 43 additions and 28 deletions

View File

@@ -16,6 +16,8 @@ pub enum ExecutionStatus {
Succeeded,
Ignored(anyhow::Error),
Failed(anyhow::Error),
// 任务可以返回该状态固定自己的 status
FixedFailed(u32, anyhow::Error),
}
// 目前 stable rust 似乎不支持自定义类型使用 ? 运算符,只能先在返回值使用 Result再这样套层娃

View File

@@ -1,7 +1,7 @@
use crate::error::ExecutionStatus;
pub(super) static STATUS_MAX_RETRY: u32 = 0b100;
pub(super) static STATUS_OK: u32 = 0b111;
pub static STATUS_OK: u32 = 0b111;
pub static STATUS_COMPLETED: u32 = 1 << 31;
/// 用来表示下载的状态,不想写太多列了,所以仅使用一个 u32 表示。
@@ -67,9 +67,10 @@ impl<const N: usize> Status<N> {
for (i, res) in result.iter().enumerate() {
self.set_result(res, i);
}
if self.should_run().iter().all(|x| !x) {
// 所有任务都成功或者由于尝试次数过多失败,为 status 最高位打上标记,将来不再重试
self.set_completed(true)
if self.should_run().into_iter().all(|x| !x) {
self.set_completed(true);
} else {
self.set_completed(false);
}
}
@@ -108,14 +109,18 @@ impl<const N: usize> Status<N> {
}
/// 根据子任务执行结果更新子任务的状态
/// 如果 Result 是 Ok那么认为任务执行成功将状态设置为 STATUS_OK
/// 如果 Result 是 Err那么认为任务执行失败将状态加一
fn set_result(&mut self, result: &ExecutionStatus, offset: usize) {
if self.get_status(offset) < STATUS_MAX_RETRY {
match result {
ExecutionStatus::Succeeded | ExecutionStatus::Skipped => self.set_ok(offset),
ExecutionStatus::Failed(_) => self.plus_one(offset),
ExecutionStatus::Ignored(_) => {}
// 如果任务返回 FixedFailed 状态,那么无论之前的状态如何,都将状态设置为 FixedFailed 的状态
if let ExecutionStatus::FixedFailed(status, _) = result {
assert!(*status < 0b1000, "status should be less than 0b1000");
self.set_status(offset, *status);
} else {
if self.get_status(offset) < STATUS_MAX_RETRY {
match result {
ExecutionStatus::Succeeded | ExecutionStatus::Skipped => self.set_ok(offset),
ExecutionStatus::Failed(_) => self.plus_one(offset),
_ => {}
}
}
}
}
@@ -187,6 +192,15 @@ mod test {
ExecutionStatus::Succeeded,
]);
assert_eq!(status.should_run(), [false, false, false]);
assert!(status.get_completed());
status.update_status(&[
ExecutionStatus::FixedFailed(1, anyhow!("")),
ExecutionStatus::FixedFailed(4, anyhow!("")),
ExecutionStatus::FixedFailed(7, anyhow!("")),
]);
assert_eq!(status.should_run(), [true, false, false]);
assert!(!status.get_completed());
assert_eq!(<[u32; 3]>::from(status), [1, 4, 7]);
}
#[test]

View File

@@ -23,7 +23,7 @@ use crate::utils::model::{
update_videos_model,
};
use crate::utils::nfo::{ModelWrapper, NFOMode, NFOSerializer};
use crate::utils::status::{PageStatus, VideoStatus};
use crate::utils::status::{PageStatus, VideoStatus, STATUS_OK};
/// 完整地处理某个视频来源
pub async fn process_video_source(
@@ -274,7 +274,9 @@ pub async fn download_video_pages(
&video_model.name, task_name, e
)
}
ExecutionStatus::Failed(e) => error!("处理视频「{}」{}失败: {:#}", &video_model.name, task_name, e),
ExecutionStatus::Failed(e) | ExecutionStatus::FixedFailed(_, e) => {
error!("处理视频「{}」{}失败: {:#}", &video_model.name, task_name, e)
}
});
if let ExecutionStatus::Failed(e) = results.into_iter().nth(4).context("page download result not found")? {
if e.downcast_ref::<DownloadAbortError>().is_some() {
@@ -314,18 +316,19 @@ pub async fn dispatch_download_page(
)
})
.collect::<FuturesUnordered<_>>();
let (mut download_aborted, mut error_occurred) = (false, false);
let (mut download_aborted, mut target_status) = (false, STATUS_OK);
let mut stream = tasks
.take_while(|res| {
match res {
Ok(model) => {
// 当前函数返回的是所有分页的下载状态,只要有任何一个分页返回新的下载状态标识位是 false当前函数就应该认为是失败的
if model
.download_status
.try_as_ref()
.is_none_or(|status| !PageStatus::from(*status).get_completed())
{
error_occurred = true;
// 该视频的所有分页的下载状态都会在此返回,需要根据这些状态确认视频层“分 P 下载”子任务的状态
// 在过去的实现中,此处仅仅根据 page_download_status 的最高标志位来判断,如果最高标志位是 true 则认为完成
// 这样会导致即使分页中有失败到 MAX_RETRY 的情况,视频层的分 P 下载状态也会被认为是 Succeeded不够准确
// 新版本实现会将此处取值为所有子任务状态的最小值,这样只有所有分页的子任务全部成功时才会认为视频层的分 P 下载状态是 Succeeded
let page_download_status = model.download_status.try_as_ref().expect("download_status must be set");
let separate_status: [u32; 5] = PageStatus::from(*page_download_status).into();
for status in separate_status {
target_status = target_status.min(status);
}
}
Err(e) => {
@@ -346,12 +349,8 @@ pub async fn dispatch_download_page(
error!("下载视频「{}」的分页时触发风控,将异常向上传递..", &video_model.name);
bail!(DownloadAbortError());
}
if error_occurred {
error!(
"下载视频「{}」的分页时出现错误,将在下一轮尝试重新处理",
&video_model.name
);
bail!(ProcessPageError());
if target_status != STATUS_OK {
return Ok(ExecutionStatus::FixedFailed(target_status, ProcessPageError().into()));
}
Ok(ExecutionStatus::Succeeded)
}
@@ -473,7 +472,7 @@ pub async fn download_page(
&video_model.name, page_model.pid, task_name, e
)
}
ExecutionStatus::Failed(e) => error!(
ExecutionStatus::Failed(e) | ExecutionStatus::FixedFailed(_, e) => error!(
"处理视频「{}」第 {} 页{}失败: {:#}",
&video_model.name, page_model.pid, task_name, e
),