mirror of
https://github.com/amtoaer/bili-sync.git
synced 2026-09-04 23:18:02 +08:00
feat: 扩大风控检测,当 http 返回 403 或 412 时认为是风控 (#640)
This commit is contained in:
@@ -31,7 +31,7 @@ impl Client {
|
|||||||
);
|
);
|
||||||
headers.insert(
|
headers.insert(
|
||||||
header::REFERER,
|
header::REFERER,
|
||||||
header::HeaderValue::from_static("https://www.bilibili.com"),
|
header::HeaderValue::from_static("https://www.bilibili.com/"),
|
||||||
);
|
);
|
||||||
Self(
|
Self(
|
||||||
reqwest::Client::builder()
|
reqwest::Client::builder()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use reqwest::Method;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::bilibili::{BiliClient, Credential, Validate, VideoInfo};
|
use crate::bilibili::{BiliClient, Credential, ErrorForStatusExt, Validate, VideoInfo};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug, Default, Copy)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug, Default, Copy)]
|
||||||
pub enum CollectionType {
|
pub enum CollectionType {
|
||||||
@@ -136,7 +136,7 @@ impl<'a> Collection<'a> {
|
|||||||
.query(&[("series_id", self.collection.sid.as_str())])
|
.query(&[("series_id", self.collection.sid.as_str())])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<Value>()
|
.json::<Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()
|
.validate()
|
||||||
@@ -176,7 +176,12 @@ impl<'a> Collection<'a> {
|
|||||||
("page_size", "30"),
|
("page_size", "30"),
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
req.send().await?.error_for_status()?.json::<Value>().await?.validate()
|
req.send()
|
||||||
|
.await?
|
||||||
|
.error_for_status_ext()?
|
||||||
|
.json::<Value>()
|
||||||
|
.await?
|
||||||
|
.validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_video_stream(self) -> impl Stream<Item = Result<VideoInfo>> + 'a {
|
pub fn into_video_stream(self) -> impl Stream<Item = Result<VideoInfo>> + 'a {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use rsa::sha2::Sha256;
|
|||||||
use rsa::{Oaep, RsaPublicKey};
|
use rsa::{Oaep, RsaPublicKey};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::bilibili::{BiliError, Client, Validate};
|
use crate::bilibili::{BiliError, Client, ErrorForStatusExt, Validate};
|
||||||
|
|
||||||
const MIXIN_KEY_ENC_TAB: [usize; 64] = [
|
const MIXIN_KEY_ENC_TAB: [usize; 64] = [
|
||||||
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38,
|
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38,
|
||||||
@@ -78,7 +78,7 @@ impl Credential {
|
|||||||
.request(Method::GET, "https://api.bilibili.com/x/web-interface/nav", Some(self))
|
.request(Method::GET, "https://api.bilibili.com/x/web-interface/nav", Some(self))
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -94,7 +94,7 @@ impl Credential {
|
|||||||
)
|
)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -111,7 +111,7 @@ impl Credential {
|
|||||||
.query(&[("qrcode_key", qrcode_key)])
|
.query(&[("qrcode_key", qrcode_key)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
let headers = std::mem::take(resp.headers_mut());
|
let headers = std::mem::take(resp.headers_mut());
|
||||||
let json = resp.json::<serde_json::Value>().await?.validate()?;
|
let json = resp.json::<serde_json::Value>().await?.validate()?;
|
||||||
let code = json["data"]["code"].as_i64().context("missing 'code' field in data")?;
|
let code = json["data"]["code"].as_i64().context("missing 'code' field in data")?;
|
||||||
@@ -147,7 +147,7 @@ impl Credential {
|
|||||||
.request(Method::GET, "https://api.bilibili.com/x/web-frontend/getbuvid", None)
|
.request(Method::GET, "https://api.bilibili.com/x/web-frontend/getbuvid", None)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -167,7 +167,7 @@ impl Credential {
|
|||||||
)
|
)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -220,7 +220,7 @@ JNrRuoEUXpabUzGB8QIDAQAB
|
|||||||
.header(header::COOKIE, "Domain=.bilibili.com")
|
.header(header::COOKIE, "Domain=.bilibili.com")
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
regex_find(r#"<div id="1-name">(.+?)</div>"#, res.text().await?.as_str())
|
regex_find(r#"<div id="1-name">(.+?)</div>"#, res.text().await?.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@ JNrRuoEUXpabUzGB8QIDAQAB
|
|||||||
])
|
])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
let headers = std::mem::take(resp.headers_mut());
|
let headers = std::mem::take(resp.headers_mut());
|
||||||
let json = resp.json::<serde_json::Value>().await?.validate()?;
|
let json = resp.json::<serde_json::Value>().await?.validate()?;
|
||||||
let mut credential = Self::extract(headers, json)?;
|
let mut credential = Self::extract(headers, json)?;
|
||||||
@@ -263,7 +263,7 @@ JNrRuoEUXpabUzGB8QIDAQAB
|
|||||||
])
|
])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use futures::Stream;
|
|||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::bilibili::{BiliClient, Credential, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
use crate::bilibili::{BiliClient, Credential, ErrorForStatusExt, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
||||||
|
|
||||||
pub struct Dynamic<'a> {
|
pub struct Dynamic<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
@@ -38,7 +38,7 @@ impl<'a> Dynamic<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()
|
.validate()
|
||||||
|
|||||||
@@ -8,12 +8,17 @@ pub enum BiliError {
|
|||||||
ErrorResponse(i64, String),
|
ErrorResponse(i64, String),
|
||||||
#[error("risk control triggered by server, full response: {0}")]
|
#[error("risk control triggered by server, full response: {0}")]
|
||||||
RiskControlOccurred(String),
|
RiskControlOccurred(String),
|
||||||
|
#[error("invalid HTTP response code {0}, reason: {1}")]
|
||||||
|
InvalidStatusCode(u16, &'static str),
|
||||||
#[error("no video streams available (may indicate risk control)")]
|
#[error("no video streams available (may indicate risk control)")]
|
||||||
VideoStreamsEmpty,
|
VideoStreamsEmpty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BiliError {
|
impl BiliError {
|
||||||
pub fn is_risk_control_related(&self) -> bool {
|
pub fn is_risk_control_related(&self) -> bool {
|
||||||
matches!(self, BiliError::RiskControlOccurred(_) | BiliError::VideoStreamsEmpty)
|
matches!(
|
||||||
|
self,
|
||||||
|
BiliError::RiskControlOccurred(_) | BiliError::VideoStreamsEmpty | BiliError::InvalidStatusCode(_, _)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use async_stream::try_stream;
|
|||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::bilibili::{BiliClient, Credential, Validate, VideoInfo};
|
use crate::bilibili::{BiliClient, Credential, ErrorForStatusExt, Validate, VideoInfo};
|
||||||
pub struct FavoriteList<'a> {
|
pub struct FavoriteList<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
fid: String,
|
fid: String,
|
||||||
@@ -43,7 +43,7 @@ impl<'a> FavoriteList<'a> {
|
|||||||
.query(&[("media_id", &self.fid)])
|
.query(&[("media_id", &self.fid)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -68,7 +68,7 @@ impl<'a> FavoriteList<'a> {
|
|||||||
])
|
])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()
|
.validate()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use anyhow::{Result, ensure};
|
use anyhow::{Result, ensure};
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
|
|
||||||
use crate::bilibili::{BiliClient, Credential, Validate};
|
use crate::bilibili::{BiliClient, Credential, ErrorForStatusExt, Validate};
|
||||||
|
|
||||||
pub struct Me<'a> {
|
pub struct Me<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
@@ -29,7 +29,7 @@ impl<'a> Me<'a> {
|
|||||||
.query(&[("up_mid", &self.mid())])
|
.query(&[("up_mid", &self.mid())])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -53,7 +53,7 @@ impl<'a> Me<'a> {
|
|||||||
.query(&[("pn", page_num), ("ps", page_size)])
|
.query(&[("pn", page_num), ("ps", page_size)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -87,7 +87,7 @@ impl<'a> Me<'a> {
|
|||||||
let mut resp = request
|
let mut resp = request
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub use favorite_list::FavoriteList;
|
|||||||
use favorite_list::Upper;
|
use favorite_list::Upper;
|
||||||
pub use me::Me;
|
pub use me::Me;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use reqwest::RequestBuilder;
|
use reqwest::{RequestBuilder, StatusCode};
|
||||||
pub use submission::Submission;
|
pub use submission::Submission;
|
||||||
pub use video::{Dimension, PageInfo, Video};
|
pub use video::{Dimension, PageInfo, Video};
|
||||||
pub use watch_later::WatchLater;
|
pub use watch_later::WatchLater;
|
||||||
@@ -47,6 +47,12 @@ pub(crate) trait Validate {
|
|||||||
fn validate(self) -> Result<Self::Output>;
|
fn validate(self) -> Result<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait ErrorForStatusExt {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn error_for_status_ext(self) -> Result<Self::Output>;
|
||||||
|
}
|
||||||
|
|
||||||
impl Validate for serde_json::Value {
|
impl Validate for serde_json::Value {
|
||||||
type Output = serde_json::Value;
|
type Output = serde_json::Value;
|
||||||
|
|
||||||
@@ -62,6 +68,23 @@ impl Validate for serde_json::Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ErrorForStatusExt for reqwest::Response {
|
||||||
|
type Output = reqwest::Response;
|
||||||
|
|
||||||
|
fn error_for_status_ext(self) -> Result<Self::Output> {
|
||||||
|
let status = self.status();
|
||||||
|
// 412 是由于请求频率过高导致的,确定是风控问题
|
||||||
|
// 403 目前偶尔出现在下载视频音频流时,由于是偶尔出现且过一段时间消失,暂时也当成风控问题处理
|
||||||
|
if status == StatusCode::PRECONDITION_FAILED || status == StatusCode::FORBIDDEN {
|
||||||
|
bail!(BiliError::InvalidStatusCode(
|
||||||
|
status.as_u16(),
|
||||||
|
status.canonical_reason().unwrap_or("Unknown")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(self.error_for_status()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) trait WbiSign {
|
pub(crate) trait WbiSign {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use reqwest::Method;
|
|||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::bilibili::favorite_list::Upper;
|
use crate::bilibili::favorite_list::Upper;
|
||||||
use crate::bilibili::{BiliClient, Credential, Dynamic, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
use crate::bilibili::{BiliClient, Credential, Dynamic, ErrorForStatusExt, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
||||||
pub struct Submission<'a> {
|
pub struct Submission<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
pub upper_id: String,
|
pub upper_id: String,
|
||||||
@@ -39,7 +39,7 @@ impl<'a> Submission<'a> {
|
|||||||
.query(&[("mid", self.upper_id.as_str())])
|
.query(&[("mid", self.upper_id.as_str())])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -66,7 +66,7 @@ impl<'a> Submission<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()
|
.validate()
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::bilibili::analyzer::PageAnalyzer;
|
|||||||
use crate::bilibili::client::BiliClient;
|
use crate::bilibili::client::BiliClient;
|
||||||
use crate::bilibili::danmaku::{DanmakuElem, DanmakuWriter, DmSegMobileReply};
|
use crate::bilibili::danmaku::{DanmakuElem, DanmakuWriter, DmSegMobileReply};
|
||||||
use crate::bilibili::subtitle::{SubTitle, SubTitleBody, SubTitleInfo, SubTitlesInfo};
|
use crate::bilibili::subtitle::{SubTitle, SubTitleBody, SubTitleInfo, SubTitlesInfo};
|
||||||
use crate::bilibili::{Credential, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
use crate::bilibili::{Credential, ErrorForStatusExt, MIXIN_KEY, Validate, VideoInfo, WbiSign};
|
||||||
|
|
||||||
pub struct Video<'a> {
|
pub struct Video<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
@@ -57,7 +57,7 @@ impl<'a> Video<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -77,7 +77,7 @@ impl<'a> Video<'a> {
|
|||||||
.query(&[("bvid", &self.bvid)])
|
.query(&[("bvid", &self.bvid)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -96,7 +96,7 @@ impl<'a> Video<'a> {
|
|||||||
.query(&[("bvid", &self.bvid)])
|
.query(&[("bvid", &self.bvid)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -132,7 +132,7 @@ impl<'a> Video<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
let headers = std::mem::take(res.headers_mut());
|
let headers = std::mem::take(res.headers_mut());
|
||||||
let content_type = headers.get("content-type");
|
let content_type = headers.get("content-type");
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -164,7 +164,7 @@ impl<'a> Video<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -181,7 +181,7 @@ impl<'a> Video<'a> {
|
|||||||
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
.wbi_sign(MIXIN_KEY.load().as_deref())?
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()?;
|
.validate()?;
|
||||||
@@ -207,7 +207,7 @@ impl<'a> Video<'a> {
|
|||||||
.request(Method::GET, format!("https:{}", &info.subtitle_url).as_str(), None)
|
.request(Method::GET, format!("https:{}", &info.subtitle_url).as_str(), None)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?;
|
.await?;
|
||||||
let body: SubTitleBody = serde_json::from_value(res["body"].take())?;
|
let body: SubTitleBody = serde_json::from_value(res["body"].take())?;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use async_stream::try_stream;
|
|||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::bilibili::{BiliClient, Credential, Validate, VideoInfo};
|
use crate::bilibili::{BiliClient, Credential, ErrorForStatusExt, Validate, VideoInfo};
|
||||||
pub struct WatchLater<'a> {
|
pub struct WatchLater<'a> {
|
||||||
client: &'a BiliClient,
|
client: &'a BiliClient,
|
||||||
credential: &'a Credential,
|
credential: &'a Credential,
|
||||||
@@ -24,7 +24,7 @@ impl<'a> WatchLater<'a> {
|
|||||||
.await
|
.await
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status_ext()?
|
||||||
.json::<serde_json::Value>()
|
.json::<serde_json::Value>()
|
||||||
.await?
|
.await?
|
||||||
.validate()
|
.validate()
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use tokio::process::Command;
|
|||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
use tokio_util::io::StreamReader;
|
use tokio_util::io::StreamReader;
|
||||||
|
|
||||||
use crate::bilibili::Client;
|
use crate::bilibili::{Client, ErrorForStatusExt};
|
||||||
use crate::config::{ARGS, ConcurrentDownloadLimit};
|
use crate::config::{ARGS, ConcurrentDownloadLimit};
|
||||||
|
|
||||||
pub struct Downloader {
|
pub struct Downloader {
|
||||||
@@ -152,7 +152,7 @@ impl Downloader {
|
|||||||
.request(Method::GET, url, None)
|
.request(Method::GET, url, None)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
let expected = resp.header_content_length();
|
let expected = resp.header_content_length();
|
||||||
let mut stream_reader = StreamReader::new(resp.bytes_stream().map_err(std::io::Error::other));
|
let mut stream_reader = StreamReader::new(resp.bytes_stream().map_err(std::io::Error::other));
|
||||||
let received = tokio::io::copy(&mut stream_reader, file).await?;
|
let received = tokio::io::copy(&mut stream_reader, file).await?;
|
||||||
@@ -184,7 +184,7 @@ impl Downloader {
|
|||||||
.header(header::RANGE, "bytes=0-0")
|
.header(header::RANGE, "bytes=0-0")
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
if resp.status() != StatusCode::PARTIAL_CONTENT {
|
if resp.status() != StatusCode::PARTIAL_CONTENT {
|
||||||
return self.fetch_serial(url, file).await;
|
return self.fetch_serial(url, file).await;
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ impl Downloader {
|
|||||||
.request(Method::HEAD, url, None)
|
.request(Method::HEAD, url, None)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
if resp
|
if resp
|
||||||
.headers()
|
.headers()
|
||||||
.get(header::ACCEPT_RANGES)
|
.get(header::ACCEPT_RANGES)
|
||||||
@@ -234,7 +234,7 @@ impl Downloader {
|
|||||||
.header(header::RANGE, &range_header)
|
.header(header::RANGE, &range_header)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?;
|
.error_for_status_ext()?;
|
||||||
if let Some(content_length) = resp.header_content_length() {
|
if let Some(content_length) = resp.header_content_length() {
|
||||||
ensure!(
|
ensure!(
|
||||||
content_length == end - start + 1,
|
content_length == end - start + 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user