mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-08 17:16:52 +08:00
feat: 后端支持获取指定用户的投稿
This commit is contained in:
Generated
+11
@@ -273,6 +273,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"float-ord",
|
||||
"fs4",
|
||||
"md-5",
|
||||
"memchr",
|
||||
"notify",
|
||||
"num_enum",
|
||||
@@ -2169,6 +2170,16 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
|
||||
@@ -49,6 +49,7 @@ prost = { version = "0.14.1" }
|
||||
yaserde = { version = "0.12.0", features = ["yaserde_derive"] }
|
||||
float-ord = { version = "0.3.2" }
|
||||
memchr = { version = "2.7.5" }
|
||||
md-5 = { version = "0.10.6" }
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
@@ -23,9 +23,10 @@ use crate::{
|
||||
cheese_media_url::CheeseMediaUrl, 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,
|
||||
normal_info::NormalInfo, normal_media_url::NormalMediaUrl, player_info::PlayerInfo,
|
||||
qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, subtitle::Subtitle, tags::Tags,
|
||||
user_info::UserInfo, watch_later_info::WatchLaterInfo,
|
||||
get_user_video_info_params::GetUserVideoInfoParams, normal_info::NormalInfo,
|
||||
normal_media_url::NormalMediaUrl, player_info::PlayerInfo, qrcode_data::QrcodeData,
|
||||
qrcode_status::QrcodeStatus, subtitle::Subtitle, tags::Tags, user_info::UserInfo,
|
||||
user_video_info::UserVideoInfo, watch_later_info::WatchLaterInfo,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -33,10 +34,10 @@ const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/
|
||||
const REFERRER: &str = "https://www.bilibili.com/";
|
||||
|
||||
pub struct BiliClient {
|
||||
app: AppHandle,
|
||||
api_client: RwLock<ClientWithMiddleware>,
|
||||
media_client: RwLock<ClientWithMiddleware>,
|
||||
content_length_client: RwLock<Client>,
|
||||
pub app: AppHandle,
|
||||
pub api_client: RwLock<ClientWithMiddleware>,
|
||||
pub media_client: RwLock<ClientWithMiddleware>,
|
||||
pub content_length_client: RwLock<Client>,
|
||||
}
|
||||
|
||||
impl BiliClient {
|
||||
@@ -281,6 +282,49 @@ impl BiliClient {
|
||||
Ok(cheese_info)
|
||||
}
|
||||
|
||||
pub async fn get_user_video_info(
|
||||
&self,
|
||||
params: GetUserVideoInfoParams,
|
||||
) -> anyhow::Result<UserVideoInfo> {
|
||||
let mut params: Vec<(&str, String)> = vec![
|
||||
("pn", params.pn.to_string()),
|
||||
("ps", "42".to_string()),
|
||||
("mid", params.mid.to_string()),
|
||||
];
|
||||
self.wbi(&mut params).await?;
|
||||
|
||||
let request = self
|
||||
.api_client
|
||||
.read()
|
||||
.get("https://api.bilibili.com/x/space/wbi/arc/search")
|
||||
.query(¶ms)
|
||||
.header("cookie", self.get_cookie());
|
||||
let http_resp = request.send().await?;
|
||||
// 检查http响应状态码
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为UserVideoInfo
|
||||
let data_str = data.to_string();
|
||||
let user_video_info: UserVideoInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为UserVideoInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(user_video_info)
|
||||
}
|
||||
|
||||
pub async fn get_normal_url(&self, bvid: &str, cid: i64) -> anyhow::Result<NormalMediaUrl> {
|
||||
let params = json!({
|
||||
"bvid": bvid,
|
||||
@@ -750,7 +794,7 @@ impl BiliClient {
|
||||
Ok(tags)
|
||||
}
|
||||
|
||||
fn get_cookie(&self) -> String {
|
||||
pub fn get_cookie(&self) -> String {
|
||||
let sessdata = self.app.get_config().read().sessdata.clone();
|
||||
format!("SESSDATA={sessdata}")
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ use crate::{
|
||||
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, normal_info::NormalInfo,
|
||||
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, watch_later_info::WatchLaterInfo,
|
||||
qrcode_status::QrcodeStatus, user_info::UserInfo, user_video_info::UserVideoInfo,
|
||||
watch_later_info::WatchLaterInfo,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -142,6 +144,20 @@ pub async fn get_cheese_info(
|
||||
Ok(cheese_info)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[specta::specta]
|
||||
pub async fn get_user_video_info(
|
||||
app: AppHandle,
|
||||
params: GetUserVideoInfoParams,
|
||||
) -> CommandResult<UserVideoInfo> {
|
||||
let bili_client = app.get_bili_client();
|
||||
let user_video_info = bili_client
|
||||
.get_user_video_info(params)
|
||||
.await
|
||||
.map_err(|err| CommandError::from("获取用户视频信息失败", err))?;
|
||||
Ok(user_video_info)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[specta::specta]
|
||||
pub async fn get_normal_url(
|
||||
|
||||
@@ -9,6 +9,7 @@ mod extensions;
|
||||
mod logger;
|
||||
mod types;
|
||||
mod utils;
|
||||
mod wbi;
|
||||
mod protobuf {
|
||||
include!("./bilibili.community.service.dm.v1.rs");
|
||||
}
|
||||
@@ -42,6 +43,7 @@ pub fn run() {
|
||||
get_normal_info,
|
||||
get_bangumi_info,
|
||||
get_cheese_info,
|
||||
get_user_video_info,
|
||||
get_normal_url,
|
||||
get_bangumi_url,
|
||||
get_cheese_url,
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct GetUserVideoInfoParams {
|
||||
pub pn: i64,
|
||||
pub mid: i64,
|
||||
}
|
||||
@@ -11,6 +11,7 @@ pub mod get_bangumi_info_params;
|
||||
pub mod get_cheese_info_params;
|
||||
pub mod get_fav_info_params;
|
||||
pub mod get_normal_info_params;
|
||||
pub mod get_user_video_info_params;
|
||||
pub mod log_level;
|
||||
pub mod normal_info;
|
||||
pub mod normal_media_url;
|
||||
@@ -20,5 +21,6 @@ pub mod qrcode_status;
|
||||
pub mod subtitle;
|
||||
pub mod tags;
|
||||
pub mod user_info;
|
||||
pub mod user_video_info;
|
||||
pub mod video_quality;
|
||||
pub mod watch_later_info;
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct UserVideoInfo {
|
||||
pub list: UserVideoList,
|
||||
pub page: PageInUserVideo,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct UserVideoList {
|
||||
pub vlist: Vec<EpInUserVideo>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct EpInUserVideo {
|
||||
pub comment: i64,
|
||||
pub typeid: i64,
|
||||
pub play: i64,
|
||||
pub pic: String,
|
||||
pub subtitle: String,
|
||||
pub description: String,
|
||||
pub copyright: String,
|
||||
pub title: String,
|
||||
pub review: i64,
|
||||
pub author: String,
|
||||
pub mid: i64,
|
||||
pub created: i64,
|
||||
pub length: String,
|
||||
pub video_review: i64,
|
||||
pub aid: i64,
|
||||
pub bvid: String,
|
||||
pub hide_click: bool,
|
||||
pub is_pay: i64,
|
||||
pub is_union_video: i64,
|
||||
pub is_steins_gate: i64,
|
||||
pub is_live_playback: i64,
|
||||
pub is_lesson_video: i64,
|
||||
pub is_lesson_finished: i64,
|
||||
pub lesson_update_info: String,
|
||||
pub jump_url: String,
|
||||
pub meta: Option<MetaInUserVideo>,
|
||||
pub is_avoided: i64,
|
||||
pub season_id: i64,
|
||||
pub attribute: i64,
|
||||
pub is_charging_arc: bool,
|
||||
pub elec_arc_type: i64,
|
||||
pub elec_arc_badge: String,
|
||||
pub vt: i64,
|
||||
pub enable_vt: i64,
|
||||
pub vt_display: String,
|
||||
pub playback_position: i64,
|
||||
pub is_self_view: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct MetaInUserVideo {
|
||||
pub id: i64,
|
||||
pub title: String,
|
||||
pub cover: String,
|
||||
pub mid: i64,
|
||||
pub intro: String,
|
||||
pub sign_state: i64,
|
||||
pub attribute: i64,
|
||||
pub stat: StatInUserVideo,
|
||||
pub ep_count: i64,
|
||||
pub first_aid: i64,
|
||||
pub ptime: i64,
|
||||
pub ep_num: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct StatInUserVideo {
|
||||
pub season_id: i64,
|
||||
pub view: i64,
|
||||
pub danmaku: i64,
|
||||
pub reply: i64,
|
||||
pub favorite: i64,
|
||||
pub coin: i64,
|
||||
pub share: i64,
|
||||
pub like: i64,
|
||||
pub mtime: i64,
|
||||
pub vt: i64,
|
||||
pub vv: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct PageInUserVideo {
|
||||
pub pn: i64,
|
||||
pub ps: i64,
|
||||
pub count: i64,
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use md5::{Digest, Md5};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::bili_client::{BiliClient, BiliResp};
|
||||
|
||||
const MIXIN_KEY_ENC_TAB: [usize; 64] = [
|
||||
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29,
|
||||
28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25,
|
||||
54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52,
|
||||
];
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WbiImgRespData {
|
||||
img_url: String,
|
||||
sub_url: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct WeiRespData {
|
||||
wbi_img: WbiImgRespData,
|
||||
}
|
||||
|
||||
impl BiliClient {
|
||||
// 为请求参数进行 wbi 签名
|
||||
pub(crate) async fn wbi(&self, params: &mut Vec<(&str, String)>) -> anyhow::Result<()> {
|
||||
let (img_key, sub_key) = self.get_wbi_keys().await.context("获取wbi keys失败")?;
|
||||
let mixin_key = get_mixin_key((img_key + &sub_key).as_bytes());
|
||||
|
||||
let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||
// 添加当前时间戳
|
||||
params.push(("wts", timestamp.to_string()));
|
||||
// 重新排序
|
||||
params.sort_by(|a, b| a.0.cmp(b.0));
|
||||
// 拼接参数
|
||||
let query = params
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{}={}", get_url_encoded(k), get_url_encoded(v)))
|
||||
.collect::<Vec<_>>()
|
||||
.join("&");
|
||||
// 计算签名
|
||||
let web_sign = format!("{:x}", Md5::digest(query.clone() + &mixin_key));
|
||||
params.push(("w_rid", web_sign));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_wbi_keys(&self) -> anyhow::Result<(String, String)> {
|
||||
let request = self
|
||||
.api_client
|
||||
.read()
|
||||
.get("https://api.bilibili.com/x/web-interface/nav")
|
||||
.header("Cookie", &self.get_cookie());
|
||||
|
||||
let http_resp = request.send().await?;
|
||||
// 检查http响应状态码
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != reqwest::StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为Data
|
||||
let data_str = data.to_string();
|
||||
let wei_resp_data: WeiRespData =
|
||||
serde_json::from_str(&data_str).context(format!("将data解析为Data失败: {data_str}"))?;
|
||||
|
||||
let img_url = wei_resp_data.wbi_img.img_url;
|
||||
let sub_url = wei_resp_data.wbi_img.sub_url;
|
||||
|
||||
let img_filename =
|
||||
take_filename(&img_url).context(format!("从img_url中提取文件名失败: {img_url}"))?;
|
||||
let sub_filename =
|
||||
take_filename(&sub_url).context(format!("从sub_url中提取文件名失败: {sub_url}"))?;
|
||||
|
||||
Ok((img_filename, sub_filename))
|
||||
}
|
||||
}
|
||||
|
||||
// 对 imgKey 和 subKey 进行字符顺序打乱编码
|
||||
fn get_mixin_key(orig: &[u8]) -> String {
|
||||
MIXIN_KEY_ENC_TAB
|
||||
.iter()
|
||||
.take(32)
|
||||
.map(|&i| orig[i] as char)
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
fn get_url_encoded(s: &str) -> String {
|
||||
s.chars()
|
||||
.filter_map(|c| {
|
||||
if c.is_ascii_alphanumeric() || "-_.~".contains(c) {
|
||||
Some(c.to_string())
|
||||
} else {
|
||||
// 过滤 value 中的 "!'()*" 字符
|
||||
if "!'()*".contains(c) {
|
||||
return None;
|
||||
}
|
||||
let encoded = c
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.bytes()
|
||||
.fold(String::new(), |acc, b| acc + &format!("%{b:02X}"));
|
||||
Some(encoded)
|
||||
}
|
||||
})
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
fn take_filename(url: &str) -> Option<String> {
|
||||
url.rsplit_once('/')
|
||||
.and_then(|(_, s)| s.rsplit_once('.'))
|
||||
.map(|(s, _)| s.to_string())
|
||||
}
|
||||
@@ -67,6 +67,14 @@ async getCheeseInfo(params: GetCheeseInfoParams) : Promise<Result<CheeseInfo, Co
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async getUserVideoInfo(params: GetUserVideoInfoParams) : Promise<Result<UserVideoInfo, CommandError>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("get_user_video_info", { params }) };
|
||||
} catch (e) {
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async getNormalUrl(bvid: string, cid: number) : Promise<Result<NormalMediaUrl, CommandError>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("get_normal_url", { bvid, cid }) };
|
||||
@@ -270,6 +278,7 @@ export type ElecHighLevel = { privilege_type: number; title: string; sub_title:
|
||||
export type EpInBangumi = { aid: number; badge: string; badge_info: BadgeInfo; badge_type: number | null; bvid: string | null; cid: number; cover: string; dimension: DimensionInBangumi | null; duration: number | null; enable_vt: boolean; ep_id: number; from: string | null; id: number; is_view_hide: boolean; link: string; link_type: string | null; long_title: string | null; pub_time: number; pv: number; release_date: string | null; rights: RightsInBangumiEp | null; section_type: number; share_copy: string | null; share_url: string | null; short_link: string | null; showDrmLoginDialog: boolean; show_title: string | null; skip: Skip | null; status: number; subtitle: string | null; title: string; vid: string | null; icon_font: IconFont | null }
|
||||
export type EpInCheese = { aid: number; catalogue_index: number; cid: number; cover: string; duration: number; ep_status: number; episode_can_view: boolean; from: string; id: number; index: number; label: string | null; page: number; play: number; play_way: number; playable: boolean; release_date: number; show_vt: boolean; status: number; subtitle: string; title: string; watched: boolean; watchedHistory: number }
|
||||
export type EpInNormal = { season_id: number; section_id: number; id: number; aid: number; cid: number; title: string; attribute: number; arc: Arc; page: PageInNormalEp; bvid: string; pages: PageInNormalEp[] }
|
||||
export type EpInUserVideo = { comment: number; typeid: number; play: number; pic: string; subtitle: string; description: string; copyright: string; title: string; review: number; author: string; mid: number; created: number; length: string; video_review: number; aid: number; bvid: string; hide_click: boolean; is_pay: number; is_union_video: number; is_steins_gate: number; is_live_playback: number; is_lesson_video: number; is_lesson_finished: number; lesson_update_info: string; jump_url: string; meta: MetaInUserVideo | null; is_avoided: number; season_id: number; attribute: number; is_charging_arc: boolean; elec_arc_type: number; elec_arc_badge: string; vt: number; enable_vt: number; vt_display: string; playback_position: number; is_self_view: boolean }
|
||||
export type EpPage = { next: boolean; num: number; size: number; total: number }
|
||||
export type EpTag = { part_preview_tag: string; pay_tag: string; preview_tag: string }
|
||||
export type EpisodeType = "Normal" | "Bangumi" | "Cheese"
|
||||
@@ -285,6 +294,7 @@ export type GetBangumiInfoParams = { EpId: number } | { SeasonId: number }
|
||||
export type GetCheeseInfoParams = { EpId: number } | { SeasonId: number }
|
||||
export type GetFavInfoParams = { media_list_id: number; pn: number }
|
||||
export type GetNormalInfoParams = { Bvid: string } | { Aid: number }
|
||||
export type GetUserVideoInfoParams = { pn: number; mid: number }
|
||||
export type Honor = { aid: number; type: number; desc: string; weekly_recommend_num: number }
|
||||
export type HonorReply = { honor: Honor[] | null }
|
||||
export type IconFont = { name: string; text: string }
|
||||
@@ -307,6 +317,7 @@ export type MediaInFav = { id: number; type: number; title: string; cover: strin
|
||||
export type MediaInNormal = { id: number; start_with_sap: number; bandwidth: number; sar: string; codecs: string; base_url: string; backup_url: string[]; segment_base: SegmentBaseInNormal; mime_type: string; frame_rate: string; width: number; height: number; codecid: number }
|
||||
export type MediaInWatchLater = { aid: number; videos: number; tid: number; tname: string; copyright: number; pic: string; title: string; pubdate: number; ctime: number; desc: string; state: number; duration: number; redirect_url: string | null; mission_id: number | null; rights: RightsInWatchLater; owner: OwnerInWatchLater; stat: StatInWatchLater; dynamic: string; dimension: DimensionInWatchLater; short_link_v2: string; up_from_v2: number | null; first_frame: string | null; pub_location: string | null; cover43: string; tidv2: number; tnamev2: string; pid_v2: number; pid_name_v2: string; page: PageInWatchLater; count: number; cid: number; progress: number; add_at: number; bvid: string; uri: string; enable_vt: number; view_text_1: string; card_type: number; left_icon_type: number; left_text: string; right_icon_type: number; right_text: string; arc_state: number; pgc_label: string; show_up: boolean; forbid_fav: boolean; forbid_sort: boolean; season_title: string; long_title: string; index_title: string; c_source: string; season_id: number | null }
|
||||
export type MergeTask = { selected: boolean; completed: boolean }
|
||||
export type MetaInUserVideo = { id: number; title: string; cover: string; mid: number; intro: string; sign_state: number; attribute: number; stat: StatInUserVideo; ep_count: number; first_aid: number; ptime: number; ep_num: number }
|
||||
export type NewEp = { desc: string; id: number; is_new: number; title: string }
|
||||
export type NewEpInSeason = { cover: string; id: number; index_show: string }
|
||||
export type NfoTask = { selected: boolean; completed: boolean }
|
||||
@@ -321,6 +332,7 @@ export type OwnerInNormal = { mid: number; name: string; face: string }
|
||||
export type OwnerInWatchLater = { mid: number; name: string; face: string }
|
||||
export type PageInNormal = { cid: number; page: number; from: string; part: string; duration: number; vid: string; weblink: string; dimension: Dimension; ctime: number }
|
||||
export type PageInNormalEp = { cid: number; page: number; from: string; part: string; duration: number; vid: string; weblink: string; dimension: Dimension }
|
||||
export type PageInUserVideo = { pn: number; ps: number; count: number }
|
||||
export type PageInWatchLater = { cid: number; page: number; from: string; part: string; duration: number; vid: string; weblink: string; dimension: DimensionInWatchLater; first_frame: string | null; ctime: number }
|
||||
export type PaidJump = { jump_url_for_app: string; url: string }
|
||||
export type PayType = { allow_discount: number; allow_pack: number; allow_ticket: number; allow_time_limit: number; allow_vip_discount: number; forbid_bb: number }
|
||||
@@ -368,6 +380,7 @@ export type StatInNormal = { aid: number; view: number; danmaku: number; reply:
|
||||
export type StatInNormalEp = { aid: number; view: number; danmaku: number; reply: number; fav: number; coin: number; share: number; now_rank: number; his_rank: number; like: number; dislike: number; evaluation: string; argue_msg: string; vt: number; vv: number }
|
||||
export type StatInNormalSeason = { season_id: number; view: number; danmaku: number; reply: number; fav: number; coin: number; share: number; now_rank: number; his_rank: number; like: number; vt: number; vv: number }
|
||||
export type StatInSeason = { favorites: number; series_follow: number; views: number; vt: number }
|
||||
export type StatInUserVideo = { season_id: number; view: number; danmaku: number; reply: number; favorite: number; coin: number; share: number; like: number; mtime: number; vt: number; vv: number }
|
||||
export type StatInWatchLater = { aid: number; view: number; danmaku: number; reply: number; favorite: number; coin: number; share: number; now_rank: number; his_rank: number; like: number; dislike: number; vt: number; vv: number }
|
||||
export type SubtitleDetailInNormal = { id: number; lan: string; lan_doc: string; is_lock: boolean; subtitle_url: string; type: number; id_str: string; ai_type: number; ai_status: number }
|
||||
export type SubtitleDetailInPlayerInfo = { id: number; lan: string; lan_doc: string; is_lock: boolean; subtitle_url: string; type: number; id_str: string; ai_type: number; ai_status: number }
|
||||
@@ -388,6 +401,8 @@ export type UserInfo = { isLogin: boolean; email_verified: number; face: string;
|
||||
export type UserStatusInBangumi = { area_limit: number; ban_area_show: number; follow: number; follow_status: number; login: number; pay: number; pay_pack_paid: number; sponsor: number }
|
||||
export type UserStatusInCheese = { bp: number; expire_at: number; favored: number; favored_count: number; is_expired: boolean; is_first_paid: boolean; payed: number; user_expiry_content: string }
|
||||
export type UserStatusInCheeseUrl = { watch_progress: WatchProgress }
|
||||
export type UserVideoInfo = { list: UserVideoList; page: PageInUserVideo }
|
||||
export type UserVideoList = { vlist: EpInUserVideo[] }
|
||||
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 }
|
||||
|
||||
Reference in New Issue
Block a user