mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-09 01:27:27 +08:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39082f62b6 | ||
|
|
843203c043 | ||
|
|
650ae0f6ca | ||
|
|
4aa8e7c032 | ||
|
|
e8f29aa371 |
@@ -192,6 +192,8 @@ pub mod v1 {
|
|||||||
Video1080P60 = 116,
|
Video1080P60 = 116,
|
||||||
#[serde(rename = "4K")]
|
#[serde(rename = "4K")]
|
||||||
Video4K = 120,
|
Video4K = 120,
|
||||||
|
#[serde(rename = "SDR")]
|
||||||
|
VideoSDR = 122,
|
||||||
#[serde(rename = "HDR")]
|
#[serde(rename = "HDR")]
|
||||||
VideoHDR = 125,
|
VideoHDR = 125,
|
||||||
#[serde(rename = "Dolby")]
|
#[serde(rename = "Dolby")]
|
||||||
|
|||||||
Generated
+7
@@ -323,6 +323,7 @@ dependencies = [
|
|||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"urlencoding",
|
||||||
"uuid",
|
"uuid",
|
||||||
"yaserde",
|
"yaserde",
|
||||||
]
|
]
|
||||||
@@ -5133,6 +5134,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urlpattern"
|
name = "urlpattern"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ md-5 = { version = "0.10.6" }
|
|||||||
rand = { version = "0.9.1" }
|
rand = { version = "0.9.1" }
|
||||||
base64 = { version = "0.22.1" }
|
base64 = { version = "0.22.1" }
|
||||||
rayon = { version = "1.12.0" }
|
rayon = { version = "1.12.0" }
|
||||||
|
urlencoding = { version = "2.1.3" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = true
|
strip = true
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ impl BiliClient {
|
|||||||
let params = json!({
|
let params = json!({
|
||||||
"cid": cid,
|
"cid": cid,
|
||||||
"qn": 127,
|
"qn": 127,
|
||||||
"fnval": 4048,
|
"fnval": 139216,
|
||||||
"drm_tech_type": 2,
|
"drm_tech_type": 2,
|
||||||
});
|
});
|
||||||
// 发送获取番剧url的请求
|
// 发送获取番剧url的请求
|
||||||
@@ -478,7 +478,7 @@ impl BiliClient {
|
|||||||
let params = json!({
|
let params = json!({
|
||||||
"cid": cid,
|
"cid": cid,
|
||||||
"qn": 127,
|
"qn": 127,
|
||||||
"fnval": 4048,
|
"fnval": 139216,
|
||||||
"drm_tech_type": 2,
|
"drm_tech_type": 2,
|
||||||
"from_client": "BROWSER",
|
"from_client": "BROWSER",
|
||||||
});
|
});
|
||||||
@@ -522,7 +522,7 @@ impl BiliClient {
|
|||||||
let params = json!({
|
let params = json!({
|
||||||
"ep_id": ep_id,
|
"ep_id": ep_id,
|
||||||
"qn": 127,
|
"qn": 127,
|
||||||
"fnval": 4048,
|
"fnval": 139216,
|
||||||
"drm_tech_type": 2,
|
"drm_tech_type": 2,
|
||||||
});
|
});
|
||||||
// 发送获取课程url的请求
|
// 发送获取课程url的请求
|
||||||
@@ -1098,7 +1098,12 @@ impl BiliClient {
|
|||||||
|
|
||||||
pub fn get_cookie(&self) -> String {
|
pub fn get_cookie(&self) -> String {
|
||||||
let sessdata = self.app.get_config().read().sessdata.clone();
|
let sessdata = self.app.get_config().read().sessdata.clone();
|
||||||
format!("SESSDATA={}", sessdata.trim_end_matches(';'))
|
|
||||||
|
let trimmed = sessdata.trim_end_matches(';');
|
||||||
|
let decoded = urlencoding::decode(trimmed).unwrap_or_else(|_| trimmed.into());
|
||||||
|
let encoded = urlencoding::encode(&decoded);
|
||||||
|
|
||||||
|
format!("SESSDATA={encoded}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-26
@@ -53,7 +53,7 @@ impl Config {
|
|||||||
let app_data_dir = app.path().app_data_dir()?;
|
let app_data_dir = app.path().app_data_dir()?;
|
||||||
let config_path = app_data_dir.join("config.json");
|
let config_path = app_data_dir.join("config.json");
|
||||||
|
|
||||||
let config = if config_path.exists() {
|
let mut config = if config_path.exists() {
|
||||||
let config_string = std::fs::read_to_string(config_path)?;
|
let config_string = std::fs::read_to_string(config_path)?;
|
||||||
match serde_json::from_str(&config_string) {
|
match serde_json::from_str(&config_string) {
|
||||||
// 如果能够直接解析为Config,则直接返回
|
// 如果能够直接解析为Config,则直接返回
|
||||||
@@ -65,6 +65,8 @@ impl Config {
|
|||||||
} else {
|
} else {
|
||||||
Config::default(&app_data_dir)
|
Config::default(&app_data_dir)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config.reconcile_priority_lists();
|
||||||
config.save(app)?;
|
config.save(app)?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
@@ -78,6 +80,18 @@ impl Config {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reconcile_priority_lists(&mut self) {
|
||||||
|
reconcile(
|
||||||
|
&mut self.video_quality_priority,
|
||||||
|
DEFAULT_VIDEO_QUALITY_PRIORITY,
|
||||||
|
);
|
||||||
|
reconcile(
|
||||||
|
&mut self.audio_quality_priority,
|
||||||
|
DEFAULT_AUDIO_QUALITY_PRIORITY,
|
||||||
|
);
|
||||||
|
reconcile(&mut self.codec_type_priority, DEFAULT_CODEC_TYPE_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
fn merge_config(config_string: &str, app_data_dir: &Path) -> Config {
|
fn merge_config(config_string: &str, app_data_dir: &Path) -> Config {
|
||||||
let Ok(mut json_value) = serde_json::from_str::<serde_json::Value>(config_string) else {
|
let Ok(mut json_value) = serde_json::from_str::<serde_json::Value>(config_string) else {
|
||||||
return Config::default(app_data_dir);
|
return Config::default(app_data_dir);
|
||||||
@@ -103,36 +117,14 @@ impl Config {
|
|||||||
fn default(app_data_dir: &Path) -> Config {
|
fn default(app_data_dir: &Path) -> Config {
|
||||||
const DEFAULT_FMT_FOR_PART: &str =
|
const DEFAULT_FMT_FOR_PART: &str =
|
||||||
"{collection_title}/{episode_title}/{episode_title}-P{part_order} {part_title}";
|
"{collection_title}/{episode_title}/{episode_title}-P{part_order} {part_title}";
|
||||||
let default_video_quality_priority = vec![
|
|
||||||
VideoQuality::Video8K,
|
|
||||||
VideoQuality::VideoDolby,
|
|
||||||
VideoQuality::VideoHDR,
|
|
||||||
VideoQuality::Video4K,
|
|
||||||
VideoQuality::Video1080P60,
|
|
||||||
VideoQuality::Video1080PPlus,
|
|
||||||
VideoQuality::Video1080P,
|
|
||||||
VideoQuality::VideoAiRepair,
|
|
||||||
VideoQuality::Video720P60,
|
|
||||||
VideoQuality::Video720P,
|
|
||||||
VideoQuality::Video480P,
|
|
||||||
VideoQuality::Video360P,
|
|
||||||
VideoQuality::Video240P,
|
|
||||||
];
|
|
||||||
let default_audio_quality_priority = vec![
|
|
||||||
AudioQuality::AudioHiRes,
|
|
||||||
AudioQuality::AudioDolby,
|
|
||||||
AudioQuality::Audio192K,
|
|
||||||
AudioQuality::Audio132K,
|
|
||||||
AudioQuality::Audio64K,
|
|
||||||
];
|
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
download_dir: app_data_dir.join("视频下载"),
|
download_dir: app_data_dir.join("视频下载"),
|
||||||
enable_file_logger: true,
|
enable_file_logger: true,
|
||||||
sessdata: String::new(),
|
sessdata: String::new(),
|
||||||
video_quality_priority: default_video_quality_priority,
|
video_quality_priority: DEFAULT_VIDEO_QUALITY_PRIORITY.to_vec(),
|
||||||
codec_type_priority: vec![CodecType::AVC, CodecType::HEVC, CodecType::AV1],
|
codec_type_priority: DEFAULT_CODEC_TYPE_PRIORITY.to_vec(),
|
||||||
audio_quality_priority: default_audio_quality_priority,
|
audio_quality_priority: DEFAULT_AUDIO_QUALITY_PRIORITY.to_vec(),
|
||||||
download_video: true,
|
download_video: true,
|
||||||
download_audio: true,
|
download_audio: true,
|
||||||
auto_merge: true,
|
auto_merge: true,
|
||||||
@@ -162,6 +154,14 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reconcile<T: Copy + Eq>(priority: &mut Vec<T>, all: &[T]) {
|
||||||
|
for &item in all {
|
||||||
|
if !priority.contains(&item) {
|
||||||
|
priority.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Type)]
|
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Type)]
|
||||||
pub enum ProxyMode {
|
pub enum ProxyMode {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -176,3 +176,31 @@ pub enum FileExistAction {
|
|||||||
Overwrite,
|
Overwrite,
|
||||||
Skip,
|
Skip,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_VIDEO_QUALITY_PRIORITY: &[VideoQuality] = &[
|
||||||
|
VideoQuality::Video8K,
|
||||||
|
VideoQuality::VideoDolby,
|
||||||
|
VideoQuality::VideoHDR,
|
||||||
|
VideoQuality::VideoSDR,
|
||||||
|
VideoQuality::Video4K,
|
||||||
|
VideoQuality::Video1080P60,
|
||||||
|
VideoQuality::Video1080PPlus,
|
||||||
|
VideoQuality::Video1080P,
|
||||||
|
VideoQuality::VideoAiRepair,
|
||||||
|
VideoQuality::Video720P60,
|
||||||
|
VideoQuality::Video720P,
|
||||||
|
VideoQuality::Video480P,
|
||||||
|
VideoQuality::Video360P,
|
||||||
|
VideoQuality::Video240P,
|
||||||
|
];
|
||||||
|
|
||||||
|
const DEFAULT_AUDIO_QUALITY_PRIORITY: &[AudioQuality] = &[
|
||||||
|
AudioQuality::AudioHiRes,
|
||||||
|
AudioQuality::AudioDolby,
|
||||||
|
AudioQuality::Audio192K,
|
||||||
|
AudioQuality::Audio132K,
|
||||||
|
AudioQuality::Audio64K,
|
||||||
|
];
|
||||||
|
|
||||||
|
const DEFAULT_CODEC_TYPE_PRIORITY: &[CodecType] =
|
||||||
|
&[CodecType::AVC, CodecType::HEVC, CodecType::AV1];
|
||||||
|
|||||||
@@ -217,6 +217,9 @@ impl DownloadProgress {
|
|||||||
|
|
||||||
self.prepare(app).await.wrap_err("准备下载失败")?;
|
self.prepare(app).await.wrap_err("准备下载失败")?;
|
||||||
|
|
||||||
|
self.completed_ts = None; // 重置完成时间戳
|
||||||
|
download_task.update_progress(|p| *p = self.clone());
|
||||||
|
|
||||||
let progress_before_hook = self.clone();
|
let progress_before_hook = self.clone();
|
||||||
app.get_plugin_manager()
|
app.get_plugin_manager()
|
||||||
.run_hook(HookContext::AfterPrepare(AfterPrepareContext::new(self)))
|
.run_hook(HookContext::AfterPrepare(AfterPrepareContext::new(self)))
|
||||||
@@ -225,9 +228,6 @@ impl DownloadProgress {
|
|||||||
download_task.update_progress(|p| *p = self.clone());
|
download_task.update_progress(|p| *p = self.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.completed_ts = None; // 重置完成时间戳
|
|
||||||
download_task.update_progress(|p| *p = self.clone());
|
|
||||||
|
|
||||||
std::fs::create_dir_all(&self.episode_dir)
|
std::fs::create_dir_all(&self.episode_dir)
|
||||||
.wrap_err(format!("创建目录`{}`失败", self.episode_dir.display()))?;
|
.wrap_err(format!("创建目录`{}`失败", self.episode_dir.display()))?;
|
||||||
|
|
||||||
@@ -250,6 +250,7 @@ impl DownloadProgress {
|
|||||||
tracing::debug!("音频下载任务完成");
|
tracing::debug!("音频下载任务完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*self = download_task.progress.read().clone();
|
||||||
let progress_before_hook = self.clone();
|
let progress_before_hook = self.clone();
|
||||||
app.get_plugin_manager()
|
app.get_plugin_manager()
|
||||||
.run_hook(HookContext::BeforeVideoProcess(
|
.run_hook(HookContext::BeforeVideoProcess(
|
||||||
@@ -324,6 +325,7 @@ impl DownloadProgress {
|
|||||||
download_task.update_progress(|p| p.completed_ts = Some(completed_ts));
|
download_task.update_progress(|p| p.completed_ts = Some(completed_ts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*self = download_task.progress.read().clone();
|
||||||
let progress_before_hook = self.clone();
|
let progress_before_hook = self.clone();
|
||||||
app.get_plugin_manager()
|
app.get_plugin_manager()
|
||||||
.run_hook(HookContext::OnCompleted(OnCompletedContext::new(self)))
|
.run_hook(HookContext::OnCompleted(OnCompletedContext::new(self)))
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ pub struct PaidJump {
|
|||||||
pub struct Payment {
|
pub struct Payment {
|
||||||
pub bp_enough: i64,
|
pub bp_enough: i64,
|
||||||
pub desc: String,
|
pub desc: String,
|
||||||
pub my_bp: i64,
|
pub my_bp: f64,
|
||||||
pub pay_shade: String,
|
pub pay_shade: String,
|
||||||
pub price: f64,
|
pub price: f64,
|
||||||
pub price_format: String,
|
pub price_format: String,
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ pub enum VideoQuality {
|
|||||||
Video1080P60 = 116,
|
Video1080P60 = 116,
|
||||||
#[serde(rename = "4K")]
|
#[serde(rename = "4K")]
|
||||||
Video4K = 120,
|
Video4K = 120,
|
||||||
|
#[serde(rename = "SDR")]
|
||||||
|
VideoSDR = 122,
|
||||||
#[serde(rename = "HDR")]
|
#[serde(rename = "HDR")]
|
||||||
VideoHDR = 125,
|
VideoHDR = 125,
|
||||||
#[serde(rename = "Dolby")]
|
#[serde(rename = "Dolby")]
|
||||||
|
|||||||
+1
-1
@@ -475,7 +475,7 @@ export type UserVideoInfo = { list: UserVideoList; page: PageInUserVideo }
|
|||||||
export type UserVideoList = { vlist: EpInUserVideo[] }
|
export type UserVideoList = { vlist: EpInUserVideo[] }
|
||||||
export type UserVideoSearchResult = UserVideoInfo
|
export type UserVideoSearchResult = UserVideoInfo
|
||||||
export type VideoProcessTask = { merge_selected: boolean; embed_chapter_selected: boolean; embed_skip_selected: boolean; completed: boolean; skipped: boolean }
|
export type VideoProcessTask = { merge_selected: boolean; embed_chapter_selected: boolean; embed_skip_selected: boolean; completed: boolean; skipped: boolean }
|
||||||
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "SDR" | "HDR" | "Dolby" | "8K"
|
||||||
export type VideoQualityAndCodecType = { video_quality: VideoQuality; codec_type: CodecType }
|
export type VideoQualityAndCodecType = { video_quality: VideoQuality; codec_type: CodecType }
|
||||||
export type VideoTask = { selected: boolean; url: string; video_quality: VideoQuality; codec_type: CodecType; content_length: number; chunks: MediaChunk[]; completed: boolean; skipped: boolean }
|
export type VideoTask = { selected: boolean; url: string; video_quality: VideoQuality; codec_type: CodecType; content_length: number; chunks: MediaChunk[]; completed: boolean; skipped: boolean }
|
||||||
export type VipInUserInfo = { type: number; status: number; due_date: number; vip_pay_type: number; theme_type: number; label: LabelInUserInfo; avatar_subscript: number; nickname_color: string; role: number; avatar_subscript_url: string; tv_vip_status: number; tv_vip_pay_type: number; tv_due_date: number }
|
export type VipInUserInfo = { type: number; status: number; due_date: number; vip_pay_type: number; theme_type: number; label: LabelInUserInfo; avatar_subscript: number; nickname_color: string; role: number; avatar_subscript_url: string; tv_vip_status: number; tv_vip_pay_type: number; tv_due_date: number }
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ const videoQualityNameMap: Record<VideoQuality, string> = {
|
|||||||
'1080P+': '1080P 高码率',
|
'1080P+': '1080P 高码率',
|
||||||
'1080P60': '1080P 60帧',
|
'1080P60': '1080P 60帧',
|
||||||
'4K': '4K 超高清',
|
'4K': '4K 超高清',
|
||||||
|
SDR: '4K SDR增强',
|
||||||
HDR: 'HDR 真彩色',
|
HDR: 'HDR 真彩色',
|
||||||
Dolby: '杜比视界',
|
Dolby: '杜比视界',
|
||||||
'8K': '8K 超高清',
|
'8K': '8K 超高清',
|
||||||
|
|||||||
Reference in New Issue
Block a user