mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-07 16:46:50 +08:00
feat: 后端支持配置代理
This commit is contained in:
@@ -16,7 +16,8 @@ use tauri::{
|
|||||||
use tokio::task::JoinSet;
|
use tokio::task::JoinSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::AppHandleExt,
|
config::ProxyMode,
|
||||||
|
extensions::{AnyhowErrorToStringChain, AppHandleExt},
|
||||||
protobuf::DmSegMobileReply,
|
protobuf::DmSegMobileReply,
|
||||||
types::{
|
types::{
|
||||||
bangumi_info::BangumiInfo, bangumi_media_url::BangumiMediaUrl, cheese_info::CheeseInfo,
|
bangumi_info::BangumiInfo, bangumi_media_url::BangumiMediaUrl, cheese_info::CheeseInfo,
|
||||||
@@ -59,6 +60,15 @@ impl BiliClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reload_client(&self) {
|
||||||
|
let api_client = create_api_client(&self.app);
|
||||||
|
*self.api_client.write() = api_client;
|
||||||
|
let media_client = create_media_client(&self.app);
|
||||||
|
*self.media_client.write() = media_client;
|
||||||
|
let content_length_client = create_content_length_client(&self.app);
|
||||||
|
*self.content_length_client.write() = content_length_client;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn generate_qrcode(&self) -> anyhow::Result<QrcodeData> {
|
pub async fn generate_qrcode(&self) -> anyhow::Result<QrcodeData> {
|
||||||
// 发送生成二维码请求
|
// 发送生成二维码请求
|
||||||
let request = self
|
let request = self
|
||||||
@@ -800,7 +810,7 @@ impl BiliClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware {
|
fn create_api_client(app: &AppHandle) -> ClientWithMiddleware {
|
||||||
let retry_policy = ExponentialBackoff::builder()
|
let retry_policy = ExponentialBackoff::builder()
|
||||||
.base(1)
|
.base(1)
|
||||||
.jitter(Jitter::Bounded)
|
.jitter(Jitter::Bounded)
|
||||||
@@ -811,6 +821,7 @@ fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware {
|
|||||||
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
||||||
|
|
||||||
let client = reqwest::ClientBuilder::new()
|
let client = reqwest::ClientBuilder::new()
|
||||||
|
.set_proxy(app, "api_client")
|
||||||
.timeout(Duration::from_secs(3))
|
.timeout(Duration::from_secs(3))
|
||||||
.default_headers(headers)
|
.default_headers(headers)
|
||||||
.build()
|
.build()
|
||||||
@@ -821,7 +832,7 @@ fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_media_client(_app: &AppHandle) -> ClientWithMiddleware {
|
fn create_media_client(app: &AppHandle) -> ClientWithMiddleware {
|
||||||
let retry_policy = ExponentialBackoff::builder()
|
let retry_policy = ExponentialBackoff::builder()
|
||||||
.base(1)
|
.base(1)
|
||||||
.jitter(Jitter::Bounded)
|
.jitter(Jitter::Bounded)
|
||||||
@@ -832,6 +843,7 @@ fn create_media_client(_app: &AppHandle) -> ClientWithMiddleware {
|
|||||||
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
||||||
|
|
||||||
let client = reqwest::ClientBuilder::new()
|
let client = reqwest::ClientBuilder::new()
|
||||||
|
.set_proxy(app, "media_client")
|
||||||
.default_headers(headers)
|
.default_headers(headers)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -841,18 +853,49 @@ fn create_media_client(_app: &AppHandle) -> ClientWithMiddleware {
|
|||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_content_length_client(_app: &AppHandle) -> Client {
|
fn create_content_length_client(app: &AppHandle) -> Client {
|
||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert("user-agent", HeaderValue::from_static(USER_AGENT));
|
headers.insert("user-agent", HeaderValue::from_static(USER_AGENT));
|
||||||
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
headers.insert("referer", HeaderValue::from_static(REFERRER));
|
||||||
|
|
||||||
reqwest::ClientBuilder::new()
|
reqwest::ClientBuilder::new()
|
||||||
|
.set_proxy(app, "content_length_client")
|
||||||
.timeout(Duration::from_secs(5))
|
.timeout(Duration::from_secs(5))
|
||||||
.default_headers(headers)
|
.default_headers(headers)
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait ClientBuilderExt {
|
||||||
|
fn set_proxy(self, app: &AppHandle, client_name: &str) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientBuilderExt for reqwest::ClientBuilder {
|
||||||
|
fn set_proxy(self, app: &AppHandle, client_name: &str) -> reqwest::ClientBuilder {
|
||||||
|
let proxy_mode = app.get_config().read().proxy_mode;
|
||||||
|
match proxy_mode {
|
||||||
|
ProxyMode::NoProxy => self.no_proxy(),
|
||||||
|
ProxyMode::System => self,
|
||||||
|
ProxyMode::Custom => {
|
||||||
|
let config = app.get_config().inner().read();
|
||||||
|
let proxy_host = &config.proxy_host;
|
||||||
|
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) {
|
||||||
|
Ok(proxy) => self.proxy(proxy),
|
||||||
|
Err(err) => {
|
||||||
|
let err_title = format!("{client_name}将`{proxy_url}`设为代理失败,将直连");
|
||||||
|
let string_chain = err.to_string_chain();
|
||||||
|
tracing::error!(err_title, message = string_chain);
|
||||||
|
self.no_proxy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct BiliResp {
|
pub struct BiliResp {
|
||||||
pub code: i64,
|
pub code: i64,
|
||||||
|
|||||||
@@ -52,13 +52,18 @@ pub fn get_config(config: tauri::State<RwLock<Config>>) -> Config {
|
|||||||
#[specta::specta]
|
#[specta::specta]
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub fn save_config(app: AppHandle, config: Config) -> CommandResult<()> {
|
pub fn save_config(app: AppHandle, config: Config) -> CommandResult<()> {
|
||||||
|
let bili_client = app.get_bili_client();
|
||||||
let config_state = app.get_config();
|
let config_state = app.get_config();
|
||||||
|
|
||||||
|
let proxy_changed = {
|
||||||
|
let config_state = config_state.read();
|
||||||
|
config_state.proxy_mode != config.proxy_mode
|
||||||
|
|| config_state.proxy_host != config.proxy_host
|
||||||
|
|| config_state.proxy_port != config.proxy_port
|
||||||
|
};
|
||||||
|
|
||||||
let enable_file_logger = config.enable_file_logger;
|
let enable_file_logger = config.enable_file_logger;
|
||||||
let enable_file_logger_changed = config_state
|
let file_logger_changed = config_state.read().enable_file_logger != enable_file_logger;
|
||||||
.read()
|
|
||||||
.enable_file_logger
|
|
||||||
.ne(&enable_file_logger);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// 包裹在大括号中,以便自动释放写锁
|
// 包裹在大括号中,以便自动释放写锁
|
||||||
@@ -70,7 +75,11 @@ pub fn save_config(app: AppHandle, config: Config) -> CommandResult<()> {
|
|||||||
tracing::debug!("保存配置成功");
|
tracing::debug!("保存配置成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
if enable_file_logger_changed {
|
if proxy_changed {
|
||||||
|
bili_client.reload_client();
|
||||||
|
}
|
||||||
|
|
||||||
|
if file_logger_changed {
|
||||||
if enable_file_logger {
|
if enable_file_logger {
|
||||||
logger::reload_file_logger()
|
logger::reload_file_logger()
|
||||||
.map_err(|err| CommandError::from("重新加载文件日志失败", err))?;
|
.map_err(|err| CommandError::from("重新加载文件日志失败", err))?;
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub struct Config {
|
|||||||
pub dir_fmt: String,
|
pub dir_fmt: String,
|
||||||
pub dir_fmt_for_part: String,
|
pub dir_fmt_for_part: String,
|
||||||
pub time_fmt: String,
|
pub time_fmt: String,
|
||||||
|
pub proxy_mode: ProxyMode,
|
||||||
|
pub proxy_host: String,
|
||||||
|
pub proxy_port: u16,
|
||||||
pub task_concurrency: usize,
|
pub task_concurrency: usize,
|
||||||
pub task_download_interval_sec: u64,
|
pub task_download_interval_sec: u64,
|
||||||
pub chunk_concurrency: usize,
|
pub chunk_concurrency: usize,
|
||||||
@@ -111,6 +114,9 @@ impl Config {
|
|||||||
dir_fmt: "{collection_title}/{episode_title}".to_string(),
|
dir_fmt: "{collection_title}/{episode_title}".to_string(),
|
||||||
dir_fmt_for_part: DEFAULT_FMT_FOR_PART.to_string(),
|
dir_fmt_for_part: DEFAULT_FMT_FOR_PART.to_string(),
|
||||||
time_fmt: "%Y-%m-%d_%H-%M-%S".to_string(),
|
time_fmt: "%Y-%m-%d_%H-%M-%S".to_string(),
|
||||||
|
proxy_mode: ProxyMode::NoProxy,
|
||||||
|
proxy_host: "127.0.0.1".to_string(),
|
||||||
|
proxy_port: 7890,
|
||||||
task_concurrency: 3,
|
task_concurrency: 3,
|
||||||
task_download_interval_sec: 0,
|
task_download_interval_sec: 0,
|
||||||
chunk_concurrency: 16,
|
chunk_concurrency: 16,
|
||||||
@@ -215,3 +221,11 @@ pub enum PreferAudioQuality {
|
|||||||
#[serde(rename = "HiRes")]
|
#[serde(rename = "HiRes")]
|
||||||
AudioHiRes = 30251,
|
AudioHiRes = 30251,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Type)]
|
||||||
|
pub enum ProxyMode {
|
||||||
|
#[default]
|
||||||
|
NoProxy,
|
||||||
|
System,
|
||||||
|
Custom,
|
||||||
|
}
|
||||||
|
|||||||
+2
-1
@@ -258,7 +258,7 @@ export type CntInfo = { collect: number; play: number; thumb_up: number; share:
|
|||||||
export type CntInfoInMedia = { collect: number; play: number; danmaku: number; vt: number; play_switch: number; reply: number; view_text_1: string }
|
export type CntInfoInMedia = { collect: number; play: number; danmaku: number; vt: number; play_switch: number; reply: number; view_text_1: string }
|
||||||
export type CodecType = "Unknown" | "Audio" | "AVC" | "HEVC" | "AV1"
|
export type CodecType = "Unknown" | "Audio" | "AVC" | "HEVC" | "AV1"
|
||||||
export type CommandError = { err_title: string; err_message: string }
|
export type CommandError = { err_title: string; err_message: string }
|
||||||
export type Config = { download_dir: string; enable_file_logger: boolean; sessdata: string; prefer_video_quality: PreferVideoQuality; prefer_codec_type: PreferCodecType; prefer_audio_quality: PreferAudioQuality; download_video: boolean; download_audio: boolean; auto_merge: boolean; download_xml_danmaku: boolean; download_ass_danmaku: boolean; download_json_danmaku: boolean; download_subtitle: boolean; download_cover: boolean; download_nfo: boolean; download_json: boolean; dir_fmt: string; dir_fmt_for_part: string; time_fmt: string; task_concurrency: number; task_download_interval_sec: number; chunk_concurrency: number; chunk_download_interval_sec: number; danmaku_config: CanvasConfig }
|
export type Config = { download_dir: string; enable_file_logger: boolean; sessdata: string; prefer_video_quality: PreferVideoQuality; prefer_codec_type: PreferCodecType; prefer_audio_quality: PreferAudioQuality; download_video: boolean; download_audio: boolean; auto_merge: boolean; download_xml_danmaku: boolean; download_ass_danmaku: boolean; download_json_danmaku: boolean; download_subtitle: boolean; download_cover: boolean; download_nfo: boolean; download_json: boolean; dir_fmt: string; dir_fmt_for_part: string; time_fmt: string; proxy_mode: ProxyMode; proxy_host: string; proxy_port: number; task_concurrency: number; task_download_interval_sec: number; chunk_concurrency: number; chunk_download_interval_sec: number; danmaku_config: CanvasConfig }
|
||||||
export type Consulting = { consulting_flag: boolean; consulting_url: string }
|
export type Consulting = { consulting_flag: boolean; consulting_url: string }
|
||||||
export type ContentList = { bold: boolean; content: string; number: string }
|
export type ContentList = { bold: boolean; content: string; number: string }
|
||||||
export type Cooperation = { link: string }
|
export type Cooperation = { link: string }
|
||||||
@@ -361,6 +361,7 @@ export type PreferAudioQuality = "Best" | "64K" | "132K" | "192K" | "Dolby" | "H
|
|||||||
export type PreferCodecType = "Unknown" | "AVC" | "HEVC" | "AV1"
|
export type PreferCodecType = "Unknown" | "AVC" | "HEVC" | "AV1"
|
||||||
export type PreferVideoQuality = "Best" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
export type PreferVideoQuality = "Best" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
||||||
export type PreviewedPurchaseNote = { long_watch_text: string; pay_text: string; price_format: string; watch_text: string; watching_text: string }
|
export type PreviewedPurchaseNote = { long_watch_text: string; pay_text: string; price_format: string; watch_text: string; watching_text: string }
|
||||||
|
export type ProxyMode = "NoProxy" | "System" | "Custom"
|
||||||
export type Publish = { is_finish: number; is_started: number; pub_time: string; pub_time_show: string; unknow_pub_date: number; weekday: number }
|
export type Publish = { is_finish: number; is_started: number; pub_time: string; pub_time_show: string; unknow_pub_date: number; weekday: number }
|
||||||
export type PurchaseFormatNote = { content_list: ContentList[]; link: string; title: string }
|
export type PurchaseFormatNote = { content_list: ContentList[]; link: string; title: string }
|
||||||
export type PurchaseNote = { content: string; link: string; title: string }
|
export type PurchaseNote = { content: string; link: string; title: string }
|
||||||
|
|||||||
Reference in New Issue
Block a user