mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-05 23:57:23 +08:00
refactor: 用eyre替换anyhow
This commit is contained in:
Generated
+17
-1
@@ -285,11 +285,11 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
name = "bilibili-video-downloader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"eyre",
|
||||
"float-ord",
|
||||
"fs4",
|
||||
"md-5",
|
||||
@@ -1007,6 +1007,16 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "eyre"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
|
||||
dependencies = [
|
||||
"indenter",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
@@ -1850,6 +1860,12 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
|
||||
@@ -35,7 +35,7 @@ reqwest = { version = "0.12.22", default-features = false, features = ["default-
|
||||
reqwest-retry = { version = "0.7.0" }
|
||||
reqwest-middleware = { version = "0.4.2" }
|
||||
|
||||
anyhow = { version = "1.0.98" }
|
||||
eyre = { version = "0.6.12" }
|
||||
parking_lot = { version = "0.12.4", features = ["send_guard"] }
|
||||
tracing = { version = "0.1.41" }
|
||||
tracing-subscriber = { version = "0.3.19", features = ["json", "time", "local-time"] }
|
||||
@@ -62,3 +62,4 @@ strip = true
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
|
||||
|
||||
+145
-149
@@ -1,8 +1,8 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use base64::{Engine, engine::general_purpose};
|
||||
use bytes::Bytes;
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
use parking_lot::RwLock;
|
||||
use prost::Message;
|
||||
use reqwest::{Client, StatusCode};
|
||||
@@ -18,7 +18,7 @@ use tokio::task::JoinSet;
|
||||
|
||||
use crate::{
|
||||
config::ProxyMode,
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
protobuf::DmSegMobileReply,
|
||||
types::{
|
||||
bangumi_follow_info::BangumiFollowInfo, bangumi_info::BangumiInfo,
|
||||
@@ -74,7 +74,7 @@ impl BiliClient {
|
||||
*self.content_length_client.write() = content_length_client;
|
||||
}
|
||||
|
||||
pub async fn generate_qrcode(&self) -> anyhow::Result<QrcodeData> {
|
||||
pub async fn generate_qrcode(&self) -> eyre::Result<QrcodeData> {
|
||||
// 发送生成二维码请求
|
||||
let request = self
|
||||
.api_client
|
||||
@@ -85,28 +85,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为二维码数据
|
||||
let data_str = data.to_string();
|
||||
let qrcode_data: QrcodeData = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为QrcodeData失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为QrcodeData失败: {data_str}"))?;
|
||||
|
||||
Ok(qrcode_data)
|
||||
}
|
||||
|
||||
pub async fn get_qrcode_status(&self, qrcode_key: &str) -> anyhow::Result<QrcodeStatus> {
|
||||
pub async fn get_qrcode_status(&self, qrcode_key: &str) -> eyre::Result<QrcodeStatus> {
|
||||
// 发送获取二维码状态请求
|
||||
let params = json!({"qrcode_key": qrcode_key});
|
||||
let request = self
|
||||
@@ -119,30 +119,30 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为二维码状态
|
||||
let data_str = data.to_string();
|
||||
let qrcode_status: QrcodeStatus = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为QrcodeStatus失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为QrcodeStatus失败: {data_str}"))?;
|
||||
if ![0, 86101, 86090, 86038].contains(&qrcode_status.code) {
|
||||
return Err(anyhow!("预料之外的二维码code: {qrcode_status:?}"));
|
||||
return Err(eyre!("预料之外的二维码code: {qrcode_status:?}"));
|
||||
}
|
||||
Ok(qrcode_status)
|
||||
}
|
||||
|
||||
pub async fn get_user_info(&self, sessdata: &str) -> anyhow::Result<UserInfo> {
|
||||
pub async fn get_user_info(&self, sessdata: &str) -> eyre::Result<UserInfo> {
|
||||
// 发送获取用户信息的请求
|
||||
let request = self
|
||||
.api_client
|
||||
@@ -154,30 +154,30 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code == -101 {
|
||||
return Err(anyhow!("cookie错误或已过期,请重新登录: {bili_resp:?}"));
|
||||
return Err(eyre!("cookie错误或已过期,请重新登录: {bili_resp:?}"));
|
||||
} else if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为UserInfo
|
||||
let data_str = data.to_string();
|
||||
let user_info: UserInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为UserInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为UserInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(user_info)
|
||||
}
|
||||
|
||||
pub async fn get_normal_info(&self, params: GetNormalInfoParams) -> anyhow::Result<NormalInfo> {
|
||||
pub async fn get_normal_info(&self, params: GetNormalInfoParams) -> eyre::Result<NormalInfo> {
|
||||
use GetNormalInfoParams::{Aid, Bvid};
|
||||
let params = match params {
|
||||
Bvid(bvid) => json!({"bvid": bvid}),
|
||||
@@ -195,23 +195,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为NormalInfo
|
||||
let data_str = data.to_string();
|
||||
let normal_info: NormalInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为NormalInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为NormalInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(normal_info)
|
||||
}
|
||||
@@ -219,7 +219,7 @@ impl BiliClient {
|
||||
pub async fn get_bangumi_info(
|
||||
&self,
|
||||
params: GetBangumiInfoParams,
|
||||
) -> anyhow::Result<BangumiInfo> {
|
||||
) -> eyre::Result<BangumiInfo> {
|
||||
use GetBangumiInfoParams::{EpId, SeasonId};
|
||||
let params = match params {
|
||||
EpId(ep_id) => json!({"ep_id": ep_id}),
|
||||
@@ -237,28 +237,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为BangumiInfo
|
||||
let data_str = data.to_string();
|
||||
let bangumi_info: BangumiInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为BangumiInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为BangumiInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(bangumi_info)
|
||||
}
|
||||
|
||||
pub async fn get_cheese_info(&self, params: GetCheeseInfoParams) -> anyhow::Result<CheeseInfo> {
|
||||
pub async fn get_cheese_info(&self, params: GetCheeseInfoParams) -> eyre::Result<CheeseInfo> {
|
||||
use GetCheeseInfoParams::{EpId, SeasonId};
|
||||
let params = match params {
|
||||
EpId(ep_id) => json!({"ep_id": ep_id}),
|
||||
@@ -276,23 +276,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为CheeseInfo
|
||||
let data_str = data.to_string();
|
||||
let cheese_info: CheeseInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为CheeseInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为CheeseInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(cheese_info)
|
||||
}
|
||||
@@ -300,7 +300,7 @@ impl BiliClient {
|
||||
pub async fn get_user_video_info(
|
||||
&self,
|
||||
params: GetUserVideoInfoParams,
|
||||
) -> anyhow::Result<UserVideoInfo> {
|
||||
) -> eyre::Result<UserVideoInfo> {
|
||||
const DM_IMG_INTER: &str = r#"{"ds":[],"wh":[0,0,0],"of":[0,0,0]}"#;
|
||||
|
||||
fn random_base64() -> String {
|
||||
@@ -337,28 +337,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为UserVideoInfo
|
||||
let data_str = data.to_string();
|
||||
let user_video_info: UserVideoInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为UserVideoInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为UserVideoInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(user_video_info)
|
||||
}
|
||||
|
||||
pub async fn get_normal_url(&self, bvid: &str, cid: i64) -> anyhow::Result<NormalMediaUrl> {
|
||||
pub async fn get_normal_url(&self, bvid: &str, cid: i64) -> eyre::Result<NormalMediaUrl> {
|
||||
let params = json!({
|
||||
"bvid": bvid,
|
||||
"cid": cid,
|
||||
@@ -377,28 +377,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为NormalMediaUrl
|
||||
let data_str = data.to_string();
|
||||
let media_url: NormalMediaUrl = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为NormalMediaUrl失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为NormalMediaUrl失败: {data_str}"))?;
|
||||
|
||||
Ok(media_url)
|
||||
}
|
||||
|
||||
pub async fn get_bangumi_url(&self, cid: i64) -> anyhow::Result<BangumiMediaUrl> {
|
||||
pub async fn get_bangumi_url(&self, cid: i64) -> eyre::Result<BangumiMediaUrl> {
|
||||
let media_url_v2 = self.get_bangumi_url_v2(cid).await?;
|
||||
if media_url_v2.video_info.is_drm {
|
||||
self.get_bangumi_url_v1(cid).await
|
||||
@@ -407,7 +407,7 @@ impl BiliClient {
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_bangumi_url_v1(&self, cid: i64) -> anyhow::Result<BangumiMediaUrl> {
|
||||
async fn get_bangumi_url_v1(&self, cid: i64) -> eyre::Result<BangumiMediaUrl> {
|
||||
let params = json!({
|
||||
"cid": cid,
|
||||
"qn": 127,
|
||||
@@ -426,32 +426,30 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code == -10403 {
|
||||
return Err(anyhow!(
|
||||
"地区限制,请使用代理或切换线路后重试: {bili_resp:?}"
|
||||
));
|
||||
return Err(eyre!("地区限制,请使用代理或切换线路后重试: {bili_resp:?}"));
|
||||
} else if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为BangumiMediaUrl
|
||||
let data_str = data.to_string();
|
||||
let media_url: BangumiMediaUrl = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为BangumiMediaUrl失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为BangumiMediaUrl失败: {data_str}"))?;
|
||||
|
||||
Ok(media_url)
|
||||
}
|
||||
|
||||
async fn get_bangumi_url_v2(&self, cid: i64) -> anyhow::Result<BangumiMediaUrlV2> {
|
||||
async fn get_bangumi_url_v2(&self, cid: i64) -> eyre::Result<BangumiMediaUrlV2> {
|
||||
let params = json!({
|
||||
"cid": cid,
|
||||
"qn": 127,
|
||||
@@ -471,32 +469,30 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code == -10403 {
|
||||
return Err(anyhow!(
|
||||
"地区限制,请使用代理或切换线路后重试: {bili_resp:?}"
|
||||
));
|
||||
return Err(eyre!("地区限制,请使用代理或切换线路后重试: {bili_resp:?}"));
|
||||
} else if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为BangumiMediaUrlV2
|
||||
let data_str = data.to_string();
|
||||
let media_url: BangumiMediaUrlV2 = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为BangumiMediaUrlV2失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为BangumiMediaUrlV2失败: {data_str}"))?;
|
||||
|
||||
Ok(media_url)
|
||||
}
|
||||
|
||||
pub async fn get_cheese_url(&self, ep_id: i64) -> anyhow::Result<CheeseMediaUrl> {
|
||||
pub async fn get_cheese_url(&self, ep_id: i64) -> eyre::Result<CheeseMediaUrl> {
|
||||
let params = json!({
|
||||
"ep_id": ep_id,
|
||||
"qn": 127,
|
||||
@@ -515,30 +511,30 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code == -403 {
|
||||
return Err(anyhow!("没有观看权限,请先购买: {bili_resp:?}"));
|
||||
return Err(eyre!("没有观看权限,请先购买: {bili_resp:?}"));
|
||||
} else if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为CheeseMediaUrl
|
||||
let data_str = data.to_string();
|
||||
let media_url: CheeseMediaUrl = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为CheeseMediaUrl失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为CheeseMediaUrl失败: {data_str}"))?;
|
||||
|
||||
Ok(media_url)
|
||||
}
|
||||
|
||||
pub async fn get_player_info(&self, aid: i64, cid: i64) -> anyhow::Result<PlayerInfo> {
|
||||
pub async fn get_player_info(&self, aid: i64, cid: i64) -> eyre::Result<PlayerInfo> {
|
||||
let params = json!({
|
||||
"aid": aid,
|
||||
"cid": cid,
|
||||
@@ -555,28 +551,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为PlayerInfo
|
||||
let data_str = data.to_string();
|
||||
let player_info: PlayerInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为PlayerInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为PlayerInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(player_info)
|
||||
}
|
||||
|
||||
pub async fn get_fav_folders(&self, uid: i64) -> anyhow::Result<FavFolders> {
|
||||
pub async fn get_fav_folders(&self, uid: i64) -> eyre::Result<FavFolders> {
|
||||
let params = json!({"up_mid": uid});
|
||||
// 发送获取收藏夹信息的请求
|
||||
let request = self
|
||||
@@ -590,28 +586,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为FavFolders
|
||||
let data_str = data.to_string();
|
||||
let fav_folders: FavFolders = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为FavFolders失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为FavFolders失败: {data_str}"))?;
|
||||
|
||||
Ok(fav_folders)
|
||||
}
|
||||
|
||||
pub async fn get_fav_info(&self, params: GetFavInfoParams) -> anyhow::Result<FavInfo> {
|
||||
pub async fn get_fav_info(&self, params: GetFavInfoParams) -> eyre::Result<FavInfo> {
|
||||
let params = json!({
|
||||
"media_id": params.media_list_id,
|
||||
"pn": params.pn,
|
||||
@@ -630,28 +626,28 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为FavInfo
|
||||
let data_str = data.to_string();
|
||||
let fav_info: FavInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为FavInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为FavInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(fav_info)
|
||||
}
|
||||
|
||||
pub async fn get_watch_later_info(&self, page: i32) -> anyhow::Result<WatchLaterInfo> {
|
||||
pub async fn get_watch_later_info(&self, page: i32) -> eyre::Result<WatchLaterInfo> {
|
||||
// 发送获取稍后观看信息的请求
|
||||
let params = json!({"ps": 20, "pn": page});
|
||||
let request = self
|
||||
@@ -665,23 +661,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为WatchLaterInfo
|
||||
let data_str = data.to_string();
|
||||
let watch_later_info: WatchLaterInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为WatchLaterInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为WatchLaterInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(watch_later_info)
|
||||
}
|
||||
@@ -689,7 +685,7 @@ impl BiliClient {
|
||||
pub async fn get_bangumi_follow_info(
|
||||
&self,
|
||||
params: GetBangumiFollowInfoParams,
|
||||
) -> anyhow::Result<BangumiFollowInfo> {
|
||||
) -> eyre::Result<BangumiFollowInfo> {
|
||||
// 发送获取番剧追踪信息的请求
|
||||
let params = json!({
|
||||
"vmid": params.vmid,
|
||||
@@ -709,23 +705,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为BangumiFollowInfo
|
||||
let data_str = data.to_string();
|
||||
let bangumi_follow_info: BangumiFollowInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为BangumiFollowInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为BangumiFollowInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(bangumi_follow_info)
|
||||
}
|
||||
@@ -733,7 +729,7 @@ impl BiliClient {
|
||||
pub async fn get_history_info(
|
||||
&self,
|
||||
params: GetHistoryInfoParams,
|
||||
) -> anyhow::Result<HistoryInfo> {
|
||||
) -> eyre::Result<HistoryInfo> {
|
||||
let device_type: i64 = params.device_type.into();
|
||||
let params = json!({
|
||||
"pn": params.pn,
|
||||
@@ -756,23 +752,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为HistoryInfo
|
||||
let data_str = data.to_string();
|
||||
let history_info: HistoryInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为HistoryInfo失败: {data_str}"))?;
|
||||
.wrap_err(format!("将data解析为HistoryInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(history_info)
|
||||
}
|
||||
@@ -782,7 +778,7 @@ impl BiliClient {
|
||||
media_url: &str,
|
||||
start: u64,
|
||||
end: u64,
|
||||
) -> anyhow::Result<Bytes> {
|
||||
) -> eyre::Result<Bytes> {
|
||||
let request = self
|
||||
.media_client
|
||||
.read()
|
||||
@@ -792,7 +788,7 @@ impl BiliClient {
|
||||
// 检查http响应状态码
|
||||
let status = http_resp.status();
|
||||
if status != StatusCode::PARTIAL_CONTENT {
|
||||
return Err(anyhow!("预料之外的状态码({status})"));
|
||||
return Err(eyre!("预料之外的状态码({status})"));
|
||||
}
|
||||
|
||||
let bytes = http_resp.bytes().await?;
|
||||
@@ -800,32 +796,32 @@ impl BiliClient {
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub async fn get_content_length(&self, media_url: &str) -> anyhow::Result<u64> {
|
||||
fn parse_content_length(headers: &HeaderMap) -> anyhow::Result<u64> {
|
||||
pub async fn get_content_length(&self, media_url: &str) -> eyre::Result<u64> {
|
||||
fn parse_content_length(headers: &HeaderMap) -> eyre::Result<u64> {
|
||||
headers
|
||||
.get("Content-Length")
|
||||
.context("缺少 Content-Length 响应头")?
|
||||
.ok_or_eyre("缺少 Content-Length 响应头")?
|
||||
.to_str()
|
||||
.context("Content-Length 响应头无法转换为字符串")?
|
||||
.wrap_err("Content-Length 响应头无法转换为字符串")?
|
||||
.parse::<u64>()
|
||||
.context("Content-Length 响应头无法转换为整数")
|
||||
.wrap_err("Content-Length 响应头无法转换为整数")
|
||||
}
|
||||
|
||||
fn parse_total_from_content_range(headers: &HeaderMap) -> anyhow::Result<u64> {
|
||||
fn parse_total_from_content_range(headers: &HeaderMap) -> eyre::Result<u64> {
|
||||
// Example: "bytes 0-0/12345"
|
||||
let content_range = headers
|
||||
.get("Content-Range")
|
||||
.context("缺少 Content-Range 响应头")?
|
||||
.ok_or_eyre("缺少 Content-Range 响应头")?
|
||||
.to_str()
|
||||
.context("Content-Range 响应头无法转换为字符串")?;
|
||||
.wrap_err("Content-Range 响应头无法转换为字符串")?;
|
||||
|
||||
let Some((_, total)) = content_range.split_once('/') else {
|
||||
return Err(anyhow!("预料之外的 Content-Range 格式: {content_range}"));
|
||||
return Err(eyre!("预料之外的 Content-Range 格式: {content_range}"));
|
||||
};
|
||||
|
||||
total
|
||||
.parse::<u64>()
|
||||
.context("Content-Range 总大小无法转换为整数")
|
||||
.wrap_err("Content-Range 总大小无法转换为整数")
|
||||
}
|
||||
|
||||
// 优先使用 HEAD 获取 Content-Length
|
||||
@@ -855,7 +851,7 @@ impl BiliClient {
|
||||
return parse_content_length(http_resp.headers());
|
||||
}
|
||||
|
||||
Err(anyhow!("预料之外的状态码({status})"))
|
||||
Err(eyre!("预料之外的状态码({status})"))
|
||||
}
|
||||
|
||||
pub async fn get_url_with_content_length(&self, urls: Vec<String>) -> Vec<(String, u64)> {
|
||||
@@ -889,7 +885,7 @@ impl BiliClient {
|
||||
aid: i64,
|
||||
cid: i64,
|
||||
duration: u64,
|
||||
) -> anyhow::Result<Vec<DmSegMobileReply>> {
|
||||
) -> eyre::Result<Vec<DmSegMobileReply>> {
|
||||
let client = self.api_client.read().clone();
|
||||
// 以6分钟为单位分段
|
||||
let segment_count = duration.div_ceil(360);
|
||||
@@ -916,11 +912,11 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
if status != StatusCode::OK {
|
||||
let body = http_resp.text().await?;
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
let body = http_resp.bytes().await?;
|
||||
let reply =
|
||||
DmSegMobileReply::decode(body).context("将body解析为DmSegMobileReply失败")?;
|
||||
DmSegMobileReply::decode(body).wrap_err("将body解析为DmSegMobileReply失败")?;
|
||||
|
||||
Ok(reply)
|
||||
});
|
||||
@@ -939,37 +935,37 @@ impl BiliClient {
|
||||
Ok(replies)
|
||||
}
|
||||
|
||||
pub async fn get_subtitle(&self, url: &str) -> anyhow::Result<Subtitle> {
|
||||
pub async fn get_subtitle(&self, url: &str) -> eyre::Result<Subtitle> {
|
||||
let request = self.api_client.read().get(url);
|
||||
let http_resp = request.send().await?;
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为Subtitle
|
||||
let subtitle: Subtitle =
|
||||
serde_json::from_str(&body).context(format!("将body解析为Subtitle失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为Subtitle失败: {body}"))?;
|
||||
|
||||
Ok(subtitle)
|
||||
}
|
||||
|
||||
pub async fn get_cover_data_and_ext(&self, url: &str) -> anyhow::Result<(Bytes, String)> {
|
||||
pub async fn get_cover_data_and_ext(&self, url: &str) -> eyre::Result<(Bytes, String)> {
|
||||
let request = self.api_client.read().get(url);
|
||||
let http_resp = request.send().await?;
|
||||
// 检查http响应状态码
|
||||
let status = http_resp.status();
|
||||
if status != StatusCode::OK {
|
||||
let body = http_resp.text().await?;
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
|
||||
let content_type = http_resp
|
||||
.headers()
|
||||
.get("Content-Type")
|
||||
.context("缺少 Content-Type 响应头")?
|
||||
.ok_or_eyre("缺少 Content-Type 响应头")?
|
||||
.to_str()
|
||||
.context("Content-Type 响应头无法转换为字符串")?
|
||||
.wrap_err("Content-Type 响应头无法转换为字符串")?
|
||||
.to_string();
|
||||
|
||||
let ext = match content_type.as_str() {
|
||||
@@ -984,7 +980,7 @@ impl BiliClient {
|
||||
Ok((bytes, ext.to_string()))
|
||||
}
|
||||
|
||||
pub async fn get_tags(&self, aid: i64) -> anyhow::Result<Tags> {
|
||||
pub async fn get_tags(&self, aid: i64) -> eyre::Result<Tags> {
|
||||
// 发送获取普通视频标签的请求
|
||||
let params = json!({"aid": aid});
|
||||
let request = self
|
||||
@@ -998,23 +994,23 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
return Err(eyre!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为Tags
|
||||
let data_str = data.to_string();
|
||||
let tags: Tags =
|
||||
serde_json::from_str(&data_str).context(format!("将data解析为Tags失败: {data_str}"))?;
|
||||
let tags: Tags = serde_json::from_str(&data_str)
|
||||
.wrap_err(format!("将data解析为Tags失败: {data_str}"))?;
|
||||
|
||||
Ok(tags)
|
||||
}
|
||||
@@ -1023,7 +1019,7 @@ impl BiliClient {
|
||||
&self,
|
||||
bvid: &str,
|
||||
cid: Option<i64>,
|
||||
) -> anyhow::Result<SkipSegments> {
|
||||
) -> eyre::Result<SkipSegments> {
|
||||
// 发送获取跳过片段的请求
|
||||
let mut params = json!({
|
||||
"videoID": bvid,
|
||||
@@ -1045,11 +1041,11 @@ impl BiliClient {
|
||||
if status == StatusCode::NOT_FOUND {
|
||||
return Ok(SkipSegments(Vec::new()));
|
||||
} else if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为SkipSegments
|
||||
let skip_segments: SkipSegments =
|
||||
serde_json::from_str(&body).context(format!("将body解析为SkipSegments失败: {body}"))?;
|
||||
let skip_segments: SkipSegments = serde_json::from_str(&body)
|
||||
.wrap_err(format!("将body解析为SkipSegments失败: {body}"))?;
|
||||
|
||||
Ok(skip_segments)
|
||||
}
|
||||
@@ -1132,7 +1128,7 @@ impl ClientBuilderExt for reqwest::ClientBuilder {
|
||||
let proxy_port = &config.proxy_port;
|
||||
let proxy_url = format!("http://{proxy_host}:{proxy_port}");
|
||||
|
||||
match reqwest::Proxy::all(&proxy_url).map_err(anyhow::Error::from) {
|
||||
match reqwest::Proxy::all(&proxy_url).map_err(eyre::Report::from) {
|
||||
Ok(proxy) => self.proxy(proxy),
|
||||
Err(err) => {
|
||||
let err_title = format!("{client_name}将`{proxy_url}`设为代理失败,将直连");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::AppHandle;
|
||||
use tauri_plugin_opener::OpenerExt;
|
||||
@@ -366,10 +366,10 @@ pub async fn search(app: AppHandle, params: SearchParams) -> CommandResult<Searc
|
||||
#[specta::specta]
|
||||
pub fn get_logs_dir_size(app: AppHandle) -> CommandResult<u64> {
|
||||
let logs_dir = logger::logs_dir(&app)
|
||||
.context("获取日志目录失败")
|
||||
.wrap_err("获取日志目录失败")
|
||||
.map_err(|err| CommandError::from("获取日志目录大小失败", err))?;
|
||||
let logs_dir_size = std::fs::read_dir(&logs_dir)
|
||||
.context(format!("读取日志目录`{}`失败", logs_dir.display()))
|
||||
.wrap_err(format!("读取日志目录`{}`失败", logs_dir.display()))
|
||||
.map_err(|err| CommandError::from("获取日志目录大小失败", err))?
|
||||
.filter_map(Result::ok)
|
||||
.filter_map(|entry| entry.metadata().ok())
|
||||
@@ -385,7 +385,7 @@ pub fn get_logs_dir_size(app: AppHandle) -> CommandResult<u64> {
|
||||
pub fn show_path_in_file_manager(app: AppHandle, path: &str) -> CommandResult<()> {
|
||||
app.opener()
|
||||
.reveal_item_in_dir(path)
|
||||
.context(format!("在文件管理器中打开`{path}`失败"))
|
||||
.wrap_err(format!("在文件管理器中打开`{path}`失败"))
|
||||
.map_err(|err| CommandError::from("在文件管理器中打开失败", err))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ pub struct Config {
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(app: &AppHandle) -> anyhow::Result<Config> {
|
||||
pub fn new(app: &AppHandle) -> eyre::Result<Config> {
|
||||
let app_data_dir = app.path().app_data_dir()?;
|
||||
let config_path = app_data_dir.join("config.json");
|
||||
|
||||
@@ -67,7 +67,7 @@ impl Config {
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn save(&self, app: &AppHandle) -> anyhow::Result<()> {
|
||||
pub fn save(&self, app: &AppHandle) -> eyre::Result<()> {
|
||||
let app_data_dir = app.path().app_data_dir()?;
|
||||
let config_path = app_data_dir.join("config.json");
|
||||
let config_string = serde_json::to_string_pretty(self)?;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use eyre::Result;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
@@ -5,10 +5,10 @@ pub mod drawable;
|
||||
|
||||
use std::{cmp::Ordering, fs::File};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use ass_writer::AssWriter;
|
||||
use canvas::CanvasConfig;
|
||||
use danmaku::{Danmaku, DanmakuType};
|
||||
use eyre::eyre;
|
||||
use yaserde::{YaDeserialize, YaSerialize};
|
||||
|
||||
#[derive(YaSerialize, YaDeserialize)]
|
||||
@@ -33,7 +33,7 @@ pub fn xml_to_ass(
|
||||
ass_file: File,
|
||||
title: String,
|
||||
config: CanvasConfig,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let mut writer = AssWriter::new(ass_file, title, config.clone())?;
|
||||
let mut canvas = config.canvas();
|
||||
|
||||
@@ -54,24 +54,24 @@ pub fn xml_to_ass(
|
||||
}
|
||||
|
||||
trait ToDanmakuType {
|
||||
fn to_danmaku_type(&self) -> anyhow::Result<DanmakuType>;
|
||||
fn to_danmaku_type(&self) -> eyre::Result<DanmakuType>;
|
||||
}
|
||||
|
||||
impl ToDanmakuType for u32 {
|
||||
fn to_danmaku_type(&self) -> anyhow::Result<DanmakuType> {
|
||||
fn to_danmaku_type(&self) -> eyre::Result<DanmakuType> {
|
||||
match self {
|
||||
1 => Ok(DanmakuType::Float),
|
||||
4 => Ok(DanmakuType::Bottom),
|
||||
5 => Ok(DanmakuType::Top),
|
||||
6 => Ok(DanmakuType::Reverse),
|
||||
_ => Err(anyhow!("未知的弹幕类型:{self}")),
|
||||
_ => Err(eyre!("未知的弹幕类型:{self}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn xml_to_danmakus(xml: &str) -> anyhow::Result<Vec<Danmaku>> {
|
||||
pub fn xml_to_danmakus(xml: &str) -> eyre::Result<Vec<Danmaku>> {
|
||||
let xml = sanitize_xml(xml);
|
||||
let i_tag: DanmakuXmlITag = yaserde::de::from_str(&xml).map_err(|e| anyhow!(e))?;
|
||||
let i_tag: DanmakuXmlITag = yaserde::de::from_str(&xml).map_err(|e| eyre!(e))?;
|
||||
|
||||
let mut danmakus = Vec::new();
|
||||
|
||||
@@ -83,7 +83,7 @@ pub fn xml_to_danmakus(xml: &str) -> anyhow::Result<Vec<Danmaku>> {
|
||||
let mut p_attr = elem.p.split(',');
|
||||
|
||||
let Some(timeline_s) = p_attr.next().and_then(|s| s.parse::<f64>().ok()) else {
|
||||
return Err(anyhow!("弹幕`{content}`的p属性中没有时间"));
|
||||
return Err(eyre!("弹幕`{content}`的p属性中没有时间"));
|
||||
};
|
||||
|
||||
let Some(r#type) = p_attr
|
||||
@@ -91,15 +91,15 @@ pub fn xml_to_danmakus(xml: &str) -> anyhow::Result<Vec<Danmaku>> {
|
||||
.and_then(|s| s.parse::<u32>().ok())
|
||||
.and_then(|num| num.to_danmaku_type().ok())
|
||||
else {
|
||||
return Err(anyhow!("弹幕`{content}`的p属性中没有弹幕类型"));
|
||||
return Err(eyre!("弹幕`{content}`的p属性中没有弹幕类型"));
|
||||
};
|
||||
|
||||
let Some(fontsize) = p_attr.next().and_then(|s| s.parse::<u32>().ok()) else {
|
||||
return Err(anyhow!("弹幕`{content}`的p属性中没有字体大小"));
|
||||
return Err(eyre!("弹幕`{content}`的p属性中没有字体大小"));
|
||||
};
|
||||
|
||||
let Some(rgb) = p_attr.next().and_then(|s| s.parse::<u32>().ok()) else {
|
||||
return Err(anyhow!("弹幕`{content}`的p属性中没有颜色"));
|
||||
return Err(eyre!("弹幕`{content}`的p属性中没有颜色"));
|
||||
};
|
||||
|
||||
// rgb 是个数字,类似 0x010203
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct DownloadChunkTask {
|
||||
}
|
||||
|
||||
impl DownloadChunkTask {
|
||||
pub async fn process(self) -> anyhow::Result<usize> {
|
||||
pub async fn process(self) -> eyre::Result<usize> {
|
||||
let download_chunk_task = self.download_chunk();
|
||||
tokio::pin!(download_chunk_task);
|
||||
|
||||
@@ -64,7 +64,7 @@ impl DownloadChunkTask {
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_chunk(&self) -> anyhow::Result<usize> {
|
||||
async fn download_chunk(&self) -> eyre::Result<usize> {
|
||||
let bili_client = self.download_task.app.get_bili_client();
|
||||
let chunk_data = bili_client
|
||||
.get_media_chunk(&self.url, self.start, self.end)
|
||||
@@ -97,7 +97,7 @@ impl DownloadChunkTask {
|
||||
async fn acquire_chunk_permit<'a>(
|
||||
&'a self,
|
||||
permit: &mut Option<SemaphorePermit<'a>>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
*permit = match permit.take() {
|
||||
// 如果有permit,则直接用
|
||||
Some(permit) => Some(permit),
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri_specta::Event;
|
||||
@@ -16,7 +16,7 @@ use tokio::sync::Semaphore;
|
||||
|
||||
use crate::{
|
||||
events::DownloadEvent,
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
types::{
|
||||
create_download_task_params::CreateDownloadTaskParams,
|
||||
restart_download_task_params::RestartDownloadTaskParams,
|
||||
@@ -59,10 +59,10 @@ impl DownloadManager {
|
||||
manager
|
||||
}
|
||||
|
||||
pub fn restore_download_tasks(&self) -> anyhow::Result<()> {
|
||||
pub fn restore_download_tasks(&self) -> eyre::Result<()> {
|
||||
let task_dir = self.get_task_dir()?;
|
||||
std::fs::create_dir_all(&task_dir)
|
||||
.context(format!("创建下载任务目录`{}`失败", task_dir.display()))?;
|
||||
.wrap_err(format!("创建下载任务目录`{}`失败", task_dir.display()))?;
|
||||
|
||||
let mut tasks = self.download_tasks.write();
|
||||
for entry in std::fs::read_dir(&task_dir)?.filter_map(Result::ok) {
|
||||
@@ -150,9 +150,9 @@ impl DownloadManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(err) = task.delete_sender.send(()).map_err(anyhow::Error::from) {
|
||||
if let Err(err) = task.delete_sender.send(()).map_err(eyre::Report::from) {
|
||||
let err_title = "删除下载任务失败";
|
||||
let err = err.context(format!("通知ID为`{task_id}`的下载任务删除失败"));
|
||||
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务删除失败"));
|
||||
let string_chain = err.to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
tasks.insert(task_id.clone(), task);
|
||||
@@ -173,9 +173,9 @@ impl DownloadManager {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Err(err) = task.restart_sender.send(()).map_err(anyhow::Error::from) {
|
||||
if let Err(err) = task.restart_sender.send(()).map_err(eyre::Report::from) {
|
||||
let err_title = "重来下载任务失败";
|
||||
let err = err.context(format!("通知ID为`{task_id}`的下载任务重来失败"));
|
||||
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务重来失败"));
|
||||
let string_chain = err.to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
continue;
|
||||
@@ -217,9 +217,9 @@ impl DownloadManager {
|
||||
progress.audio_task.audio_quality = params.audio_quality;
|
||||
}
|
||||
|
||||
if let Err(err) = task.restart_sender.send(()).map_err(anyhow::Error::from) {
|
||||
if let Err(err) = task.restart_sender.send(()).map_err(eyre::Report::from) {
|
||||
let err_title = "重来下载任务失败";
|
||||
let err = err.context(format!("通知ID为`{task_id}`的下载任务重来失败"));
|
||||
let err = err.wrap_err(format!("通知ID为`{task_id}`的下载任务重来失败"));
|
||||
let string_chain = err.to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
return;
|
||||
@@ -241,13 +241,13 @@ impl DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_task_dir(&self) -> anyhow::Result<PathBuf> {
|
||||
fn get_task_dir(&self) -> eyre::Result<PathBuf> {
|
||||
let app_data_dir = self.app.path().app_data_dir()?;
|
||||
let task_dir = app_data_dir.join(".下载任务");
|
||||
Ok(task_dir)
|
||||
}
|
||||
|
||||
fn delete_progress_file(&self, task_id: &str) -> anyhow::Result<()> {
|
||||
fn delete_progress_file(&self, task_id: &str) -> eyre::Result<()> {
|
||||
let task_dir = self.get_task_dir()?;
|
||||
let task_file = task_dir.join(format!("{task_id}.json"));
|
||||
if task_file.exists() {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::{AppHandle, Manager};
|
||||
@@ -76,7 +76,7 @@ impl DownloadProgress {
|
||||
info: &NormalInfo,
|
||||
aid: i64,
|
||||
cid: Option<i64>,
|
||||
) -> anyhow::Result<Vec<Self>> {
|
||||
) -> eyre::Result<Vec<Self>> {
|
||||
let config = app.get_config().read().clone();
|
||||
|
||||
if let Some(ugc_season) = &info.ugc_season {
|
||||
@@ -87,10 +87,10 @@ impl DownloadProgress {
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
pub fn from_bangumi(app: &AppHandle, info: &BangumiInfo, ep_id: i64) -> anyhow::Result<Self> {
|
||||
pub fn from_bangumi(app: &AppHandle, info: &BangumiInfo, ep_id: i64) -> eyre::Result<Self> {
|
||||
let (episode, episode_order) = info.get_episode_with_order(ep_id)?;
|
||||
let Some(duration) = episode.duration else {
|
||||
return Err(anyhow!("找不到ep_id为`{ep_id}`的番剧的时长"));
|
||||
return Err(eyre!("找不到ep_id为`{ep_id}`的番剧的时长"));
|
||||
};
|
||||
// 将毫秒转换为秒
|
||||
let duration = duration / 1000;
|
||||
@@ -147,12 +147,12 @@ impl DownloadProgress {
|
||||
Ok(progress)
|
||||
}
|
||||
|
||||
pub fn from_cheese(app: &AppHandle, info: &CheeseInfo, ep_id: i64) -> anyhow::Result<Self> {
|
||||
pub fn from_cheese(app: &AppHandle, info: &CheeseInfo, ep_id: i64) -> eyre::Result<Self> {
|
||||
let episode = info
|
||||
.episodes
|
||||
.iter()
|
||||
.find(|ep| ep.id == ep_id)
|
||||
.context(format!("找不到ep_id为`{ep_id}`的课程"))?;
|
||||
.ok_or_eyre(format!("找不到ep_id为`{ep_id}`的课程"))?;
|
||||
|
||||
let config = app.get_config().read().clone();
|
||||
|
||||
@@ -196,7 +196,7 @@ impl DownloadProgress {
|
||||
Ok(progress)
|
||||
}
|
||||
|
||||
pub async fn process(&mut self, download_task: &Arc<DownloadTask>) -> anyhow::Result<()> {
|
||||
pub async fn process(&mut self, download_task: &Arc<DownloadTask>) -> eyre::Result<()> {
|
||||
let ids_string = self.get_ids_string();
|
||||
|
||||
let _ = DownloadEvent::ProgressPreparing {
|
||||
@@ -206,14 +206,14 @@ impl DownloadProgress {
|
||||
|
||||
self.prepare(&download_task.app)
|
||||
.await
|
||||
.context("准备下载失败")?;
|
||||
.wrap_err("准备下载失败")?;
|
||||
|
||||
self.completed_ts = None; // 重置完成时间戳
|
||||
download_task.update_progress(|p| *p = self.clone());
|
||||
|
||||
let (episode_dir, filename) = (&self.episode_dir, &self.filename);
|
||||
|
||||
std::fs::create_dir_all(episode_dir).context(format!(
|
||||
std::fs::create_dir_all(episode_dir).wrap_err(format!(
|
||||
"{ids_string} 创建目录`{}`失败",
|
||||
episode_dir.display()
|
||||
))?;
|
||||
@@ -234,7 +234,7 @@ impl DownloadProgress {
|
||||
video_task
|
||||
.process(download_task, self)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载视频文件失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载视频文件失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`视频下载任务完成");
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ impl DownloadProgress {
|
||||
audio_task
|
||||
.process(download_task, self)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载音频文件失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载音频文件失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`音频下载任务完成");
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ impl DownloadProgress {
|
||||
video_process_task
|
||||
.process(download_task, self, &mut player_info)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`视频处理失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`视频处理失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`视频处理任务完成");
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ impl DownloadProgress {
|
||||
danmaku_task
|
||||
.process(download_task, self)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载弹幕失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载弹幕失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`弹幕下载任务完成");
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ impl DownloadProgress {
|
||||
subtitle_task
|
||||
.process(download_task, self, &mut player_info)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载字幕失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载字幕失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`字幕下载任务完成");
|
||||
}
|
||||
|
||||
@@ -283,7 +283,7 @@ impl DownloadProgress {
|
||||
cover_task
|
||||
.process(download_task, self)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载封面失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载封面失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`封面下载任务完成");
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ impl DownloadProgress {
|
||||
nfo_task
|
||||
.process(download_task, self, &mut episode_info)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载NFO失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载NFO失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`NFO下载任务完成");
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ impl DownloadProgress {
|
||||
json_task
|
||||
.process(download_task, self, &mut episode_info)
|
||||
.await
|
||||
.context(format!("{ids_string} `{filename}`下载JSON元数据失败"))?;
|
||||
.wrap_err(format!("{ids_string} `{filename}`下载JSON元数据失败"))?;
|
||||
tracing::debug!("{ids_string} `{filename}`JSON元数据下载任务完成");
|
||||
}
|
||||
|
||||
@@ -314,7 +314,7 @@ impl DownloadProgress {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn prepare(&mut self, app: &AppHandle) -> anyhow::Result<()> {
|
||||
async fn prepare(&mut self, app: &AppHandle) -> eyre::Result<()> {
|
||||
let video_selected = self.video_task.selected;
|
||||
let video_completed = self.video_task.completed;
|
||||
let audio_selected = self.audio_task.selected;
|
||||
@@ -323,7 +323,7 @@ impl DownloadProgress {
|
||||
if (!video_selected && !audio_selected) || (video_completed && audio_completed) {
|
||||
// 如果视频和音频都没有选中,或者都已经完成,则更新需要格式化的字段就返回
|
||||
self.update_fmt_fields(app)
|
||||
.context("更新需要格式化的字段失败")?;
|
||||
.wrap_err("更新需要格式化的字段失败")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -332,12 +332,12 @@ impl DownloadProgress {
|
||||
match self.episode_type {
|
||||
EpisodeType::Normal => {
|
||||
let Some(bvid) = &self.bvid else {
|
||||
return Err(anyhow!("progress中的bvid为None,无法获取视频链接"));
|
||||
return Err(eyre!("progress中的bvid为None,无法获取视频链接"));
|
||||
};
|
||||
let media_url = bili_client
|
||||
.get_normal_url(bvid, self.cid)
|
||||
.await
|
||||
.context("获取视频链接失败")?;
|
||||
.wrap_err("获取视频链接失败")?;
|
||||
|
||||
self.is_preview = !media_url.durl.is_empty() && media_url.dash.video.is_empty();
|
||||
|
||||
@@ -355,7 +355,7 @@ impl DownloadProgress {
|
||||
let media_url = bili_client
|
||||
.get_bangumi_url(self.cid)
|
||||
.await
|
||||
.context("获取番剧视频链接失败")?;
|
||||
.wrap_err("获取番剧视频链接失败")?;
|
||||
|
||||
self.is_preview = media_url.is_preview != 0;
|
||||
|
||||
@@ -371,12 +371,12 @@ impl DownloadProgress {
|
||||
}
|
||||
EpisodeType::Cheese => {
|
||||
let Some(ep_id) = self.ep_id else {
|
||||
return Err(anyhow!("progress中的ep_id为None,无法获取课程视频链接"));
|
||||
return Err(eyre!("progress中的ep_id为None,无法获取课程视频链接"));
|
||||
};
|
||||
let media_url = bili_client
|
||||
.get_cheese_url(ep_id)
|
||||
.await
|
||||
.context("获取课程视频链接失败")?;
|
||||
.wrap_err("获取课程视频链接失败")?;
|
||||
|
||||
self.is_drm = media_url.is_drm;
|
||||
self.is_preview = media_url.is_preview != 0;
|
||||
@@ -394,12 +394,12 @@ impl DownloadProgress {
|
||||
}
|
||||
|
||||
self.update_fmt_fields(app)
|
||||
.context("更新需要格式化的字段失败")?;
|
||||
.wrap_err("更新需要格式化的字段失败")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_fmt_fields(&mut self, app: &AppHandle) -> anyhow::Result<()> {
|
||||
fn update_fmt_fields(&mut self, app: &AppHandle) -> eyre::Result<()> {
|
||||
let fmt_params = self.create_fmt_params();
|
||||
|
||||
let config = app.get_config().read().clone();
|
||||
@@ -435,7 +435,7 @@ impl DownloadProgress {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn save(&self, app: &AppHandle, allow_create: bool) -> anyhow::Result<()> {
|
||||
pub fn save(&self, app: &AppHandle, allow_create: bool) -> eyre::Result<()> {
|
||||
let progress = self.clone();
|
||||
let file_name = format!("{}.json", progress.task_id);
|
||||
|
||||
@@ -490,7 +490,7 @@ fn create_normal_progresses_for_single(
|
||||
info: &NormalInfo,
|
||||
cid: Option<i64>,
|
||||
config: &Config,
|
||||
) -> anyhow::Result<Vec<DownloadProgress>> {
|
||||
) -> eyre::Result<Vec<DownloadProgress>> {
|
||||
let tasks = Tasks::new(config, &info.pic);
|
||||
|
||||
let create_ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||
@@ -498,7 +498,7 @@ fn create_normal_progresses_for_single(
|
||||
if let Some(cid) = cid {
|
||||
// 如果有cid,则说明是要下载单个分P
|
||||
let Some(page) = info.pages.iter().find(|p| p.cid == cid) else {
|
||||
return Err(anyhow!("找不到cid为`{cid}`的分P"));
|
||||
return Err(eyre!("找不到cid为`{cid}`的分P"));
|
||||
};
|
||||
let progress = DownloadProgress {
|
||||
task_id: Uuid::new_v4().to_string(),
|
||||
@@ -621,12 +621,12 @@ fn create_normal_progresses_for_season(
|
||||
aid: i64,
|
||||
cid: Option<i64>,
|
||||
config: &Config,
|
||||
) -> anyhow::Result<Vec<DownloadProgress>> {
|
||||
) -> eyre::Result<Vec<DownloadProgress>> {
|
||||
let section_index = ugc_season
|
||||
.sections
|
||||
.iter()
|
||||
.position(|s| s.episodes.iter().any(|e| e.aid == aid))
|
||||
.context(format!("找不到含有aid为`{aid}`的ep的section"))?;
|
||||
.ok_or_eyre(format!("找不到含有aid为`{aid}`的ep的section"))?;
|
||||
let section = &ugc_season.sections[section_index];
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
let (ep, episode_order) = section
|
||||
@@ -635,7 +635,7 @@ fn create_normal_progresses_for_season(
|
||||
.enumerate()
|
||||
.map(|(i, e)| (e, i as i64 + 1))
|
||||
.find(|(e, _)| e.aid == aid)
|
||||
.context(format!("在section中找不到aid为`{aid}`的ep"))?;
|
||||
.ok_or_eyre(format!("在section中找不到aid为`{aid}`的ep"))?;
|
||||
|
||||
let tasks = Tasks::new(config, &ep.arc.pic);
|
||||
|
||||
@@ -644,7 +644,7 @@ fn create_normal_progresses_for_season(
|
||||
if let Some(cid) = cid {
|
||||
// 如果有cid,则说明是要下载单个分P
|
||||
let Some(page) = ep.pages.iter().find(|p| p.cid == cid) else {
|
||||
return Err(anyhow!("找不到cid为`{cid}`的分P"));
|
||||
return Err(eyre!("找不到cid为`{cid}`的分P"));
|
||||
};
|
||||
let progress = DownloadProgress {
|
||||
task_id: Uuid::new_v4().to_string(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::AppHandle;
|
||||
use tauri_specta::Event;
|
||||
@@ -11,7 +11,7 @@ use tokio::{
|
||||
|
||||
use crate::{
|
||||
events::DownloadEvent,
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
types::create_download_task_params::CreateDownloadTaskParams,
|
||||
};
|
||||
|
||||
@@ -234,7 +234,7 @@ impl DownloadTask {
|
||||
if let Err(err) = progress
|
||||
.process(self)
|
||||
.await
|
||||
.context("[继续]失败的任务可以断点续传")
|
||||
.wrap_err("[继续]失败的任务可以断点续传")
|
||||
{
|
||||
let err_title = format!("{ids_string} `{episode_title}`下载失败");
|
||||
let string_chain = err.to_string_chain();
|
||||
@@ -283,7 +283,7 @@ impl DownloadTask {
|
||||
.task_sem
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(eyre::Report::from)
|
||||
{
|
||||
Ok(permit) => Some(permit),
|
||||
Err(err) => {
|
||||
@@ -306,7 +306,7 @@ impl DownloadTask {
|
||||
if let Err(err) = self
|
||||
.state_sender
|
||||
.send(DownloadTaskState::Downloading)
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(eyre::Report::from)
|
||||
{
|
||||
let err_title = format!("{ids_string} `{episode_title}`发送状态`Downloading`失败");
|
||||
let string_chain = err.to_string_chain();
|
||||
@@ -350,7 +350,7 @@ impl DownloadTask {
|
||||
(progress.episode_title.clone(), progress.get_ids_string())
|
||||
};
|
||||
|
||||
if let Err(err) = self.state_sender.send(state).map_err(anyhow::Error::from) {
|
||||
if let Err(err) = self.state_sender.send(state).map_err(eyre::Report::from) {
|
||||
let err_title = format!("{ids_string} `{episode_title}`发送状态`{state:?}`失败");
|
||||
let string_chain = err.to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Context;
|
||||
use eyre::{OptionExt, WrapErr};
|
||||
use tauri::AppHandle;
|
||||
|
||||
use crate::{
|
||||
@@ -23,7 +23,7 @@ pub trait GetOrInitEpisodeInfo {
|
||||
&'a mut self,
|
||||
app: &AppHandle,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<&'a mut EpisodeInfo>;
|
||||
) -> eyre::Result<&'a mut EpisodeInfo>;
|
||||
}
|
||||
|
||||
impl GetOrInitEpisodeInfo for Option<EpisodeInfo> {
|
||||
@@ -31,7 +31,7 @@ impl GetOrInitEpisodeInfo for Option<EpisodeInfo> {
|
||||
&'a mut self,
|
||||
app: &AppHandle,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<&'a mut EpisodeInfo> {
|
||||
) -> eyre::Result<&'a mut EpisodeInfo> {
|
||||
if let Some(info) = self {
|
||||
return Ok(info);
|
||||
}
|
||||
@@ -44,23 +44,23 @@ impl GetOrInitEpisodeInfo for Option<EpisodeInfo> {
|
||||
let info = bili_client
|
||||
.get_normal_info(GetNormalInfoParams::Aid(aid))
|
||||
.await
|
||||
.context("获取普通视频信息失败")?;
|
||||
.wrap_err("获取普通视频信息失败")?;
|
||||
EpisodeInfo::Normal(info)
|
||||
}
|
||||
EpisodeType::Bangumi => {
|
||||
let ep_id = ep_id.context("ep_id为None")?;
|
||||
let ep_id = ep_id.ok_or_eyre("ep_id为None")?;
|
||||
let info = bili_client
|
||||
.get_bangumi_info(GetBangumiInfoParams::EpId(ep_id))
|
||||
.await
|
||||
.context("获取番剧信息失败")?;
|
||||
.wrap_err("获取番剧信息失败")?;
|
||||
EpisodeInfo::Bangumi(info, ep_id)
|
||||
}
|
||||
EpisodeType::Cheese => {
|
||||
let ep_id = ep_id.context("ep_id为None")?;
|
||||
let ep_id = ep_id.ok_or_eyre("ep_id为None")?;
|
||||
let info = bili_client
|
||||
.get_cheese_info(GetCheeseInfoParams::EpId(ep_id))
|
||||
.await
|
||||
.context("获取课程信息失败")?;
|
||||
.wrap_err("获取课程信息失败")?;
|
||||
EpisodeInfo::Cheese(info, ep_id)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::{OptionExt, WrapErr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
@@ -36,18 +36,15 @@ pub struct FmtParams {
|
||||
}
|
||||
|
||||
impl FmtParams {
|
||||
pub fn get_episode_dir_and_filename(
|
||||
&self,
|
||||
config: &Config,
|
||||
) -> anyhow::Result<(PathBuf, String)> {
|
||||
pub fn get_episode_dir_and_filename(&self, config: &Config) -> eyre::Result<(PathBuf, String)> {
|
||||
use strfmt::strfmt;
|
||||
|
||||
let mut json_value =
|
||||
serde_json::to_value(self).context("将FmtParams转为serde_json::Value失败")?;
|
||||
serde_json::to_value(self).wrap_err("将FmtParams转为serde_json::Value失败")?;
|
||||
|
||||
let json_map = json_value
|
||||
.as_object_mut()
|
||||
.context("FmtParams不是JSON对象")?;
|
||||
.ok_or_eyre("FmtParams不是JSON对象")?;
|
||||
// 格式化时间字段
|
||||
format_time_fields(json_map, &config.time_fmt);
|
||||
|
||||
@@ -74,7 +71,7 @@ impl FmtParams {
|
||||
let dir_fmt_parts: Vec<&str> = dir_fmt.split('/').collect();
|
||||
let mut dir_names = Vec::new();
|
||||
for fmt in dir_fmt_parts {
|
||||
let dir_name = strfmt(fmt, &vars).context("格式化目录名失败")?;
|
||||
let dir_name = strfmt(fmt, &vars).wrap_err("格式化目录名失败")?;
|
||||
let dir_name = filename_filter(&dir_name);
|
||||
if !dir_name.is_empty() {
|
||||
dir_names.push(dir_name);
|
||||
@@ -82,7 +79,7 @@ impl FmtParams {
|
||||
}
|
||||
|
||||
// 最后一部分是文件名
|
||||
let filename = dir_names.pop().context("没有找到文件名部分")?;
|
||||
let filename = dir_names.pop().ok_or_eyre("没有找到文件名部分")?;
|
||||
// 剩下的部分是目录名
|
||||
let mut episode_dir = config.download_dir.clone();
|
||||
for dir_name in dir_names {
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{WrapErr, eyre};
|
||||
use fs4::fs_std::FileExt;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
download_chunk_task::DownloadChunkTask, download_progress::DownloadProgress,
|
||||
download_task::DownloadTask, media_chunk::MediaChunk,
|
||||
},
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
types::{
|
||||
audio_quality::AudioQuality, bangumi_media_url::BangumiMediaUrl,
|
||||
cheese_media_url::CheeseMediaUrl, normal_media_url::NormalMediaUrl,
|
||||
@@ -45,7 +45,7 @@ impl AudioTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &NormalMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let mut join_set = JoinSet::new();
|
||||
|
||||
if let Some(medias) = &media_url.dash.audio {
|
||||
@@ -130,7 +130,7 @@ impl AudioTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &BangumiMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let Some(dash) = &media_url.dash else {
|
||||
// 如果没有音频,则直接返回
|
||||
self.completed = true;
|
||||
@@ -190,7 +190,7 @@ impl AudioTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &CheeseMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let Some(dash) = &media_url.dash else {
|
||||
// 如果没有音频,则直接返回
|
||||
self.completed = true;
|
||||
@@ -314,7 +314,7 @@ impl AudioTask {
|
||||
&self,
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let (audio_task, episode_title, ids_string) = {
|
||||
(
|
||||
@@ -378,7 +378,7 @@ impl AudioTask {
|
||||
};
|
||||
|
||||
join_set.spawn(async move {
|
||||
download_chunk_task.process().await.context(format!(
|
||||
download_chunk_task.process().await.wrap_err(format!(
|
||||
"分片`{chunk_index}/{chunk_count}`下载失败({start}-{end})"
|
||||
))
|
||||
});
|
||||
@@ -407,20 +407,20 @@ impl AudioTask {
|
||||
.iter()
|
||||
.all(|chunk| chunk.completed);
|
||||
if !download_completed {
|
||||
return Err(anyhow!(
|
||||
return Err(eyre!(
|
||||
"音频文件`{}`有分片未下载完成,[继续]可以跳过已下载分片断点续传",
|
||||
temp_file_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
let is_audio_file_complete = utils::is_mp4_complete(&temp_file_path).context(format!(
|
||||
let is_audio_file_complete = utils::is_mp4_complete(&temp_file_path).wrap_err(format!(
|
||||
"检查音频文件`{}`是否完整失败",
|
||||
temp_file_path.display()
|
||||
))?;
|
||||
|
||||
if !is_audio_file_complete {
|
||||
download_task.update_progress(|p| p.audio_task.mark_uncompleted());
|
||||
return Err(anyhow!(
|
||||
return Err(eyre!(
|
||||
"音频文件`{}`不完整,[继续]会重新下载所有分片",
|
||||
temp_file_path.display()
|
||||
));
|
||||
@@ -429,9 +429,9 @@ impl AudioTask {
|
||||
// 重命名临时文件
|
||||
if m4a_path.exists() {
|
||||
std::fs::remove_file(&m4a_path)
|
||||
.context(format!("删除已存在的音频文件`{}`失败", m4a_path.display()))?;
|
||||
.wrap_err(format!("删除已存在的音频文件`{}`失败", m4a_path.display()))?;
|
||||
}
|
||||
std::fs::rename(&temp_file_path, &m4a_path).context(format!(
|
||||
std::fs::rename(&temp_file_path, &m4a_path).wrap_err(format!(
|
||||
"将临时文件`{}`重命名为`{}`失败",
|
||||
temp_file_path.display(),
|
||||
m4a_path.display()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
@@ -30,18 +30,18 @@ impl CoverTask {
|
||||
&self,
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
|
||||
let bili_client = download_task.app.get_bili_client();
|
||||
let (cover_data, ext) = bili_client
|
||||
.get_cover_data_and_ext(&progress.cover_task.url)
|
||||
.await
|
||||
.context("获取封面失败")?;
|
||||
.wrap_err("获取封面失败")?;
|
||||
|
||||
let save_path = episode_dir.join(format!("{filename}.{ext}"));
|
||||
std::fs::write(&save_path, cover_data)
|
||||
.context(format!("保存封面到`{}`失败", save_path.display()))?;
|
||||
.wrap_err(format!("保存封面到`{}`失败", save_path.display()))?;
|
||||
|
||||
download_task.update_progress(|p| p.cover_task.completed = true);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{fs::File, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
@@ -37,7 +37,7 @@ impl DanmakuTask {
|
||||
&self,
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let danmaku_task = &progress.danmaku_task;
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let ids_string = progress.get_ids_string();
|
||||
@@ -66,29 +66,29 @@ impl DanmakuTask {
|
||||
let replies = bili_client
|
||||
.get_danmaku(progress.aid, progress.cid, progress.duration)
|
||||
.await
|
||||
.context("获取弹幕失败")?;
|
||||
.wrap_err("获取弹幕失败")?;
|
||||
|
||||
let xml = replies
|
||||
.to_xml(progress.cid)
|
||||
.context("将弹幕转换为XML失败")?;
|
||||
.wrap_err("将弹幕转换为XML失败")?;
|
||||
|
||||
if danmaku_task.xml_selected {
|
||||
std::fs::write(&xml_path, &xml)
|
||||
.context(format!("保存弹幕XML到`{}`失败", xml_path.display()))?;
|
||||
.wrap_err(format!("保存弹幕XML到`{}`失败", xml_path.display()))?;
|
||||
}
|
||||
|
||||
if danmaku_task.ass_selected {
|
||||
let config = download_task.app.get_config().read().danmaku_config.clone();
|
||||
let ass_file = File::create(&ass_path)
|
||||
.context(format!("创建弹幕ASS文件`{}`失败", ass_path.display()))?;
|
||||
.wrap_err(format!("创建弹幕ASS文件`{}`失败", ass_path.display()))?;
|
||||
let title = filename.clone();
|
||||
xml_to_ass(&xml, ass_file, title, config).context("将弹幕XML转换为ASS失败")?;
|
||||
xml_to_ass(&xml, ass_file, title, config).wrap_err("将弹幕XML转换为ASS失败")?;
|
||||
}
|
||||
|
||||
if danmaku_task.json_selected {
|
||||
let json_string = serde_json::to_string(&replies).context("将弹幕转换为JSON失败")?;
|
||||
let json_string = serde_json::to_string(&replies).wrap_err("将弹幕转换为JSON失败")?;
|
||||
std::fs::write(&json_path, json_string)
|
||||
.context(format!("保存弹幕JSON到`{}`失败", json_path.display()))?;
|
||||
.wrap_err(format!("保存弹幕JSON到`{}`失败", json_path.display()))?;
|
||||
}
|
||||
|
||||
download_task.update_progress(|p| p.danmaku_task.completed = true);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
@@ -31,7 +31,7 @@ impl JsonTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
episode_info: &mut Option<EpisodeInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
|
||||
let episode_info = episode_info
|
||||
@@ -41,17 +41,17 @@ impl JsonTask {
|
||||
let json_path = episode_dir.join(format!("{filename}-元数据.json"));
|
||||
let json_string = match episode_info {
|
||||
EpisodeInfo::Normal(info) => {
|
||||
serde_json::to_string(&info).context("将普通视频信息转换为JSON失败")?
|
||||
serde_json::to_string(&info).wrap_err("将普通视频信息转换为JSON失败")?
|
||||
}
|
||||
EpisodeInfo::Bangumi(info, _ep_id) => {
|
||||
serde_json::to_string(&info).context("将番剧信息转换为JSON失败")?
|
||||
serde_json::to_string(&info).wrap_err("将番剧信息转换为JSON失败")?
|
||||
}
|
||||
EpisodeInfo::Cheese(info, _ep_id) => {
|
||||
serde_json::to_string(&info).context("将课程信息转换为JSON失败")?
|
||||
serde_json::to_string(&info).wrap_err("将课程信息转换为JSON失败")?
|
||||
}
|
||||
};
|
||||
std::fs::write(&json_path, json_string)
|
||||
.context(format!("保存JSON到`{}`失败", json_path.display()))?;
|
||||
.wrap_err(format!("保存JSON到`{}`失败", json_path.display()))?;
|
||||
|
||||
download_task.update_progress(|p| p.json_task.completed = true);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use chrono::{DateTime, Datelike, NaiveDateTime};
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use yaserde::{YaDeserialize, YaSerialize};
|
||||
@@ -42,7 +42,7 @@ impl NfoTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
episode_info: &mut Option<EpisodeInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let episode_info = episode_info
|
||||
.get_or_init(&download_task.app, progress)
|
||||
.await?;
|
||||
@@ -69,7 +69,7 @@ impl NfoTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
info: &NormalInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let ids_string = progress.get_ids_string();
|
||||
let nfo_path = episode_dir.join(format!("{filename}.nfo"));
|
||||
@@ -89,21 +89,21 @@ impl NfoTask {
|
||||
let tags = bili_client
|
||||
.get_tags(progress.aid)
|
||||
.await
|
||||
.context("获取视频标签失败")?;
|
||||
.wrap_err("获取视频标签失败")?;
|
||||
let movie_nfo = info
|
||||
.to_movie_nfo(tags)
|
||||
.context("将普通视频信息转换为movie NFO失败")?;
|
||||
.wrap_err("将普通视频信息转换为movie NFO失败")?;
|
||||
std::fs::write(&nfo_path, movie_nfo)
|
||||
.context(format!("保存普通视频NFO到`{}`失败", nfo_path.display()))?;
|
||||
.wrap_err(format!("保存普通视频NFO到`{}`失败", nfo_path.display()))?;
|
||||
|
||||
if let Some(ugc_season) = &info.ugc_season {
|
||||
let collection_cover = &ugc_season.cover;
|
||||
let (cover_data, ext) = bili_client
|
||||
.get_cover_data_and_ext(collection_cover)
|
||||
.await
|
||||
.context("获取普通视频合集封面失败")?;
|
||||
.wrap_err("获取普通视频合集封面失败")?;
|
||||
let cover_path = episode_dir.join(format!("poster.{ext}"));
|
||||
std::fs::write(&cover_path, cover_data).context(format!(
|
||||
std::fs::write(&cover_path, cover_data).wrap_err(format!(
|
||||
"保存普通视频合集封面到`{}`失败",
|
||||
cover_path.display()
|
||||
))?;
|
||||
@@ -120,7 +120,7 @@ impl NfoTask {
|
||||
progress: &DownloadProgress,
|
||||
info: &BangumiInfo,
|
||||
ep_id: &i64,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let ids_string = progress.get_ids_string();
|
||||
let episode_details_nfo_path = episode_dir.join(format!("{filename}.nfo"));
|
||||
@@ -139,16 +139,16 @@ impl NfoTask {
|
||||
|
||||
let tvshow_nfo = info
|
||||
.to_tvshow_nfo()
|
||||
.context("将番剧信息转换为tvshow NFO失败")?;
|
||||
.wrap_err("将番剧信息转换为tvshow NFO失败")?;
|
||||
let tvshow_nfo_path = episode_dir.join("tvshow.nfo");
|
||||
std::fs::write(&tvshow_nfo_path, tvshow_nfo)
|
||||
.context(format!("保存番剧NFO到`{}`失败", tvshow_nfo_path.display()))?;
|
||||
.wrap_err(format!("保存番剧NFO到`{}`失败", tvshow_nfo_path.display()))?;
|
||||
|
||||
let episode_details_nfo = info
|
||||
.to_episode_details_nfo(*ep_id)
|
||||
.context("将番剧信息转换为episodedetail NFO失败")?;
|
||||
.wrap_err("将番剧信息转换为episodedetail NFO失败")?;
|
||||
let episode_details_nfo_path = episode_dir.join(format!("{filename}.nfo"));
|
||||
std::fs::write(&episode_details_nfo_path, episode_details_nfo).context(format!(
|
||||
std::fs::write(&episode_details_nfo_path, episode_details_nfo).wrap_err(format!(
|
||||
"保存番剧NFO到`{}`失败",
|
||||
episode_details_nfo_path.display()
|
||||
))?;
|
||||
@@ -157,20 +157,20 @@ impl NfoTask {
|
||||
let (poster_data, ext) = bili_client
|
||||
.get_cover_data_and_ext(poster_url)
|
||||
.await
|
||||
.context("获取番剧封面失败")?;
|
||||
.wrap_err("获取番剧封面失败")?;
|
||||
let poster_path = episode_dir.join(format!("poster.{ext}"));
|
||||
std::fs::write(&poster_path, poster_data)
|
||||
.context(format!("保存番剧封面到`{}`失败", poster_path.display()))?;
|
||||
.wrap_err(format!("保存番剧封面到`{}`失败", poster_path.display()))?;
|
||||
|
||||
let fanart_url = &info.bkg_cover;
|
||||
if !fanart_url.is_empty() {
|
||||
let (fanart_data, ext) = bili_client
|
||||
.get_cover_data_and_ext(fanart_url)
|
||||
.await
|
||||
.context("获取番剧封面失败")?;
|
||||
.wrap_err("获取番剧封面失败")?;
|
||||
let fanart_path = episode_dir.join(format!("fanart.{ext}"));
|
||||
std::fs::write(&fanart_path, fanart_data)
|
||||
.context(format!("保存番剧封面到`{}`失败", fanart_path.display()))?;
|
||||
.wrap_err(format!("保存番剧封面到`{}`失败", fanart_path.display()))?;
|
||||
}
|
||||
|
||||
download_task.update_progress(|p| p.nfo_task.completed = true);
|
||||
@@ -184,7 +184,7 @@ impl NfoTask {
|
||||
progress: &DownloadProgress,
|
||||
info: &CheeseInfo,
|
||||
ep_id: &i64,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let ids_string = progress.get_ids_string();
|
||||
let episode_details_nfo_path = episode_dir.join(format!("{filename}.nfo"));
|
||||
@@ -203,15 +203,15 @@ impl NfoTask {
|
||||
|
||||
let tvshow_nfo = info
|
||||
.to_tvshow_nfo()
|
||||
.context("将课程信息转换为tvshow NFO失败")?;
|
||||
.wrap_err("将课程信息转换为tvshow NFO失败")?;
|
||||
let tvshow_nfo_path = episode_dir.join("tvshow.nfo");
|
||||
std::fs::write(&tvshow_nfo_path, tvshow_nfo)
|
||||
.context(format!("保存课程NFO到`{}`失败", tvshow_nfo_path.display()))?;
|
||||
.wrap_err(format!("保存课程NFO到`{}`失败", tvshow_nfo_path.display()))?;
|
||||
|
||||
let episode_details_nfo = info
|
||||
.to_episode_details_nfo(*ep_id)
|
||||
.context("将课程信息转换为episodedetail NFO失败")?;
|
||||
std::fs::write(&episode_details_nfo_path, episode_details_nfo).context(format!(
|
||||
.wrap_err("将课程信息转换为episodedetail NFO失败")?;
|
||||
std::fs::write(&episode_details_nfo_path, episode_details_nfo).wrap_err(format!(
|
||||
"保存课程NFO到`{}`失败",
|
||||
episode_details_nfo_path.display()
|
||||
))?;
|
||||
@@ -220,10 +220,10 @@ impl NfoTask {
|
||||
let (poster_data, ext) = bili_client
|
||||
.get_cover_data_and_ext(poster_url)
|
||||
.await
|
||||
.context("获取课程封面失败")?;
|
||||
.wrap_err("获取课程封面失败")?;
|
||||
let poster_path = episode_dir.join(format!("poster.{ext}"));
|
||||
std::fs::write(&poster_path, poster_data)
|
||||
.context(format!("保存课程封面到`{}`失败", poster_path.display()))?;
|
||||
.wrap_err(format!("保存课程封面到`{}`失败", poster_path.display()))?;
|
||||
|
||||
download_task.update_progress(|p| p.nfo_task.completed = true);
|
||||
|
||||
@@ -299,7 +299,7 @@ struct EpisodeDetails {
|
||||
}
|
||||
|
||||
impl NormalInfo {
|
||||
pub fn to_movie_nfo(&self, tags: Tags) -> anyhow::Result<String> {
|
||||
pub fn to_movie_nfo(&self, tags: Tags) -> eyre::Result<String> {
|
||||
let genre = vec![
|
||||
"Bilibili视频".to_string(),
|
||||
self.tname.clone(),
|
||||
@@ -314,7 +314,7 @@ impl NormalInfo {
|
||||
|
||||
let ts = self.pubdate;
|
||||
let date_time = DateTime::from_timestamp(ts, 0)
|
||||
.context(format!("将视频发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.ok_or_eyre(format!("将视频发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.with_timezone(&chrono::Local);
|
||||
|
||||
let set = self.ugc_season.as_ref().map(|ugc_season| Set {
|
||||
@@ -357,16 +357,16 @@ impl NormalInfo {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let nfo = yaserde::ser::to_string_with_config(&movie, &cfg).map_err(|e| anyhow!(e))?;
|
||||
let nfo = yaserde::ser::to_string_with_config(&movie, &cfg).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(nfo)
|
||||
}
|
||||
}
|
||||
|
||||
impl BangumiInfo {
|
||||
pub fn to_tvshow_nfo(&self) -> anyhow::Result<String> {
|
||||
pub fn to_tvshow_nfo(&self) -> eyre::Result<String> {
|
||||
let time_str = &self.publish.pub_time;
|
||||
let date_time = NaiveDateTime::parse_from_str(time_str, "%Y-%m-%d %H:%M:%S").context(
|
||||
let date_time = NaiveDateTime::parse_from_str(time_str, "%Y-%m-%d %H:%M:%S").wrap_err(
|
||||
format!("将番剧发布时间字符串转换为日期时间失败: {time_str}"),
|
||||
)?;
|
||||
|
||||
@@ -394,30 +394,30 @@ impl BangumiInfo {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let nfo = yaserde::ser::to_string_with_config(&tv_show, &cfg).map_err(|e| anyhow!(e))?;
|
||||
let nfo = yaserde::ser::to_string_with_config(&tv_show, &cfg).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(nfo)
|
||||
}
|
||||
|
||||
pub fn to_episode_details_nfo(&self, ep_id: i64) -> anyhow::Result<String> {
|
||||
pub fn to_episode_details_nfo(&self, ep_id: i64) -> eyre::Result<String> {
|
||||
let (episode, episode_order) = self.get_episode_with_order(ep_id)?;
|
||||
|
||||
let ts = episode.pub_time;
|
||||
let date_time = DateTime::from_timestamp(ts, 0)
|
||||
.context(format!("将番剧发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.ok_or_eyre(format!("将番剧发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.with_timezone(&chrono::Local);
|
||||
|
||||
let title = episode
|
||||
.show_title
|
||||
.clone()
|
||||
.context("episode.show_title为None")?;
|
||||
.ok_or_eyre("episode.show_title为None")?;
|
||||
|
||||
let plot = episode
|
||||
.share_copy
|
||||
.clone()
|
||||
.context("episode.share_copy为None")?;
|
||||
.ok_or_eyre("episode.share_copy为None")?;
|
||||
|
||||
let duration = episode.duration.context("episode.duration为None")?;
|
||||
let duration = episode.duration.ok_or_eyre("episode.duration为None")?;
|
||||
|
||||
let episode_details = EpisodeDetails {
|
||||
title,
|
||||
@@ -440,7 +440,7 @@ impl BangumiInfo {
|
||||
};
|
||||
|
||||
let nfo =
|
||||
yaserde::ser::to_string_with_config(&episode_details, &cfg).map_err(|e| anyhow!(e))?;
|
||||
yaserde::ser::to_string_with_config(&episode_details, &cfg).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(nfo)
|
||||
}
|
||||
@@ -489,11 +489,11 @@ impl BangumiInfo {
|
||||
}
|
||||
|
||||
impl CheeseInfo {
|
||||
pub fn to_tvshow_nfo(&self) -> anyhow::Result<String> {
|
||||
let episode = self.episodes.first().context("episodes列表为空")?;
|
||||
pub fn to_tvshow_nfo(&self) -> eyre::Result<String> {
|
||||
let episode = self.episodes.first().ok_or_eyre("episodes列表为空")?;
|
||||
let ts = episode.release_date;
|
||||
let date_time = DateTime::from_timestamp(ts, 0)
|
||||
.context(format!("将课程的发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.ok_or_eyre(format!("将课程的发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.with_timezone(&chrono::Local);
|
||||
|
||||
let status = match self.release_status.as_str() {
|
||||
@@ -520,21 +520,21 @@ impl CheeseInfo {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let nfo = yaserde::ser::to_string_with_config(&tv_show, &cfg).map_err(|e| anyhow!(e))?;
|
||||
let nfo = yaserde::ser::to_string_with_config(&tv_show, &cfg).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(nfo)
|
||||
}
|
||||
|
||||
pub fn to_episode_details_nfo(&self, ep_id: i64) -> anyhow::Result<String> {
|
||||
pub fn to_episode_details_nfo(&self, ep_id: i64) -> eyre::Result<String> {
|
||||
let episode = self
|
||||
.episodes
|
||||
.iter()
|
||||
.find(|ep| ep.id == ep_id)
|
||||
.context(format!("找不到ep_id为`{ep_id}`的课程"))?;
|
||||
.ok_or_eyre(format!("找不到ep_id为`{ep_id}`的课程"))?;
|
||||
|
||||
let ts = episode.release_date;
|
||||
let date_time = DateTime::from_timestamp(ts, 0)
|
||||
.context(format!("将课程发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.ok_or_eyre(format!("将课程发布时间戳转换为日期时间失败: {ts}"))?
|
||||
.with_timezone(&chrono::Local);
|
||||
|
||||
let episode_details = EpisodeDetails {
|
||||
@@ -558,7 +558,7 @@ impl CheeseInfo {
|
||||
};
|
||||
|
||||
let nfo =
|
||||
yaserde::ser::to_string_with_config(&episode_details, &cfg).map_err(|e| anyhow!(e))?;
|
||||
yaserde::ser::to_string_with_config(&episode_details, &cfg).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(nfo)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
@@ -32,7 +32,7 @@ impl SubtitleTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
player_info: &mut Option<PlayerInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
use std::fmt::Write;
|
||||
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
@@ -48,7 +48,7 @@ impl SubtitleTask {
|
||||
let subtitle = bili_client
|
||||
.get_subtitle(&url)
|
||||
.await
|
||||
.context("获取字幕失败")?;
|
||||
.wrap_err("获取字幕失败")?;
|
||||
|
||||
let mut srt_content = String::new();
|
||||
for (i, b) in subtitle.body.iter().enumerate() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{WrapErr, eyre};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::AppHandle;
|
||||
@@ -43,21 +43,21 @@ impl VideoProcessTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
player_info: &mut Option<PlayerInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let embed_selected = self.embed_chapter_selected || self.embed_skip_selected;
|
||||
|
||||
if self.merge_selected && embed_selected {
|
||||
self.merge_and_embed(download_task, progress, player_info)
|
||||
.await
|
||||
.context("自动合并+嵌入章节元数据失败")?;
|
||||
.wrap_err("自动合并+嵌入章节元数据失败")?;
|
||||
} else if self.merge_selected {
|
||||
self.merge(download_task, progress)
|
||||
.await
|
||||
.context("自动合并失败")?;
|
||||
.wrap_err("自动合并失败")?;
|
||||
} else if embed_selected {
|
||||
self.embed(download_task, progress, player_info)
|
||||
.await
|
||||
.context("嵌入章节元数据失败")?;
|
||||
.wrap_err("嵌入章节元数据失败")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -68,10 +68,10 @@ impl VideoProcessTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
player_info: &mut Option<PlayerInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().context("获取FFmpeg程序路径失败")?;
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().wrap_err("获取FFmpeg程序路径失败")?;
|
||||
|
||||
let video_path = episode_dir.join(format!("{filename}.mp4"));
|
||||
if !video_path.exists() {
|
||||
@@ -84,14 +84,14 @@ impl VideoProcessTask {
|
||||
// 如果音频文件不存在,则只嵌入章节元数据
|
||||
self.embed(download_task, progress, player_info)
|
||||
.await
|
||||
.context("嵌入章节元数据失败")?;
|
||||
.wrap_err("嵌入章节元数据失败")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let metadata_path = self
|
||||
.create_chapter_metadata(&download_task.app, progress, player_info)
|
||||
.await
|
||||
.context("创建章节元数据失败")?;
|
||||
.wrap_err("创建章节元数据失败")?;
|
||||
|
||||
let output_path = episode_dir.join(format!("{filename}-merged.mp4"));
|
||||
|
||||
@@ -134,24 +134,24 @@ impl VideoProcessTask {
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let err = anyhow!(format!("STDOUT: {stdout}"))
|
||||
.context(format!("STDERR: {stderr}"))
|
||||
.context("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
let err = eyre!(format!("STDOUT: {stdout}"))
|
||||
.wrap_err(format!("STDERR: {stderr}"))
|
||||
.wrap_err("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
std::fs::remove_file(&video_path)
|
||||
.context(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
.wrap_err(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
std::fs::remove_file(&audio_path)
|
||||
.context(format!("删除音频文件`{}`失败", audio_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).context(format!(
|
||||
.wrap_err(format!("删除音频文件`{}`失败", audio_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).wrap_err(format!(
|
||||
"将`{}`重命名为`{}`失败",
|
||||
output_path.display(),
|
||||
video_path.display()
|
||||
))?;
|
||||
|
||||
if let Some(metadata_path) = metadata_path {
|
||||
std::fs::remove_file(&metadata_path).context(format!(
|
||||
std::fs::remove_file(&metadata_path).wrap_err(format!(
|
||||
"删除章节元数据文件`{}`失败",
|
||||
metadata_path.display()
|
||||
))?;
|
||||
@@ -166,7 +166,7 @@ impl VideoProcessTask {
|
||||
&self,
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
|
||||
let video_path = episode_dir.join(format!("{filename}.mp4"));
|
||||
@@ -183,7 +183,7 @@ impl VideoProcessTask {
|
||||
|
||||
let output_path = episode_dir.join(format!("{filename}-merged.mp4"));
|
||||
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().context("获取FFmpeg程序路径失败")?;
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().wrap_err("获取FFmpeg程序路径失败")?;
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
let video_path_clone = video_path.clone();
|
||||
@@ -219,17 +219,17 @@ impl VideoProcessTask {
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let err = anyhow!(format!("STDOUT: {stdout}"))
|
||||
.context(format!("STDERR: {stderr}"))
|
||||
.context("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
let err = eyre!(format!("STDOUT: {stdout}"))
|
||||
.wrap_err(format!("STDERR: {stderr}"))
|
||||
.wrap_err("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
std::fs::remove_file(&video_path)
|
||||
.context(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
.wrap_err(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
std::fs::remove_file(&audio_path)
|
||||
.context(format!("删除音频文件`{}`失败", audio_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).context(format!(
|
||||
.wrap_err(format!("删除音频文件`{}`失败", audio_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).wrap_err(format!(
|
||||
"将`{}`重命名为`{}`失败",
|
||||
output_path.display(),
|
||||
video_path.display()
|
||||
@@ -245,10 +245,10 @@ impl VideoProcessTask {
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
player_info: &mut Option<PlayerInfo>,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().context("获取FFmpeg程序路径失败")?;
|
||||
let ffmpeg_program = utils::get_ffmpeg_program().wrap_err("获取FFmpeg程序路径失败")?;
|
||||
|
||||
let video_path = episode_dir.join(format!("{filename}.mp4"));
|
||||
if !video_path.exists() {
|
||||
@@ -261,7 +261,7 @@ impl VideoProcessTask {
|
||||
let metadata_path = self
|
||||
.create_chapter_metadata(&download_task.app, progress, player_info)
|
||||
.await
|
||||
.context("创建章节元数据失败")?;
|
||||
.wrap_err("创建章节元数据失败")?;
|
||||
|
||||
let Some(metadata_path) = metadata_path else {
|
||||
download_task.update_progress(|p| p.video_process_task.completed = true);
|
||||
@@ -301,20 +301,20 @@ impl VideoProcessTask {
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let err = anyhow!(format!("STDOUT: {stdout}"))
|
||||
.context(format!("STDERR: {stderr}"))
|
||||
.context("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
let err = eyre!(format!("STDOUT: {stdout}"))
|
||||
.wrap_err(format!("STDERR: {stderr}"))
|
||||
.wrap_err("原因可能是视频或音频文件损坏,建议[重来]试试");
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
std::fs::remove_file(&video_path)
|
||||
.context(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).context(format!(
|
||||
.wrap_err(format!("删除视频文件`{}`失败", video_path.display()))?;
|
||||
std::fs::rename(&output_path, &video_path).wrap_err(format!(
|
||||
"将`{}`重命名为`{}`失败",
|
||||
output_path.display(),
|
||||
video_path.display()
|
||||
))?;
|
||||
std::fs::remove_file(&metadata_path).context(format!(
|
||||
std::fs::remove_file(&metadata_path).wrap_err(format!(
|
||||
"删除章节元数据文件`{}`失败",
|
||||
metadata_path.display()
|
||||
))?;
|
||||
@@ -329,7 +329,7 @@ impl VideoProcessTask {
|
||||
app: &AppHandle,
|
||||
progress: &DownloadProgress,
|
||||
player_info: &mut Option<PlayerInfo>,
|
||||
) -> anyhow::Result<Option<PathBuf>> {
|
||||
) -> eyre::Result<Option<PathBuf>> {
|
||||
let mut chapter_segments = ChapterSegments {
|
||||
segments: Vec::new(),
|
||||
};
|
||||
@@ -368,7 +368,7 @@ impl VideoProcessTask {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let metadata_path = episode_dir.join(format!("{filename}.FFMETA.ini"));
|
||||
std::fs::write(&metadata_path, metadata_content)
|
||||
.context(format!("保存章节元数据到`{}`失败", metadata_path.display()))?;
|
||||
.wrap_err(format!("保存章节元数据到`{}`失败", metadata_path.display()))?;
|
||||
|
||||
Ok(Some(metadata_path))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
use fs4::fs_std::FileExt;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
download_chunk_task::DownloadChunkTask, download_progress::DownloadProgress,
|
||||
download_task::DownloadTask, media_chunk::MediaChunk,
|
||||
},
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
types::{
|
||||
bangumi_media_url::BangumiMediaUrl, cheese_media_url::CheeseMediaUrl,
|
||||
codec_type::CodecType, normal_media_url::NormalMediaUrl, video_quality::VideoQuality,
|
||||
@@ -46,7 +46,7 @@ impl VideoTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &NormalMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let mut join_set = JoinSet::new();
|
||||
|
||||
for media in &media_url.dash.video {
|
||||
@@ -110,7 +110,7 @@ impl VideoTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &BangumiMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let mut medias: Vec<MediaForPrepare> = Vec::new();
|
||||
|
||||
let mut join_set = JoinSet::new();
|
||||
@@ -180,7 +180,7 @@ impl VideoTask {
|
||||
&mut self,
|
||||
app: &AppHandle,
|
||||
media_url: &CheeseMediaUrl,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let mut medias: Vec<MediaForPrepare> = Vec::new();
|
||||
|
||||
let mut join_set = JoinSet::new();
|
||||
@@ -246,16 +246,16 @@ impl VideoTask {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare(&mut self, app: &AppHandle, medias: &[MediaForPrepare]) -> anyhow::Result<()> {
|
||||
fn prepare(&mut self, app: &AppHandle, medias: &[MediaForPrepare]) -> eyre::Result<()> {
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取视频地址失败,medias为空"));
|
||||
return Err(eyre!("获取视频地址失败,medias为空"));
|
||||
}
|
||||
|
||||
let video_quality_is_unknown = self.video_quality == VideoQuality::Unknown;
|
||||
let codec_type_is_unknown = self.codec_type == CodecType::Unknown;
|
||||
|
||||
if video_quality_is_unknown != codec_type_is_unknown {
|
||||
return Err(anyhow!(
|
||||
return Err(eyre!(
|
||||
"`video_quality`和`codec_type`必须同时为`Unknown`或同时不为`Unknown`"
|
||||
));
|
||||
}
|
||||
@@ -269,7 +269,7 @@ impl VideoTask {
|
||||
select_exact_match_media(self, medias).or_else(|| select_media_by_priority(app, medias))
|
||||
};
|
||||
|
||||
let media = selected_media.context("获取视频地址失败,medias为空")?;
|
||||
let media = selected_media.ok_or_eyre("获取视频地址失败,medias为空")?;
|
||||
|
||||
self.video_quality = media.id.into();
|
||||
self.codec_type = media.codecid.into();
|
||||
@@ -322,7 +322,7 @@ impl VideoTask {
|
||||
&self,
|
||||
download_task: &Arc<DownloadTask>,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<()> {
|
||||
) -> eyre::Result<()> {
|
||||
let (episode_dir, filename) = (&progress.episode_dir, &progress.filename);
|
||||
let (video_task, episode_title, ids_string) = {
|
||||
let progress = download_task.progress.read();
|
||||
@@ -389,7 +389,7 @@ impl VideoTask {
|
||||
let chunk_order = i + 1;
|
||||
|
||||
join_set.spawn(async move {
|
||||
download_chunk_task.process().await.context(format!(
|
||||
download_chunk_task.process().await.wrap_err(format!(
|
||||
"分片`{chunk_order}/{chunk_count}`下载失败({start}-{end})"
|
||||
))
|
||||
});
|
||||
@@ -418,20 +418,20 @@ impl VideoTask {
|
||||
.iter()
|
||||
.all(|chunk| chunk.completed);
|
||||
if !download_completed {
|
||||
return Err(anyhow!(
|
||||
return Err(eyre!(
|
||||
"视频文件`{}`有分片未下载完成,[继续]可以跳过已下载分片断点续传",
|
||||
temp_file_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
let is_video_file_complete = utils::is_mp4_complete(&temp_file_path).context(format!(
|
||||
let is_video_file_complete = utils::is_mp4_complete(&temp_file_path).wrap_err(format!(
|
||||
"检查视频文件`{}`是否完整失败",
|
||||
temp_file_path.display()
|
||||
))?;
|
||||
|
||||
if !is_video_file_complete {
|
||||
download_task.update_progress(|p| p.video_task.mark_uncompleted());
|
||||
return Err(anyhow!(
|
||||
return Err(eyre!(
|
||||
"视频文件`{}`不完整,[继续]会重新下载所有分片",
|
||||
temp_file_path.display()
|
||||
));
|
||||
@@ -440,9 +440,9 @@ impl VideoTask {
|
||||
// 重命名临时文件
|
||||
if mp4_path.exists() {
|
||||
std::fs::remove_file(&mp4_path)
|
||||
.context(format!("删除已存在的视频文件`{}`失败", mp4_path.display()))?;
|
||||
.wrap_err(format!("删除已存在的视频文件`{}`失败", mp4_path.display()))?;
|
||||
}
|
||||
std::fs::rename(&temp_file_path, &mp4_path).context(format!(
|
||||
std::fs::rename(&temp_file_path, &mp4_path).wrap_err(format!(
|
||||
"将临时文件`{}`重命名为`{}`失败",
|
||||
temp_file_path.display(),
|
||||
mp4_path.display()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
use crate::extensions::AnyhowErrorToStringChain;
|
||||
use crate::extensions::EyreToStringChain;
|
||||
|
||||
pub type CommandResult<T> = Result<T, CommandError>;
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct CommandError {
|
||||
impl CommandError {
|
||||
pub fn from<E>(err_title: &str, err: E) -> Self
|
||||
where
|
||||
E: Into<anyhow::Error>,
|
||||
E: Into<eyre::Report>,
|
||||
{
|
||||
let string_chain = err.into().to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::Context;
|
||||
use eyre::WrapErr;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::{
|
||||
types::player_info::PlayerInfo,
|
||||
};
|
||||
|
||||
pub trait AnyhowErrorToStringChain {
|
||||
/// 将 `anyhow::Error` 转换为chain格式
|
||||
pub trait EyreToStringChain {
|
||||
/// 将 `eyre::Report` 转换为chain格式
|
||||
/// # Example
|
||||
/// 0: error message\
|
||||
/// 1: error message\
|
||||
@@ -18,7 +18,7 @@ pub trait AnyhowErrorToStringChain {
|
||||
fn to_string_chain(&self) -> String;
|
||||
}
|
||||
|
||||
impl AnyhowErrorToStringChain for anyhow::Error {
|
||||
impl EyreToStringChain for eyre::Report {
|
||||
fn to_string_chain(&self) -> String {
|
||||
use std::fmt::Write;
|
||||
self.chain()
|
||||
@@ -53,7 +53,7 @@ pub trait GetOrInitPlayerInfo {
|
||||
&'a mut self,
|
||||
app: &AppHandle,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<&'a mut PlayerInfo>;
|
||||
) -> eyre::Result<&'a mut PlayerInfo>;
|
||||
}
|
||||
|
||||
impl GetOrInitPlayerInfo for Option<PlayerInfo> {
|
||||
@@ -61,7 +61,7 @@ impl GetOrInitPlayerInfo for Option<PlayerInfo> {
|
||||
&'a mut self,
|
||||
app: &AppHandle,
|
||||
progress: &DownloadProgress,
|
||||
) -> anyhow::Result<&'a mut PlayerInfo> {
|
||||
) -> eyre::Result<&'a mut PlayerInfo> {
|
||||
if let Some(info) = self {
|
||||
return Ok(info);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ impl GetOrInitPlayerInfo for Option<PlayerInfo> {
|
||||
let info = bili_client
|
||||
.get_player_info(progress.aid, progress.cid)
|
||||
.await
|
||||
.context("获取播放器信息失败")?;
|
||||
.wrap_err("获取播放器信息失败")?;
|
||||
|
||||
Ok(self.insert(info))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ mod protobuf {
|
||||
include!("./bilibili.community.service.dm.v1.rs");
|
||||
}
|
||||
|
||||
use anyhow::Context;
|
||||
use commands::{
|
||||
create_download_tasks, delete_download_tasks, generate_qrcode, get_available_media_formats,
|
||||
get_bangumi_follow_info, get_bangumi_info, get_config, get_fav_folders, get_fav_info,
|
||||
@@ -25,6 +24,7 @@ use commands::{
|
||||
save_config, search, show_path_in_file_manager,
|
||||
};
|
||||
use config::Config;
|
||||
use eyre::WrapErr;
|
||||
use parking_lot::RwLock;
|
||||
use tauri::{Manager, Wry};
|
||||
|
||||
@@ -99,9 +99,9 @@ pub fn run() {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.context("获取app_data_dir目录失败")?;
|
||||
.wrap_err("获取app_data_dir目录失败")?;
|
||||
|
||||
std::fs::create_dir_all(&app_data_dir).context(format!(
|
||||
std::fs::create_dir_all(&app_data_dir).wrap_err(format!(
|
||||
"创建app_data_dir目录`{:?}`失败",
|
||||
app_data_dir.display()
|
||||
))?;
|
||||
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
use std::{io::Write, sync::OnceLock};
|
||||
|
||||
use anyhow::Context;
|
||||
use eyre::{OptionExt, WrapErr};
|
||||
use notify::{RecommendedWatcher, Watcher};
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri_specta::Event;
|
||||
@@ -20,7 +20,7 @@ use tracing_subscriber::{
|
||||
|
||||
use crate::{
|
||||
events::LogEvent,
|
||||
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||
extensions::{AppHandleExt, EyreToStringChain},
|
||||
};
|
||||
|
||||
struct LogEventWriter {
|
||||
@@ -48,12 +48,12 @@ impl Write for LogEventWriter {
|
||||
}
|
||||
}
|
||||
|
||||
static RELOAD_FN: OnceLock<Box<dyn Fn() -> anyhow::Result<()> + Send + Sync>> = OnceLock::new();
|
||||
static RELOAD_FN: OnceLock<Box<dyn Fn() -> eyre::Result<()> + Send + Sync>> = OnceLock::new();
|
||||
static GUARD: OnceLock<parking_lot::Mutex<Option<WorkerGuard>>> = OnceLock::new();
|
||||
|
||||
pub fn init(app: &AppHandle) -> anyhow::Result<()> {
|
||||
pub fn init(app: &AppHandle) -> eyre::Result<()> {
|
||||
let lib_module_path = module_path!();
|
||||
let lib_target = lib_module_path.split("::").next().context(format!(
|
||||
let lib_target = lib_module_path.split("::").next().ok_or_eyre(format!(
|
||||
"解析lib_target失败: lib_module_path={lib_module_path}"
|
||||
))?;
|
||||
// 过滤掉来自其他库的日志
|
||||
@@ -92,8 +92,8 @@ pub fn init(app: &AppHandle) -> anyhow::Result<()> {
|
||||
let app = app.clone();
|
||||
Box::new(move || {
|
||||
let (file_layer, guard) = create_file_layer(&app)?;
|
||||
reload_handle.reload(file_layer).context("reload失败")?;
|
||||
*GUARD.get().context("GUARD未初始化")?.lock() = guard;
|
||||
reload_handle.reload(file_layer).wrap_err("reload失败")?;
|
||||
*GUARD.get().ok_or_eyre("GUARD未初始化")?.lock() = guard;
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
@@ -102,12 +102,12 @@ pub fn init(app: &AppHandle) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn reload_file_logger() -> anyhow::Result<()> {
|
||||
RELOAD_FN.get().context("RELOAD_FN未初始化")?()
|
||||
pub fn reload_file_logger() -> eyre::Result<()> {
|
||||
RELOAD_FN.get().ok_or_eyre("RELOAD_FN未初始化")?()
|
||||
}
|
||||
|
||||
pub fn disable_file_logger() -> anyhow::Result<()> {
|
||||
if let Some(guard) = GUARD.get().context("GUARD未初始化")?.lock().take() {
|
||||
pub fn disable_file_logger() -> eyre::Result<()> {
|
||||
if let Some(guard) = GUARD.get().ok_or_eyre("GUARD未初始化")?.lock().take() {
|
||||
drop(guard);
|
||||
}
|
||||
Ok(())
|
||||
@@ -115,7 +115,7 @@ pub fn disable_file_logger() -> anyhow::Result<()> {
|
||||
|
||||
fn create_file_layer<S>(
|
||||
app: &AppHandle,
|
||||
) -> anyhow::Result<(Box<dyn Layer<S> + Send + Sync>, Option<WorkerGuard>)>
|
||||
) -> eyre::Result<(Box<dyn Layer<S> + Send + Sync>, Option<WorkerGuard>)>
|
||||
where
|
||||
S: Subscriber + for<'a> LookupSpan<'a>,
|
||||
{
|
||||
@@ -130,13 +130,13 @@ where
|
||||
.with_line_number(true);
|
||||
return Ok((Box::new(sink_layer), None));
|
||||
}
|
||||
let logs_dir = logs_dir(app).context("获取日志目录失败")?;
|
||||
let logs_dir = logs_dir(app).wrap_err("获取日志目录失败")?;
|
||||
let file_appender = RollingFileAppender::builder()
|
||||
.filename_prefix("bilibili-video-downloader")
|
||||
.filename_suffix("log")
|
||||
.rotation(Rotation::DAILY)
|
||||
.build(&logs_dir)
|
||||
.context("创建RollingFileAppender失败")?;
|
||||
.wrap_err("创建RollingFileAppender失败")?;
|
||||
let (non_blocking_appender, guard) = tracing_appender::non_blocking(file_appender);
|
||||
let file_layer = layer()
|
||||
.with_writer(non_blocking_appender)
|
||||
@@ -152,7 +152,7 @@ async fn file_log_watcher(app: AppHandle) {
|
||||
|
||||
let event_handler = move |res| {
|
||||
tauri::async_runtime::block_on(async {
|
||||
if let Err(err) = sender.send(res).await.map_err(anyhow::Error::from) {
|
||||
if let Err(err) = sender.send(res).await.map_err(eyre::Report::from) {
|
||||
let err_title = "发送日志文件watcher事件失败";
|
||||
let string_chain = err.to_string_chain();
|
||||
tracing::error!(err_title, message = string_chain);
|
||||
@@ -161,7 +161,7 @@ async fn file_log_watcher(app: AppHandle) {
|
||||
};
|
||||
|
||||
let mut watcher = match RecommendedWatcher::new(event_handler, notify::Config::default())
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(eyre::Report::from)
|
||||
{
|
||||
Ok(watcher) => watcher,
|
||||
Err(err) => {
|
||||
@@ -184,7 +184,7 @@ async fn file_log_watcher(app: AppHandle) {
|
||||
|
||||
if let Err(err) = watcher
|
||||
.watch(&logs_dir, notify::RecursiveMode::NonRecursive)
|
||||
.map_err(anyhow::Error::from)
|
||||
.map_err(eyre::Report::from)
|
||||
{
|
||||
let err_title = "日志文件watcher监听日志目录失败";
|
||||
let string_chain = err.to_string_chain();
|
||||
@@ -193,7 +193,7 @@ async fn file_log_watcher(app: AppHandle) {
|
||||
}
|
||||
|
||||
while let Some(res) = receiver.recv().await {
|
||||
match res.map_err(anyhow::Error::from) {
|
||||
match res.map_err(eyre::Report::from) {
|
||||
Ok(event) => {
|
||||
if let notify::EventKind::Remove(_) = event.kind
|
||||
&& let Err(err) = reload_file_logger()
|
||||
@@ -212,10 +212,10 @@ async fn file_log_watcher(app: AppHandle) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logs_dir(app: &AppHandle) -> anyhow::Result<std::path::PathBuf> {
|
||||
pub fn logs_dir(app: &AppHandle) -> eyre::Result<std::path::PathBuf> {
|
||||
let app_data_dir = app
|
||||
.path()
|
||||
.app_data_dir()
|
||||
.context("获取app_data_dir目录失败")?;
|
||||
.wrap_err("获取app_data_dir目录失败")?;
|
||||
Ok(app_data_dir.join("日志"))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{OptionExt, eyre};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
@@ -55,7 +55,7 @@ pub struct BangumiInfo {
|
||||
|
||||
impl BangumiInfo {
|
||||
#[allow(clippy::cast_possible_wrap)]
|
||||
pub fn get_episode_with_order(&self, ep_id: i64) -> anyhow::Result<(&EpInBangumi, i64)> {
|
||||
pub fn get_episode_with_order(&self, ep_id: i64) -> eyre::Result<(&EpInBangumi, i64)> {
|
||||
let episode_with_order = self
|
||||
.episodes
|
||||
.iter()
|
||||
@@ -69,19 +69,19 @@ impl BangumiInfo {
|
||||
} else {
|
||||
// 如果在正片中没有找到对应的ep_id,则在section中查找
|
||||
let Some(sections) = &self.section else {
|
||||
return Err(anyhow!("找不到对应的ep_id为`{ep_id}`的番剧"));
|
||||
return Err(eyre!("找不到对应的ep_id为`{ep_id}`的番剧"));
|
||||
};
|
||||
let section_index = sections
|
||||
.iter()
|
||||
.position(|s| s.episodes.iter().any(|e| e.id == ep_id))
|
||||
.context(format!("找不到含有ep_id为`{ep_id}`的ep的section"))?;
|
||||
.ok_or_eyre(format!("找不到含有ep_id为`{ep_id}`的ep的section"))?;
|
||||
sections[section_index]
|
||||
.episodes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, e)| (e, i as i64 + 1))
|
||||
.find(|(e, _)| e.id == ep_id)
|
||||
.context(format!("在section中找不到ep_id为`{ep_id}`的ep"))?
|
||||
.ok_or_eyre(format!("在section中找不到ep_id为`{ep_id}`的ep"))?
|
||||
};
|
||||
|
||||
Ok(episode_with_order)
|
||||
|
||||
+12
-12
@@ -4,8 +4,8 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
|
||||
use crate::{
|
||||
danmaku_xml_to_ass::{DamakuXmlDTag, DanmakuXmlITag},
|
||||
@@ -48,11 +48,11 @@ impl From<u32> for BoxSizeField {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mp4_complete(file_path: &Path) -> anyhow::Result<bool> {
|
||||
let file = File::open(file_path).context(format!("打开文件`{}`失败", file_path.display()))?;
|
||||
pub fn is_mp4_complete(file_path: &Path) -> eyre::Result<bool> {
|
||||
let file = File::open(file_path).wrap_err(format!("打开文件`{}`失败", file_path.display()))?;
|
||||
let real_size = file
|
||||
.metadata()
|
||||
.context(format!("获取文件`{}`元数据失败", file_path.display()))?
|
||||
.wrap_err(format!("获取文件`{}`元数据失败", file_path.display()))?
|
||||
.len();
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut total_size: u64 = 0;
|
||||
@@ -65,7 +65,7 @@ pub fn is_mp4_complete(file_path: &Path) -> anyhow::Result<bool> {
|
||||
let box_size_field: BoxSizeField = match reader.read_u32::<BigEndian>() {
|
||||
Ok(s) => s.into(),
|
||||
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => break, // 正常结束
|
||||
Err(e) => return Err(anyhow!(e)),
|
||||
Err(e) => return Err(eyre!(e)),
|
||||
};
|
||||
// 读取Box类型字段
|
||||
let mut box_type_bytes = [0u8; 4];
|
||||
@@ -74,7 +74,7 @@ pub fn is_mp4_complete(file_path: &Path) -> anyhow::Result<bool> {
|
||||
if e.kind() == std::io::ErrorKind::UnexpectedEof {
|
||||
return Ok(false);
|
||||
}
|
||||
return Err(anyhow!(e));
|
||||
return Err(eyre!(e));
|
||||
}
|
||||
// 如果是第一个Box,检查是否是 'ftyp' Box
|
||||
if is_first_box {
|
||||
@@ -131,11 +131,11 @@ pub fn is_mp4_complete(file_path: &Path) -> anyhow::Result<bool> {
|
||||
}
|
||||
|
||||
pub trait ToXml {
|
||||
fn to_xml(&self, cid: i64) -> anyhow::Result<String>;
|
||||
fn to_xml(&self, cid: i64) -> eyre::Result<String>;
|
||||
}
|
||||
|
||||
impl ToXml for Vec<DmSegMobileReply> {
|
||||
fn to_xml(&self, cid: i64) -> anyhow::Result<String> {
|
||||
fn to_xml(&self, cid: i64) -> eyre::Result<String> {
|
||||
let elems = self
|
||||
.iter()
|
||||
.flat_map(|reply| &reply.elems)
|
||||
@@ -157,7 +157,7 @@ impl ToXml for Vec<DmSegMobileReply> {
|
||||
|
||||
let i_tag = DanmakuXmlITag { chatid: cid, elems };
|
||||
|
||||
let xml = yaserde::ser::to_string(&i_tag).map_err(|e| anyhow!(e))?;
|
||||
let xml = yaserde::ser::to_string(&i_tag).map_err(|e| eyre!(e))?;
|
||||
|
||||
Ok(xml)
|
||||
}
|
||||
@@ -177,11 +177,11 @@ pub fn seconds_to_srt_time(seconds: f64) -> String {
|
||||
format!("{h:02}:{m:02}:{s:02},{ms:03}")
|
||||
}
|
||||
|
||||
pub fn get_ffmpeg_program() -> anyhow::Result<PathBuf> {
|
||||
pub fn get_ffmpeg_program() -> eyre::Result<PathBuf> {
|
||||
let ffmpeg_program = std::env::current_exe()
|
||||
.context("获取当前可执行文件路径失败")?
|
||||
.wrap_err("获取当前可执行文件路径失败")?
|
||||
.parent()
|
||||
.context("获取当前可执行文件所在目录失败")?
|
||||
.ok_or_eyre("获取当前可执行文件所在目录失败")?
|
||||
.join("com.lanyeeee.bilibili-video-downloader-ffmpeg");
|
||||
|
||||
Ok(ffmpeg_program)
|
||||
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{Context, anyhow};
|
||||
use eyre::{OptionExt, WrapErr, eyre};
|
||||
use md5::{Digest, Md5};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -25,8 +25,8 @@ struct WeiRespData {
|
||||
|
||||
impl BiliClient {
|
||||
// 为请求参数进行 wbi 签名
|
||||
pub(crate) async fn wbi(&self, params: &mut Vec<(&str, String)>) -> anyhow::Result<()> {
|
||||
let (img_key, sub_key) = self.get_wbi_keys().await.context("获取wbi keys失败")?;
|
||||
pub(crate) async fn wbi(&self, params: &mut Vec<(&str, String)>) -> eyre::Result<()> {
|
||||
let (img_key, sub_key) = self.get_wbi_keys().await.wrap_err("获取wbi keys失败")?;
|
||||
let mixin_key = get_mixin_key((img_key + &sub_key).as_bytes());
|
||||
|
||||
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||
@@ -46,7 +46,7 @@ impl BiliClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_wbi_keys(&self) -> anyhow::Result<(String, String)> {
|
||||
async fn get_wbi_keys(&self) -> eyre::Result<(String, String)> {
|
||||
let request = self
|
||||
.api_client
|
||||
.read()
|
||||
@@ -58,27 +58,27 @@ impl BiliClient {
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != reqwest::StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
return Err(eyre!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
serde_json::from_str(&body).wrap_err(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
return Err(eyre!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为Data
|
||||
let data_str = data.to_string();
|
||||
let wei_resp_data: WeiRespData =
|
||||
serde_json::from_str(&data_str).context(format!("将data解析为Data失败: {data_str}"))?;
|
||||
let wei_resp_data: WeiRespData = serde_json::from_str(&data_str)
|
||||
.wrap_err(format!("将data解析为Data失败: {data_str}"))?;
|
||||
|
||||
let img_url = wei_resp_data.wbi_img.img_url;
|
||||
let sub_url = wei_resp_data.wbi_img.sub_url;
|
||||
|
||||
let img_filename =
|
||||
take_filename(&img_url).context(format!("从img_url中提取文件名失败: {img_url}"))?;
|
||||
take_filename(&img_url).ok_or_eyre(format!("从img_url中提取文件名失败: {img_url}"))?;
|
||||
let sub_filename =
|
||||
take_filename(&sub_url).context(format!("从sub_url中提取文件名失败: {sub_url}"))?;
|
||||
take_filename(&sub_url).ok_or_eyre(format!("从sub_url中提取文件名失败: {sub_url}"))?;
|
||||
|
||||
Ok((img_filename, sub_filename))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user