mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-08-28 19:47:24 +08:00
feat: 后端支持获取视频的画质和音质
This commit is contained in:
@@ -9,11 +9,13 @@ use crate::{
|
||||
extensions::AppHandleExt,
|
||||
logger,
|
||||
types::{
|
||||
available_media_formats::AvailableMediaFormats,
|
||||
bangumi_follow_info::BangumiFollowInfo,
|
||||
bangumi_info::{BangumiInfo, EpInBangumi},
|
||||
create_download_task_params::CreateDownloadTaskParams,
|
||||
fav_folders::FavFolders,
|
||||
fav_info::FavInfo,
|
||||
get_available_media_formats_params::GetAvailableMediaFormatsParams,
|
||||
get_bangumi_follow_info_params::GetBangumiFollowInfoParams,
|
||||
get_bangumi_info_params::GetBangumiInfoParams,
|
||||
get_cheese_info_params::GetCheeseInfoParams,
|
||||
@@ -402,3 +404,40 @@ pub async fn get_skip_segments(
|
||||
.map_err(|err| CommandError::from("获取跳过片段失败", err))?;
|
||||
Ok(skip_segments)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[specta::specta]
|
||||
pub async fn get_available_media_formats(
|
||||
app: AppHandle,
|
||||
params: GetAvailableMediaFormatsParams,
|
||||
) -> CommandResult<AvailableMediaFormats> {
|
||||
let bili_client = app.get_bili_client();
|
||||
let result = match params {
|
||||
GetAvailableMediaFormatsParams::Normal(params) => {
|
||||
let media_url = bili_client
|
||||
.get_normal_url(¶ms.bvid, params.cid)
|
||||
.await
|
||||
.map_err(|err| CommandError::from("获取普通视频可用格式失败", err))?;
|
||||
|
||||
media_url.to_get_available_media_formats_result()
|
||||
}
|
||||
GetAvailableMediaFormatsParams::Bangumi(params) => {
|
||||
let media_url = bili_client
|
||||
.get_bangumi_url(params.cid)
|
||||
.await
|
||||
.map_err(|err| CommandError::from("获取番剧视频可用格式失败", err))?;
|
||||
|
||||
media_url.to_get_available_media_formats_result()
|
||||
}
|
||||
GetAvailableMediaFormatsParams::Cheese(params) => {
|
||||
let media_url = bili_client
|
||||
.get_cheese_url(params.ep_id)
|
||||
.await
|
||||
.map_err(|err| CommandError::from("获取课程视频可用格式失败", err))?;
|
||||
|
||||
media_url.to_get_available_media_formats_result()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ pub fn run() {
|
||||
get_logs_dir_size,
|
||||
show_path_in_file_manager,
|
||||
get_skip_segments,
|
||||
get_available_media_formats,
|
||||
])
|
||||
.events(tauri_specta::collect_events![LogEvent, DownloadEvent]);
|
||||
|
||||
|
||||
@@ -22,10 +22,13 @@ pub enum AudioQuality {
|
||||
Unknown = -1,
|
||||
|
||||
#[serde(rename = "64K")]
|
||||
#[num_enum(alternatives = [100008])]
|
||||
Audio64K = 30216,
|
||||
#[serde(rename = "132K")]
|
||||
#[num_enum(alternatives = [100009])]
|
||||
Audio132K = 30232,
|
||||
#[serde(rename = "192K")]
|
||||
#[num_enum(alternatives = [100010])]
|
||||
Audio192K = 30280,
|
||||
#[serde(rename = "Dolby")]
|
||||
AudioDolby = 30250,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
use crate::types::{
|
||||
audio_quality::AudioQuality, codec_type::CodecType, video_quality::VideoQuality,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct AvailableMediaFormats {
|
||||
pub video_qualities_and_codec_types: Vec<VideoQualityAndCodecType>,
|
||||
pub audio_qualities: Vec<AudioQuality>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct VideoQualityAndCodecType {
|
||||
pub video_quality: VideoQuality,
|
||||
pub codec_type: CodecType,
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
use crate::types::{
|
||||
audio_quality::AudioQuality,
|
||||
available_media_formats::{AvailableMediaFormats, VideoQualityAndCodecType},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[serde(default)]
|
||||
pub struct BangumiMediaUrl {
|
||||
@@ -130,3 +135,43 @@ pub struct DurlDetailInBangumi {
|
||||
pub order: i64,
|
||||
pub md5: String,
|
||||
}
|
||||
|
||||
impl BangumiMediaUrl {
|
||||
pub fn to_get_available_media_formats_result(&self) -> AvailableMediaFormats {
|
||||
let mut video_qualities_and_codec_types: Vec<VideoQualityAndCodecType> = Vec::new();
|
||||
let mut audio_qualities: Vec<AudioQuality> = Vec::new();
|
||||
|
||||
if let Some(dash) = &self.dash {
|
||||
for media in &dash.video {
|
||||
let video_qualities_and_codec_type = VideoQualityAndCodecType {
|
||||
video_quality: media.id.into(),
|
||||
codec_type: media.codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec_type);
|
||||
}
|
||||
}
|
||||
|
||||
for durl in &self.durls {
|
||||
if !durl.durl.is_empty() {
|
||||
let video_qualities_and_codec_type = VideoQualityAndCodecType {
|
||||
video_quality: durl.quality.into(),
|
||||
codec_type: self.video_codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec_type);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(medias) = self.dash.as_ref().and_then(|dash| dash.audio.as_ref()) {
|
||||
for media in medias {
|
||||
audio_qualities.push(media.id.into());
|
||||
}
|
||||
}
|
||||
|
||||
AvailableMediaFormats {
|
||||
video_qualities_and_codec_types,
|
||||
audio_qualities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
use crate::types::{
|
||||
audio_quality::AudioQuality,
|
||||
available_media_formats::{AvailableMediaFormats, VideoQualityAndCodecType},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[serde(default)]
|
||||
pub struct CheeseMediaUrl {
|
||||
@@ -122,3 +127,43 @@ pub struct DurlDetailInCheese {
|
||||
pub order: i64,
|
||||
pub md5: String,
|
||||
}
|
||||
|
||||
impl CheeseMediaUrl {
|
||||
pub fn to_get_available_media_formats_result(&self) -> AvailableMediaFormats {
|
||||
let mut video_qualities_and_codec_types: Vec<VideoQualityAndCodecType> = Vec::new();
|
||||
let mut audio_qualities: Vec<AudioQuality> = Vec::new();
|
||||
|
||||
if let Some(dash) = &self.dash {
|
||||
for media in &dash.video {
|
||||
let video_qualities_and_codec_type = VideoQualityAndCodecType {
|
||||
video_quality: media.id.into(),
|
||||
codec_type: media.codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec_type);
|
||||
}
|
||||
}
|
||||
|
||||
for durl in &self.durls {
|
||||
if !durl.durl.is_empty() {
|
||||
let video_qualities_and_codec_type = VideoQualityAndCodecType {
|
||||
video_quality: durl.quality.into(),
|
||||
codec_type: self.video_codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec_type);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(medias) = self.dash.as_ref().and_then(|dash| dash.audio.as_ref()) {
|
||||
for media in medias {
|
||||
audio_qualities.push(media.id.into());
|
||||
}
|
||||
}
|
||||
|
||||
AvailableMediaFormats {
|
||||
video_qualities_and_codec_types,
|
||||
audio_qualities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub enum GetAvailableMediaFormatsParams {
|
||||
Normal(GetNormalAvailableMediaFormatsParams),
|
||||
Bangumi(GetBangumiAvailableMediaFormatsParams),
|
||||
Cheese(GetCheeseAvailableMediaFormatsParams),
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct GetNormalAvailableMediaFormatsParams {
|
||||
pub bvid: String,
|
||||
pub cid: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct GetBangumiAvailableMediaFormatsParams {
|
||||
pub cid: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct GetCheeseAvailableMediaFormatsParams {
|
||||
pub ep_id: i64,
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod audio_quality;
|
||||
pub mod available_media_formats;
|
||||
pub mod bangumi_follow_info;
|
||||
pub mod bangumi_info;
|
||||
pub mod bangumi_media_url;
|
||||
@@ -8,6 +9,7 @@ pub mod codec_type;
|
||||
pub mod create_download_task_params;
|
||||
pub mod fav_folders;
|
||||
pub mod fav_info;
|
||||
pub mod get_available_media_formats_params;
|
||||
pub mod get_bangumi_follow_info_params;
|
||||
pub mod get_bangumi_info_params;
|
||||
pub mod get_cheese_info_params;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
use crate::types::{
|
||||
audio_quality::AudioQuality,
|
||||
available_media_formats::{AvailableMediaFormats, VideoQualityAndCodecType},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[serde(default)]
|
||||
pub struct NormalMediaUrl {
|
||||
@@ -103,3 +108,50 @@ pub struct SupportFormatInNormal {
|
||||
pub struct PlayConf {
|
||||
pub is_new_description: bool,
|
||||
}
|
||||
|
||||
impl NormalMediaUrl {
|
||||
pub fn to_get_available_media_formats_result(&self) -> AvailableMediaFormats {
|
||||
let mut video_qualities_and_codec_types: Vec<VideoQualityAndCodecType> = Vec::new();
|
||||
let mut audio_qualities: Vec<AudioQuality> = Vec::new();
|
||||
|
||||
for media in &self.dash.video {
|
||||
let video_qualities_and_codec = VideoQualityAndCodecType {
|
||||
video_quality: media.id.into(),
|
||||
codec_type: media.codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec);
|
||||
}
|
||||
|
||||
if !self.durl.is_empty() {
|
||||
let video_qualities_and_codec = VideoQualityAndCodecType {
|
||||
video_quality: self.quality.into(),
|
||||
codec_type: self.video_codecid.into(),
|
||||
};
|
||||
|
||||
video_qualities_and_codec_types.push(video_qualities_and_codec);
|
||||
}
|
||||
|
||||
if let Some(medias) = &self.dash.audio {
|
||||
for media in medias {
|
||||
audio_qualities.push(media.id.into());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(medias) = &self.dash.dolby.audio {
|
||||
for media in medias {
|
||||
audio_qualities.push(media.id.into());
|
||||
}
|
||||
}
|
||||
|
||||
let flac = self.dash.flac.as_ref();
|
||||
if let Some(media) = flac.and_then(|flac| flac.audio.as_ref()) {
|
||||
audio_qualities.push(media.id.into());
|
||||
}
|
||||
|
||||
AvailableMediaFormats {
|
||||
video_qualities_and_codec_types,
|
||||
audio_qualities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,6 +161,14 @@ async getSkipSegments(bvid: string, cid: number | null) : Promise<Result<SkipSeg
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async getAvailableMediaFormats(params: GetAvailableMediaFormatsParams) : Promise<Result<AvailableMediaFormats, CommandError>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("get_available_media_formats", { params }) };
|
||||
} catch (e) {
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +198,7 @@ export type ArgueInfo = { argue_msg: string; argue_type: number; argue_link: str
|
||||
export type AudioQuality = "Unknown" | "64K" | "132K" | "192K" | "Dolby" | "HiRes"
|
||||
export type AudioTask = { selected: boolean; url: string; audio_quality: AudioQuality; content_length: number; chunks: MediaChunk[]; completed: boolean; skipped: boolean }
|
||||
export type Author = { mid: number; name: string; face: string }
|
||||
export type AvailableMediaFormats = { video_qualities_and_codec_types: VideoQualityAndCodecType[]; audio_qualities: AudioQuality[] }
|
||||
export type BadgeInfoInBangumi = { bg_color: string; bg_color_night: string; text: string }
|
||||
export type BadgeInfoInBangumiFollow = { text: string | null; bg_color: string; bg_color_night: string; img: string | null; multi_img: MultiImg }
|
||||
export type BadgeInfos = { vip_or_pay: VipOrPay | null; content_attr: ContentAttr | null }
|
||||
@@ -295,15 +304,19 @@ export type FavSearchResult = FavInfo
|
||||
export type FileExistAction = "Overwrite" | "Skip"
|
||||
export type FirstEpInfo = { id: number; cover: string; title: string; long_title: string | null; pub_time: string; duration: number }
|
||||
export type Folder = { id: number; fid: number; mid: number; attr: number; title: string; fav_state: number; media_count: number }
|
||||
export type GetAvailableMediaFormatsParams = { Normal: GetNormalAvailableMediaFormatsParams } | { Bangumi: GetBangumiAvailableMediaFormatsParams } | { Cheese: GetCheeseAvailableMediaFormatsParams }
|
||||
export type GetBangumiAvailableMediaFormatsParams = { cid: number }
|
||||
export type GetBangumiFollowInfoParams = { vmid: number;
|
||||
/**
|
||||
* 1: 番剧 2: 电视剧或电影
|
||||
*/
|
||||
type: number; pn: number; follow_status: number }
|
||||
export type GetBangumiInfoParams = { EpId: number } | { SeasonId: number }
|
||||
export type GetCheeseAvailableMediaFormatsParams = { ep_id: number }
|
||||
export type GetCheeseInfoParams = { EpId: number } | { SeasonId: number }
|
||||
export type GetFavInfoParams = { media_list_id: number; pn: number }
|
||||
export type GetHistoryInfoParams = { pn: number; keyword: string; add_time_start: number; add_time_end: number; arc_max_duration: number; arc_min_duration: number; device_type: DeviceType }
|
||||
export type GetNormalAvailableMediaFormatsParams = { bvid: string; cid: number }
|
||||
export type GetNormalInfoParams = { Bvid: string } | { Aid: number }
|
||||
export type GetUserVideoInfoParams = { pn: number; mid: number }
|
||||
export type History = { oid: number; epid: number; bvid: string; page: number; cid: number; part: string; business: string; dt: number }
|
||||
@@ -409,6 +422,7 @@ export type UserVideoList = { vlist: EpInUserVideo[] }
|
||||
export type UserVideoSearchResult = UserVideoInfo
|
||||
export type VideoProcessTask = { merge_selected: boolean; embed_chapter_selected: boolean; embed_skip_selected: boolean; completed: boolean }
|
||||
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
|
||||
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 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 VipLabel = { path: string; text: string; label_theme: string; text_color: string; bg_style: number; bg_color: string; border_color: string; use_img_label: boolean; img_label_uri_hans: string; img_label_uri_hant: string; img_label_uri_hans_static: string; img_label_uri_hant_static: string }
|
||||
|
||||
Reference in New Issue
Block a user