Skip to content

Commit

Permalink
fix bilibili video
Browse files Browse the repository at this point in the history
  • Loading branch information
THMonster committed Dec 12, 2024
1 parent d7078d5 commit d0d909d
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 23 additions & 6 deletions src/streamfinder/bilibili.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use url::Url;
const BILI_API1: &'static str = "https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo";
const BILI_API2: &'static str = "https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom";
const BILI_API3: &'static str = "https://api.live.bilibili.com/room/v1/Room/playUrl";
// const BILI_APIV: &'static str = "https://api.bilibili.com/x/player/playurl";
const BILI_APIV: &'static str = "https://api.bilibili.com/x/player/wbi/playurl";
// const BILI_APIV_EP: &'static str = "https://api.bilibili.com/pgc/player/web/playurl";
const BILI_APIV_EP_LIST: &'static str = "https://api.bilibili.com/pgc/view/web/ep/list";

Expand Down Expand Up @@ -297,14 +297,31 @@ impl Bilibili {
self.cm.bvideo_info.borrow().current_page.to_string()
};
param1.push(("p", p));
let resp = client.get(&u).header("Cookie", cookies).query(&param1).send().await?.text().await?;
let (_bvid, cid, title, _artist) = self.get_page_info(&resp, page).await?;
let resp = client.get(&u).header("Cookie", &cookies).query(&param1).send().await?.text().await?;
let (bvid, cid, title, _artist) = self.get_page_info(&resp, page).await?;
ret.push(title);
ret.push(cid.clone());
// println!("{} {} {} {}", &bvid, &cid, &title, &artist);
let re = Regex::new(r"window.__playinfo__\s*=\s*(\{.+?\})\s*</script>").unwrap();
let j: serde_json::Value =
serde_json::from_str(re.captures(&resp).ok_or_else(|| dmlerr!())?[1].to_string().as_ref())?;
// let re = Regex::new(r"window.__playinfo__\s*=\s*(\{.+?\})\s*</script>").unwrap();
// let j: serde_json::Value =
// serde_json::from_str(re.captures(&resp).ok_or_else(|| dmlerr!())?[1].to_string().as_ref())?;
let keys = crate::utils::bili_wbi::get_wbi_keys(&cookies).await?;
let params2 = vec![
("bvid", bvid),
("cid", cid),
("qn", String::from("0")),
("fnval", String::from("80")),
("fnver", String::from("0")),
("fourk", String::from("1")),
];
let query = crate::utils::bili_wbi::encode_wbi(params2, keys);
let j = client
.get(format!("{}?{}", BILI_APIV, query))
.header("Cookie", &cookies)
.send()
.await?
.json::<serde_json::Value>()
.await?;
let j = j.pointer("/data").ok_or_else(|| dmlerr!())?;
f1(&j, &mut ret)?;
}
Expand Down
96 changes: 96 additions & 0 deletions src/utils/bili_wbi.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// from SocialSisterYi/bilibili-API-collect
use reqwest::header::USER_AGENT;
use serde::Deserialize;
use std::time::{SystemTime, UNIX_EPOCH};

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 WbiImg {
img_url: String,
sub_url: String,
}

#[derive(Deserialize)]
struct Data {
wbi_img: WbiImg,
}

#[derive(Deserialize)]
struct ResWbi {
data: Data,
}

// 对 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| match c.is_ascii_alphanumeric() || "-_.~".contains(c) {
true => Some(c.to_string()),
false => {
// 过滤 value 中的 "!'()*" 字符
if "!'()*".contains(c) {
return None;
}
let encoded =
c.encode_utf8(&mut [0; 4]).bytes().fold("".to_string(), |acc, b| acc + &format!("%{:02X}", b));
Some(encoded)
}
})
.collect::<String>()
}

// 为请求参数进行 wbi 签名
pub fn encode_wbi(params: Vec<(&str, String)>, (img_key, sub_key): (String, String)) -> String {
let cur_time = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(t) => t.as_secs(),
Err(_) => panic!("SystemTime before UNIX EPOCH!"),
};
_encode_wbi(params, (img_key, sub_key), cur_time)
}

fn _encode_wbi(mut params: Vec<(&str, String)>, (img_key, sub_key): (String, String), timestamp: u64) -> String {
let mixin_key = get_mixin_key((img_key + &sub_key).as_bytes());
// 添加当前时间戳
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!("{:?}", md5::compute(query.clone() + &mixin_key));
// 返回最终的 query
query + &format!("&w_rid={}", web_sign)
}

pub async fn get_wbi_keys(cookies: &str) -> Result<(String, String), reqwest::Error> {
let client = reqwest::Client::new();
let ResWbi { data: Data { wbi_img } } = client
.get("https://api.bilibili.com/x/web-interface/nav")
.header(USER_AGENT, crate::utils::gen_ua_safari())
.header("Referer", "https://www.bilibili.com/")
.header("Cookie", cookies)
.send()
.await?
.json::<ResWbi>()
.await?;
Ok((
take_filename(wbi_img.img_url).unwrap(),
take_filename(wbi_img.sub_url).unwrap(),
))
}

fn take_filename(url: String) -> Option<String> {
url.rsplit_once('/').and_then(|(_, s)| s.rsplit_once('.')).map(|(s, _)| s.to_string())
}
1 change: 1 addition & 0 deletions src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod cookies;
pub mod bili_wbi;

use log::info;
use tokio::{
Expand Down

0 comments on commit d0d909d

Please sign in to comment.