mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-05 23:57:23 +08:00
feat: 支持设置 画质、音质、编码 的优先级
This commit is contained in:
+33
-104
@@ -1,11 +1,13 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use num_enum::{FromPrimitive, IntoPrimitive};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
use tauri::{AppHandle, Manager};
|
||||
|
||||
use crate::danmaku_xml_to_ass::canvas::CanvasConfig;
|
||||
use crate::{
|
||||
danmaku_xml_to_ass::canvas::CanvasConfig,
|
||||
types::{audio_quality::AudioQuality, codec_type::CodecType, video_quality::VideoQuality},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Type)]
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
@@ -14,9 +16,9 @@ pub struct Config {
|
||||
pub download_dir: PathBuf,
|
||||
pub enable_file_logger: bool,
|
||||
pub sessdata: String,
|
||||
pub prefer_video_quality: PreferVideoQuality,
|
||||
pub prefer_codec_type: PreferCodecType,
|
||||
pub prefer_audio_quality: PreferAudioQuality,
|
||||
pub video_quality_priority: Vec<VideoQuality>,
|
||||
pub codec_type_priority: Vec<CodecType>,
|
||||
pub audio_quality_priority: Vec<AudioQuality>,
|
||||
pub download_video: bool,
|
||||
pub download_audio: bool,
|
||||
pub auto_merge: bool,
|
||||
@@ -96,13 +98,36 @@ impl Config {
|
||||
fn default(app_data_dir: &Path) -> Config {
|
||||
const DEFAULT_FMT_FOR_PART: &str =
|
||||
"{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 {
|
||||
download_dir: app_data_dir.join("视频下载"),
|
||||
enable_file_logger: true,
|
||||
sessdata: String::new(),
|
||||
prefer_video_quality: PreferVideoQuality::Best,
|
||||
prefer_codec_type: PreferCodecType::AVC,
|
||||
prefer_audio_quality: PreferAudioQuality::Best,
|
||||
video_quality_priority: default_video_quality_priority,
|
||||
codec_type_priority: vec![CodecType::AVC, CodecType::HEVC, CodecType::AV1],
|
||||
audio_quality_priority: default_audio_quality_priority,
|
||||
download_video: true,
|
||||
download_audio: true,
|
||||
auto_merge: true,
|
||||
@@ -130,102 +155,6 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Type,
|
||||
IntoPrimitive,
|
||||
FromPrimitive,
|
||||
)]
|
||||
#[repr(i64)]
|
||||
pub enum PreferVideoQuality {
|
||||
#[default]
|
||||
Best = -1,
|
||||
|
||||
#[serde(rename = "240P")]
|
||||
Video240P = 6,
|
||||
#[serde(rename = "360P")]
|
||||
Video360P = 16,
|
||||
#[serde(rename = "480P")]
|
||||
Video480P = 32,
|
||||
#[serde(rename = "720P")]
|
||||
Video720P = 64,
|
||||
#[serde(rename = "720P60")]
|
||||
Video720P60 = 74,
|
||||
#[serde(rename = "1080P")]
|
||||
Video1080P = 80,
|
||||
#[serde(rename = "AiRepair")]
|
||||
VideoAiRepair = 100,
|
||||
#[serde(rename = "1080P+")]
|
||||
Video1080PPlus = 112,
|
||||
#[serde(rename = "1080P60")]
|
||||
Video1080P60 = 116,
|
||||
#[serde(rename = "4K")]
|
||||
Video4K = 120,
|
||||
#[serde(rename = "HDR")]
|
||||
VideoHDR = 125,
|
||||
#[serde(rename = "Dolby")]
|
||||
VideoDolby = 126,
|
||||
#[serde(rename = "8K")]
|
||||
Video8K = 127,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Type,
|
||||
IntoPrimitive,
|
||||
FromPrimitive,
|
||||
)]
|
||||
#[repr(i64)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum PreferCodecType {
|
||||
#[default]
|
||||
Unknown = -1,
|
||||
|
||||
AVC = 7,
|
||||
HEVC = 12,
|
||||
AV1 = 13,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Default,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Type,
|
||||
IntoPrimitive,
|
||||
FromPrimitive,
|
||||
)]
|
||||
#[repr(i64)]
|
||||
pub enum PreferAudioQuality {
|
||||
#[default]
|
||||
Best = -1,
|
||||
#[serde(rename = "64K")]
|
||||
Audio64K = 30216,
|
||||
#[serde(rename = "132K")]
|
||||
Audio132K = 30232,
|
||||
#[serde(rename = "192K")]
|
||||
Audio192K = 30280,
|
||||
#[serde(rename = "Dolby")]
|
||||
AudioDolby = 30250,
|
||||
#[serde(rename = "HiRes")]
|
||||
AudioHiRes = 30251,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub enum ProxyMode {
|
||||
#[default]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::HashMap,
|
||||
fs::{File, OpenOptions},
|
||||
sync::Arc,
|
||||
};
|
||||
@@ -114,11 +114,7 @@ impl AudioTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取音频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -174,11 +170,7 @@ impl AudioTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取音频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -234,37 +226,28 @@ impl AudioTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取音频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare(&mut self, app: &AppHandle, mut medias: Vec<MediaForPrepare>) {
|
||||
medias.sort_by_key(|m| Reverse(m.id.to_audio_quality_for_prepare()));
|
||||
let best_quality_id = medias[0].id;
|
||||
fn prepare(&mut self, app: &AppHandle, mut medias: Vec<MediaForPrepare>) -> anyhow::Result<()> {
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取音频地址失败"));
|
||||
}
|
||||
|
||||
let prefer_quality = app.get_config().read().prefer_audio_quality;
|
||||
let prefer_quality_id: i64 = prefer_quality.into();
|
||||
let prefer_quality_found = medias.iter().any(|m| m.id == prefer_quality_id);
|
||||
let quality_filtered_medias: Vec<MediaForPrepare> = if prefer_quality_found {
|
||||
// 如果用户指定质量存在,则使用用户指定的质量
|
||||
medias
|
||||
.into_iter()
|
||||
.filter(|m| m.id == prefer_quality_id)
|
||||
.collect()
|
||||
} else {
|
||||
// 否则使用最高质量
|
||||
medias
|
||||
.into_iter()
|
||||
.filter(|m| m.id == best_quality_id)
|
||||
.collect()
|
||||
};
|
||||
let quality_priority = app.get_config().read().audio_quality_priority.clone();
|
||||
let priority_map: HashMap<&AudioQuality, usize> = quality_priority
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, quality)| (quality, index))
|
||||
.collect();
|
||||
medias.sort_by_key(|media| {
|
||||
let quality: AudioQuality = media.id.into();
|
||||
priority_map.get(&quality).unwrap_or(&usize::MAX)
|
||||
});
|
||||
|
||||
let media = &quality_filtered_medias[0];
|
||||
let media = &medias[0];
|
||||
|
||||
self.audio_quality = media.id.into();
|
||||
|
||||
@@ -295,6 +278,8 @@ impl AudioTask {
|
||||
self.content_length = content_length;
|
||||
self.chunks = chunks;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_uncompleted(&mut self) {
|
||||
@@ -427,30 +412,3 @@ struct MediaForPrepare {
|
||||
pub id: i64,
|
||||
pub url_with_content_length: Vec<(String, u64)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum AudioQualityForPrepare {
|
||||
Audio64K,
|
||||
Audio132K,
|
||||
Audio192K,
|
||||
AudioDolby,
|
||||
AudioHiRes,
|
||||
}
|
||||
|
||||
trait ToAudioQualityForPrepare {
|
||||
fn to_audio_quality_for_prepare(self) -> Option<AudioQualityForPrepare>;
|
||||
}
|
||||
|
||||
impl ToAudioQualityForPrepare for i64 {
|
||||
fn to_audio_quality_for_prepare(self) -> Option<AudioQualityForPrepare> {
|
||||
let audio_quality: AudioQuality = self.into();
|
||||
match audio_quality {
|
||||
AudioQuality::Audio64K => Some(AudioQualityForPrepare::Audio64K),
|
||||
AudioQuality::Audio132K => Some(AudioQualityForPrepare::Audio132K),
|
||||
AudioQuality::Audio192K => Some(AudioQualityForPrepare::Audio192K),
|
||||
AudioQuality::AudioDolby => Some(AudioQualityForPrepare::AudioDolby),
|
||||
AudioQuality::AudioHiRes => Some(AudioQualityForPrepare::AudioHiRes),
|
||||
AudioQuality::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
cmp::Reverse,
|
||||
collections::HashMap,
|
||||
fs::{File, OpenOptions},
|
||||
sync::Arc,
|
||||
};
|
||||
@@ -74,11 +74,7 @@ impl VideoTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取视频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -144,11 +140,7 @@ impl VideoTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取视频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -214,47 +206,49 @@ impl VideoTask {
|
||||
}
|
||||
}
|
||||
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取视频地址失败"));
|
||||
}
|
||||
|
||||
self.prepare(app, medias);
|
||||
self.prepare(app, medias)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare(&mut self, app: &AppHandle, mut medias: Vec<MediaForPrepare>) {
|
||||
medias.sort_by_key(|m| Reverse(m.id));
|
||||
let best_quality_id = medias[0].id;
|
||||
fn prepare(&mut self, app: &AppHandle, mut medias: Vec<MediaForPrepare>) -> anyhow::Result<()> {
|
||||
if medias.is_empty() {
|
||||
return Err(anyhow!("获取音频地址失败"));
|
||||
}
|
||||
|
||||
let (prefer_quality, prefer_codec_type) = {
|
||||
let (video_quality_priority, codec_type_priority) = {
|
||||
let config = app.get_config().inner().read();
|
||||
(config.prefer_video_quality, config.prefer_codec_type)
|
||||
(
|
||||
config.video_quality_priority.clone(),
|
||||
config.codec_type_priority.clone(),
|
||||
)
|
||||
};
|
||||
|
||||
let prefer_quality_id: i64 = prefer_quality.into();
|
||||
let prefer_codec_id: i64 = prefer_codec_type.into();
|
||||
let prefer_quality_found = medias.iter().any(|m| m.id == prefer_quality_id);
|
||||
let mut quality_filtered_medias: Vec<MediaForPrepare> = if prefer_quality_found {
|
||||
// 如果用户指定质量存在,则使用用户指定的质量
|
||||
medias
|
||||
.into_iter()
|
||||
.filter(|m| m.id == prefer_quality_id)
|
||||
.collect()
|
||||
} else {
|
||||
// 否则使用最高质量
|
||||
medias
|
||||
.into_iter()
|
||||
.filter(|m| m.id == best_quality_id)
|
||||
.collect()
|
||||
};
|
||||
// 按照 AVC > HEVC > AV1 的顺序排列
|
||||
quality_filtered_medias.sort_by_key(|m| m.codecid);
|
||||
|
||||
let media = quality_filtered_medias
|
||||
let video_priority_map: HashMap<&VideoQuality, usize> = video_quality_priority
|
||||
.iter()
|
||||
.find(|m| m.codecid == prefer_codec_id)
|
||||
.unwrap_or(&quality_filtered_medias[0]);
|
||||
.enumerate()
|
||||
.map(|(index, quality)| (quality, index))
|
||||
.collect();
|
||||
medias.sort_by_key(|media| {
|
||||
let quality: VideoQuality = media.id.into();
|
||||
video_priority_map.get(&quality).unwrap_or(&usize::MAX)
|
||||
});
|
||||
|
||||
let retain_id = medias[0].id;
|
||||
medias.retain(|m| m.id == retain_id);
|
||||
|
||||
let codec_priority_map: HashMap<&CodecType, usize> = codec_type_priority
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, codec_type)| (codec_type, index))
|
||||
.collect();
|
||||
|
||||
medias.sort_by_key(|m| {
|
||||
let codec_type: CodecType = m.codecid.into();
|
||||
codec_priority_map.get(&codec_type).unwrap_or(&usize::MAX)
|
||||
});
|
||||
|
||||
let media = &medias[0];
|
||||
|
||||
self.video_quality = media.id.into();
|
||||
self.codec_type = media.codecid.into();
|
||||
@@ -286,6 +280,8 @@ impl VideoTask {
|
||||
self.content_length = content_length;
|
||||
self.chunks = chunks;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_uncompleted(&mut self) {
|
||||
|
||||
@@ -7,6 +7,8 @@ use specta::Type;
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
|
||||
@@ -7,6 +7,8 @@ use specta::Type;
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
@@ -19,6 +21,7 @@ use specta::Type;
|
||||
pub enum CodecType {
|
||||
#[default]
|
||||
Unknown = -1,
|
||||
|
||||
Audio = 0,
|
||||
AVC = 7,
|
||||
HEVC = 12,
|
||||
|
||||
@@ -7,6 +7,8 @@ use specta::Type;
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Hash,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
|
||||
Reference in New Issue
Block a user