mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-09-05 15:39:07 +08:00
fix: 修复 video 中分 p 下载状态的设置 (#272)
This commit is contained in:
@@ -16,6 +16,8 @@ pub enum ExecutionStatus {
|
|||||||
Succeeded,
|
Succeeded,
|
||||||
Ignored(anyhow::Error),
|
Ignored(anyhow::Error),
|
||||||
Failed(anyhow::Error),
|
Failed(anyhow::Error),
|
||||||
|
// 任务可以返回该状态固定自己的 status
|
||||||
|
FixedFailed(u32, anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
// 目前 stable rust 似乎不支持自定义类型使用 ? 运算符,只能先在返回值使用 Result,再这样套层娃
|
// 目前 stable rust 似乎不支持自定义类型使用 ? 运算符,只能先在返回值使用 Result,再这样套层娃
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::error::ExecutionStatus;
|
use crate::error::ExecutionStatus;
|
||||||
|
|
||||||
pub(super) static STATUS_MAX_RETRY: u32 = 0b100;
|
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;
|
pub static STATUS_COMPLETED: u32 = 1 << 31;
|
||||||
|
|
||||||
/// 用来表示下载的状态,不想写太多列了,所以仅使用一个 u32 表示。
|
/// 用来表示下载的状态,不想写太多列了,所以仅使用一个 u32 表示。
|
||||||
@@ -67,9 +67,10 @@ impl<const N: usize> Status<N> {
|
|||||||
for (i, res) in result.iter().enumerate() {
|
for (i, res) in result.iter().enumerate() {
|
||||||
self.set_result(res, i);
|
self.set_result(res, i);
|
||||||
}
|
}
|
||||||
if self.should_run().iter().all(|x| !x) {
|
if self.should_run().into_iter().all(|x| !x) {
|
||||||
// 所有任务都成功或者由于尝试次数过多失败,为 status 最高位打上标记,将来不再重试
|
self.set_completed(true);
|
||||||
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) {
|
fn set_result(&mut self, result: &ExecutionStatus, offset: usize) {
|
||||||
if self.get_status(offset) < STATUS_MAX_RETRY {
|
// 如果任务返回 FixedFailed 状态,那么无论之前的状态如何,都将状态设置为 FixedFailed 的状态
|
||||||
match result {
|
if let ExecutionStatus::FixedFailed(status, _) = result {
|
||||||
ExecutionStatus::Succeeded | ExecutionStatus::Skipped => self.set_ok(offset),
|
assert!(*status < 0b1000, "status should be less than 0b1000");
|
||||||
ExecutionStatus::Failed(_) => self.plus_one(offset),
|
self.set_status(offset, *status);
|
||||||
ExecutionStatus::Ignored(_) => {}
|
} 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,
|
ExecutionStatus::Succeeded,
|
||||||
]);
|
]);
|
||||||
assert_eq!(status.should_run(), [false, false, false]);
|
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]
|
#[test]
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use crate::utils::model::{
|
|||||||
update_videos_model,
|
update_videos_model,
|
||||||
};
|
};
|
||||||
use crate::utils::nfo::{ModelWrapper, NFOMode, NFOSerializer};
|
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(
|
pub async fn process_video_source(
|
||||||
@@ -274,7 +274,9 @@ pub async fn download_video_pages(
|
|||||||
&video_model.name, task_name, e
|
&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 let ExecutionStatus::Failed(e) = results.into_iter().nth(4).context("page download result not found")? {
|
||||||
if e.downcast_ref::<DownloadAbortError>().is_some() {
|
if e.downcast_ref::<DownloadAbortError>().is_some() {
|
||||||
@@ -314,18 +316,19 @@ pub async fn dispatch_download_page(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<FuturesUnordered<_>>();
|
.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
|
let mut stream = tasks
|
||||||
.take_while(|res| {
|
.take_while(|res| {
|
||||||
match res {
|
match res {
|
||||||
Ok(model) => {
|
Ok(model) => {
|
||||||
// 当前函数返回的是所有分页的下载状态,只要有任何一个分页返回新的下载状态标识位是 false,当前函数就应该认为是失败的
|
// 该视频的所有分页的下载状态都会在此返回,需要根据这些状态确认视频层“分 P 下载”子任务的状态
|
||||||
if model
|
// 在过去的实现中,此处仅仅根据 page_download_status 的最高标志位来判断,如果最高标志位是 true 则认为完成
|
||||||
.download_status
|
// 这样会导致即使分页中有失败到 MAX_RETRY 的情况,视频层的分 P 下载状态也会被认为是 Succeeded,不够准确
|
||||||
.try_as_ref()
|
// 新版本实现会将此处取值为所有子任务状态的最小值,这样只有所有分页的子任务全部成功时才会认为视频层的分 P 下载状态是 Succeeded
|
||||||
.is_none_or(|status| !PageStatus::from(*status).get_completed())
|
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();
|
||||||
error_occurred = true;
|
for status in separate_status {
|
||||||
|
target_status = target_status.min(status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -346,12 +349,8 @@ pub async fn dispatch_download_page(
|
|||||||
error!("下载视频「{}」的分页时触发风控,将异常向上传递..", &video_model.name);
|
error!("下载视频「{}」的分页时触发风控,将异常向上传递..", &video_model.name);
|
||||||
bail!(DownloadAbortError());
|
bail!(DownloadAbortError());
|
||||||
}
|
}
|
||||||
if error_occurred {
|
if target_status != STATUS_OK {
|
||||||
error!(
|
return Ok(ExecutionStatus::FixedFailed(target_status, ProcessPageError().into()));
|
||||||
"下载视频「{}」的分页时出现错误,将在下一轮尝试重新处理",
|
|
||||||
&video_model.name
|
|
||||||
);
|
|
||||||
bail!(ProcessPageError());
|
|
||||||
}
|
}
|
||||||
Ok(ExecutionStatus::Succeeded)
|
Ok(ExecutionStatus::Succeeded)
|
||||||
}
|
}
|
||||||
@@ -473,7 +472,7 @@ pub async fn download_page(
|
|||||||
&video_model.name, page_model.pid, task_name, e
|
&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
|
&video_model.name, page_model.pid, task_name, e
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user