feat: 后端支持搜索

This commit is contained in:
lanyeeee
2025-07-31 07:46:33 +08:00
parent c1999aac82
commit 5fd8d7363b
6 changed files with 170 additions and 8 deletions
+94 -7
View File
@@ -7,14 +7,30 @@ use crate::{
extensions::AppHandleExt,
logger,
types::{
bangumi_info::BangumiInfo, bangumi_media_url::BangumiMediaUrl, cheese_info::CheeseInfo,
cheese_media_url::CheeseMediaUrl, create_download_task_params::CreateDownloadTaskParams,
fav_folders::FavFolders, fav_info::FavInfo, get_bangumi_info_params::GetBangumiInfoParams,
get_cheese_info_params::GetCheeseInfoParams, get_fav_info_params::GetFavInfoParams,
bangumi_info::{BangumiInfo, EpInBangumi},
bangumi_media_url::BangumiMediaUrl,
cheese_info::CheeseInfo,
cheese_media_url::CheeseMediaUrl,
create_download_task_params::CreateDownloadTaskParams,
fav_folders::FavFolders,
fav_info::FavInfo,
get_bangumi_info_params::GetBangumiInfoParams,
get_cheese_info_params::GetCheeseInfoParams,
get_fav_info_params::GetFavInfoParams,
get_normal_info_params::GetNormalInfoParams,
get_user_video_info_params::GetUserVideoInfoParams, normal_info::NormalInfo,
normal_media_url::NormalMediaUrl, player_info::PlayerInfo, qrcode_data::QrcodeData,
qrcode_status::QrcodeStatus, user_info::UserInfo, user_video_info::UserVideoInfo,
get_user_video_info_params::GetUserVideoInfoParams,
normal_info::NormalInfo,
normal_media_url::NormalMediaUrl,
player_info::PlayerInfo,
qrcode_data::QrcodeData,
qrcode_status::QrcodeStatus,
search_params::SearchParams,
search_result::{
BangumiSearchResult, CheeseSearchResult, FavSearchResult, NormalSearchResult,
SearchResult, UserVideoSearchResult,
},
user_info::UserInfo,
user_video_info::UserVideoInfo,
watch_later_info::WatchLaterInfo,
},
};
@@ -291,3 +307,74 @@ pub fn restore_download_tasks(app: AppHandle) -> CommandResult<()> {
tracing::debug!("恢复下载任务成功");
Ok(())
}
#[tauri::command(async)]
#[specta::specta]
pub async fn search(app: AppHandle, params: SearchParams) -> CommandResult<SearchResult> {
use SearchParams::{Bangumi, Cheese, Fav, Normal, UserVideo};
let bili_client = app.get_bili_client();
let search_result = match params {
Normal(params) => {
let info = bili_client
.get_normal_info(params)
.await
.map_err(|err| CommandError::from("获取普通视频信息失败", err))?;
SearchResult::Normal(NormalSearchResult(info))
}
Bangumi(GetBangumiInfoParams::EpId(ep_id)) => {
let info = bili_client
.get_bangumi_info(GetBangumiInfoParams::EpId(ep_id))
.await
.map_err(|err| CommandError::from("获取番剧视频信息失败", err))?;
let episodes: Vec<&EpInBangumi> = info
.episodes
.iter()
.chain(
info.section
.iter()
.flat_map(|sections| sections.iter())
.flat_map(|section| section.episodes.iter()),
)
.collect();
let ep = episodes.iter().find(|ep| ep.id == ep_id).copied().cloned();
SearchResult::Bangumi(BangumiSearchResult { ep, info })
}
Bangumi(GetBangumiInfoParams::SeasonId(season_id)) => {
let info = bili_client
.get_bangumi_info(GetBangumiInfoParams::SeasonId(season_id))
.await
.map_err(|err| CommandError::from("获取番剧视频信息失败", err))?;
SearchResult::Bangumi(BangumiSearchResult { ep: None, info })
}
Cheese(GetCheeseInfoParams::EpId(ep_id)) => {
let info = bili_client
.get_cheese_info(GetCheeseInfoParams::EpId(ep_id))
.await
.map_err(|err| CommandError::from("获取课程视频信息失败", err))?;
let ep = info.episodes.iter().find(|ep| ep.id == ep_id).cloned();
SearchResult::Cheese(CheeseSearchResult { ep, info })
}
Cheese(GetCheeseInfoParams::SeasonId(season_id)) => {
let info = bili_client
.get_cheese_info(GetCheeseInfoParams::SeasonId(season_id))
.await
.map_err(|err| CommandError::from("获取课程视频信息失败", err))?;
SearchResult::Cheese(CheeseSearchResult { ep: None, info })
}
UserVideo(params) => {
let info = bili_client
.get_user_video_info(params)
.await
.map_err(|err| CommandError::from("获取用户视频信息失败", err))?;
SearchResult::UserVideo(UserVideoSearchResult(info))
}
Fav(params) => {
let info = bili_client
.get_fav_info(params)
.await
.map_err(|err| CommandError::from("获取收藏夹内容失败", err))?;
SearchResult::Fav(FavSearchResult(info))
}
};
Ok(search_result)
}
+1
View File
@@ -57,6 +57,7 @@ pub fn run() {
delete_download_tasks,
restart_download_tasks,
restore_download_tasks,
search,
])
.events(tauri_specta::collect_events![LogEvent, DownloadEvent]);
+2
View File
@@ -18,6 +18,8 @@ pub mod normal_media_url;
pub mod player_info;
pub mod qrcode_data;
pub mod qrcode_status;
pub mod search_params;
pub mod search_result;
pub mod subtitle;
pub mod tags;
pub mod user_info;
+17
View File
@@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
use specta::Type;
use super::{
get_bangumi_info_params::GetBangumiInfoParams, get_cheese_info_params::GetCheeseInfoParams,
get_fav_info_params::GetFavInfoParams, get_normal_info_params::GetNormalInfoParams,
get_user_video_info_params::GetUserVideoInfoParams,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub enum SearchParams {
Normal(GetNormalInfoParams),
Bangumi(GetBangumiInfoParams),
Cheese(GetCheeseInfoParams),
UserVideo(GetUserVideoInfoParams),
Fav(GetFavInfoParams),
}
+40
View File
@@ -0,0 +1,40 @@
use serde::{Deserialize, Serialize};
use specta::Type;
use super::{
bangumi_info::{self, BangumiInfo},
cheese_info::{self, CheeseInfo},
fav_info::FavInfo,
normal_info::NormalInfo,
user_video_info::UserVideoInfo,
};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub enum SearchResult {
Normal(NormalSearchResult),
Bangumi(BangumiSearchResult),
Cheese(CheeseSearchResult),
UserVideo(UserVideoSearchResult),
Fav(FavSearchResult),
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct NormalSearchResult(pub NormalInfo);
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct BangumiSearchResult {
pub ep: Option<bangumi_info::EpInBangumi>,
pub info: BangumiInfo,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct CheeseSearchResult {
pub ep: Option<cheese_info::EpInCheese>,
pub info: CheeseInfo,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct UserVideoSearchResult(pub UserVideoInfo);
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct FavSearchResult(pub FavInfo);
+16 -1
View File
@@ -153,6 +153,14 @@ async restoreDownloadTasks() : Promise<Result<null, CommandError>> {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async search(params: SearchParams) : Promise<Result<SearchResult, CommandError>> {
try {
return { status: "ok", data: await TAURI_INVOKE("search", { params }) };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
}
}
@@ -183,8 +191,9 @@ export type AudioTask = { selected: boolean; url: string; audio_quality: AudioQu
export type Author = { mid: number; name: string; face: string }
export type AvatarIcon = { icon_resource: IconResource }
export type BadgeInfo = { bg_color: string; bg_color_night: string; text: string }
export type BangumiMediaUrl = { accept_format: string; code: number; seek_param: string; is_preview: number; fnval: number; video_project: boolean; fnver: number; type: string; bp: number; seek_type: string; result: string; vip_type: number | null; from: string; video_codecid: number; record_info: RecordInfo | null; is_drm: boolean; no_rexcode: number; format: string; support_formats: SupportFormatInBangumi[]; message: string; accept_quality: number[]; quality: number; timelength: number; durls: DurlInBangumi[]; has_paid: boolean; vip_status: number | null; error_code: number; dash: DashInBangumi | null; clip_info_list: ClipInfoList[]; accept_description: string[]; status: number }
export type BangumiInfo = { activity: Activity; actors: string; alias: string; areas: Area[]; bkg_cover: string; cover: string; delivery_fragment_video: boolean; enable_vt: boolean; episodes: EpInBangumi[]; evaluate: string; hide_ep_vv_vt_dm: number; icon_font: IconFont; jp_title: string; link: string; media_id: number; mode: number; new_ep: NewEp; payment: PaymentInBangumi | null; play_strategy: PlayStrategy | null; positive: Positive; publish: Publish; rating: Rating | null; record: string; rights: RightsInBangumi; season_id: number; season_title: string; seasons: Season[]; section: SectionInBangumi[] | null; series: Series; share_copy: string; share_sub_title: string; share_url: string; show: Show; show_season_type: number; square_cover: string; staff: string; stat: StatInBangumi; status: number; styles: string[]; subtitle: string; title: string; total: number; type: number; up_info: UpInfoInBangumi | null; user_status: UserStatusInBangumi }
export type BangumiMediaUrl = { accept_format: string; code: number; seek_param: string; is_preview: number; fnval: number; video_project: boolean; fnver: number; type: string; bp: number; seek_type: string; result: string; vip_type: number | null; from: string; video_codecid: number; record_info: RecordInfo | null; is_drm: boolean; no_rexcode: number; format: string; support_formats: SupportFormatInBangumi[]; message: string; accept_quality: number[]; quality: number; timelength: number; durls: DurlInBangumi[]; has_paid: boolean; vip_status: number | null; error_code: number; dash: DashInBangumi | null; clip_info_list: ClipInfoList[]; accept_description: string[]; status: number }
export type BangumiSearchResult = { ep: EpInBangumi | null; info: BangumiInfo }
export type Brief = { content: string; img: Img[]; title: string; type: number }
export type CanvasConfig = {
/**
@@ -243,6 +252,7 @@ outline: number;
time_offset: number }
export type CheeseInfo = { abtest_info: AbtestInfo; be_subscription: boolean; brief: Brief; consulting: Consulting; cooperation: Cooperation; course_content: string; cover: string; ep_count: number; episode_page: EpPage; episode_sort: number; episode_tag: EpTag; episodes: EpInCheese[]; expiry_day: number; expiry_info_content: string; faq: Faq; faq1: Faq1; is_enable_cash: boolean; is_series: boolean; live_ep_count: number; opened_ep_count: number; paid_jump: PaidJump; paid_view: boolean; payment: Payment; previewed_purchase_note: PreviewedPurchaseNote; purchase_format_note: PurchaseFormatNote; purchase_note: PurchaseNote; purchase_protocol: PurchaseProtocol; recommend_seasons: RecommendSeason[]; release_bottom_info: string; release_info: string; release_info2: string; release_status: string; season_id: number; season_tag: number; share_url: string; short_link: string; show_watermark: boolean; stat: StatInCheese; status: number; stop_sell: boolean; subscription_update_count_cycle_text: string; subtitle: string; title: string; up_info: UpInfoInCheese; update_status: number; user_status: UserStatusInCheese; watermark_interval: number }
export type CheeseMediaUrl = { accept_format: string; code: number; seek_param: string; is_preview: number; fnval: number; video_project: boolean; play_view_business_info: PlayViewBusinessInfo | null; fnver: number; type: string; result: string; seek_type: string; from: string; video_codecid: number; no_rexcode: number; format: string; support_formats: SupportFormatInCheese[]; message: string; accept_quality: number[]; quality: number; timelength: number; durls: DurlInCheese[]; has_paid: boolean; dash: DashInCheese | null; accept_description: string[]; status: number }
export type CheeseSearchResult = { ep: EpInCheese | null; info: CheeseInfo }
export type ClipInfoList = { materialNo: number; start: number; end: number; toastText: string; clipType: string }
export type CntInfo = { collect: number; play: number; thumb_up: number; share: number }
export type CntInfoInMedia = { collect: number; play: number; danmaku: number; vt: number; play_switch: number; reply: number; view_text_1: string }
@@ -287,6 +297,7 @@ export type Faq1 = { items: Faq1Item[]; title: string }
export type Faq1Item = { answer: string; question: string }
export type FavFolders = { count: number; list: Folder[] }
export type FavInfo = { info: Info; medias: MediaInFav[] | null; has_more: boolean; ttl: number }
export type FavSearchResult = FavInfo
export type Fawkes = { config_version: number; ff_version: number }
export type Flac = { display: boolean; audio: MediaInNormal | null }
export type Folder = { id: number; fid: number; mid: number; attr: number; title: string; fav_state: number; media_count: number }
@@ -323,6 +334,7 @@ export type NewEpInSeason = { cover: string; id: number; index_show: string }
export type NfoTask = { selected: boolean; completed: boolean }
export type NormalInfo = { bvid: string; aid: number; videos: number; tid: number; tid_v2: number; tname: string; tname_v2: string; copyright: number; pic: string; title: string; pubdate: number; ctime: number; desc: string; desc_v2: DescV2[] | null; state: number; duration: number; rights: Rights; owner: OwnerInNormal; stat: StatInNormal; argue_info: ArgueInfo; dynamic: string; cid: number; dimension: Dimension; teenage_mode: number; is_chargeable_season: boolean; is_story: boolean; is_upower_exclusive: boolean; is_upower_play: boolean; is_upower_preview: boolean; enable_vt: number; vt_display: string; is_upower_exclusive_with_qa: boolean; no_cache: boolean; pages: PageInNormal[]; subtitle: SubtitleInNormal; staff: Staff[] | null; ugc_season: UgcSeason | null; is_season_display: boolean; user_garb: UserGarb; honor_reply: HonorReply; like_icon: string; need_jump_bv: boolean; disable_show_up_info: boolean; is_story_play: number; is_view_self: boolean }
export type NormalMediaUrl = { from: string; result: string; message: string; quality: number; format: string; timelength: number; accept_format: string; accept_description: string[]; accept_quality: number[]; video_codecid: number; seek_param: string; seek_type: string; dash: DashInNormal; support_formats: SupportFormatInNormal[]; last_play_time: number; last_play_cid: number; play_conf: PlayConf }
export type NormalSearchResult = NormalInfo
export type Official = { role: number; title: string; desc: string; type: number }
export type OfficialVerify = { type: number; desc: string }
export type OnlineSwitch = { enable_gray_dash_playback: string; new_broadcast: string; realtime_dm: string; subtitle_submit_switch: string }
@@ -363,6 +375,8 @@ export type RightsInBangumi = { allow_bp: number; allow_bp_rank: number; allow_d
export type RightsInBangumiEp = { allow_dm: number; allow_download: number; area_limit: number }
export type RightsInNormalEp = { bp: number; elec: number; download: number; movie: number; pay: number; hd5: number; no_reprint: number; autoplay: number; ugc_pay: number; is_cooperation: number; ugc_pay_preview: number; arc_pay: number; free_watch: number }
export type RightsInWatchLater = { bp: number; elec: number; download: number; movie: number; pay: number; hd5: number; no_reprint: number; autoplay: number; ugc_pay: number; is_cooperation: number; ugc_pay_preview: number; no_background: number; arc_pay: number; pay_free_watch: number }
export type SearchParams = { Normal: GetNormalInfoParams } | { Bangumi: GetBangumiInfoParams } | { Cheese: GetCheeseInfoParams } | { UserVideo: GetUserVideoInfoParams } | { Fav: GetFavInfoParams }
export type SearchResult = { Normal: NormalSearchResult } | { Bangumi: BangumiSearchResult } | { Cheese: CheeseSearchResult } | { UserVideo: UserVideoSearchResult } | { Fav: FavSearchResult }
export type Season = { badge: string; badge_info: BadgeInfo; badge_type: number; cover: string; enable_vt: boolean; horizontal_cover_1610: string; horizontal_cover_169: string; icon_font: IconFont; media_id: number; new_ep: NewEpInSeason; season_id: number; season_title: string; season_type: number; stat: StatInSeason }
export type SectionInBangumi = { attr: number; episodes: EpInBangumi[]; id: number; title: string; type: number; type2: number }
export type SectionInNormal = { season_id: number; id: number; title: string; type: number; episodes: EpInNormal[] }
@@ -403,6 +417,7 @@ export type UserStatusInCheese = { bp: number; expire_at: number; favored: numbe
export type UserStatusInCheeseUrl = { watch_progress: WatchProgress }
export type UserVideoInfo = { list: UserVideoList; page: PageInUserVideo }
export type UserVideoList = { vlist: EpInUserVideo[] }
export type UserVideoSearchResult = UserVideoInfo
export type VideoQuality = "Unknown" | "240P" | "360P" | "480P" | "720P" | "720P60" | "1080P" | "AiRepair" | "1080P+" | "1080P60" | "4K" | "HDR" | "Dolby" | "8K"
export type VideoTask = { selected: boolean; url: string; video_quality: VideoQuality; codec_type: CodecType; content_length: number; chunks: MediaChunk[]; completed: boolean }
export type ViewPoint = { type: number; from: number; to: number; content: string; img_url: string | null; logo_url: string | null; team_type: string; team_name: string }