feat: 支持获取我的收藏夹、收藏的视频合集与关注的 up 主 (#349)
This commit is contained in:
119
crates/bili_sync/src/bilibili/me.rs
Normal file
119
crates/bili_sync/src/bilibili/me.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use anyhow::Result;
|
||||
use reqwest::Method;
|
||||
|
||||
use crate::bilibili::{BiliClient, Validate};
|
||||
use crate::config::CONFIG;
|
||||
pub struct Me<'a> {
|
||||
client: &'a BiliClient,
|
||||
mid: String,
|
||||
}
|
||||
|
||||
impl<'a> Me<'a> {
|
||||
pub fn new(client: &'a BiliClient) -> Self {
|
||||
Self {
|
||||
client,
|
||||
mid: Self::my_id(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_created_favorites(&self) -> Result<Option<Vec<FavoriteItem>>> {
|
||||
let mut resp = self
|
||||
.client
|
||||
.request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/created/list-all")
|
||||
.await
|
||||
.query(&[("up_mid", &self.mid)])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json::<serde_json::Value>()
|
||||
.await?
|
||||
.validate()?;
|
||||
Ok(serde_json::from_value(resp["data"]["list"].take())?)
|
||||
}
|
||||
|
||||
pub async fn get_followed_collections(&self, page: i32) -> Result<Collections> {
|
||||
let mut resp = self
|
||||
.client
|
||||
.request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/collected/list")
|
||||
.await
|
||||
.query(&[
|
||||
("up_mid", self.mid.as_ref()),
|
||||
("pn", page.to_string().as_ref()),
|
||||
("ps", "20"),
|
||||
("platform", "web"),
|
||||
])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json::<serde_json::Value>()
|
||||
.await?
|
||||
.validate()?;
|
||||
Ok(serde_json::from_value(resp["data"].take())?)
|
||||
}
|
||||
|
||||
pub async fn get_followed_uppers(&self, page: i32) -> Result<FollowedUppers> {
|
||||
let mut resp = self
|
||||
.client
|
||||
.request(Method::GET, "https://api.bilibili.com/x/relation/followings")
|
||||
.await
|
||||
.query(&[
|
||||
("vmid", self.mid.as_ref()),
|
||||
("pn", page.to_string().as_ref()),
|
||||
("ps", "20"),
|
||||
])
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json::<serde_json::Value>()
|
||||
.await?
|
||||
.validate()?;
|
||||
Ok(serde_json::from_value(resp["data"].take())?)
|
||||
}
|
||||
|
||||
fn my_id() -> String {
|
||||
CONFIG
|
||||
.credential
|
||||
.load()
|
||||
.as_deref()
|
||||
.map(|c| c.dedeuserid.clone())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct FavoriteItem {
|
||||
pub title: String,
|
||||
pub media_count: i64,
|
||||
pub fid: i64,
|
||||
pub mid: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct CollectionItem {
|
||||
pub id: i64,
|
||||
pub mid: i64,
|
||||
pub state: i32,
|
||||
pub title: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct Collections {
|
||||
pub count: i64,
|
||||
pub list: Option<Vec<CollectionItem>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct FollowedUppers {
|
||||
pub total: i64,
|
||||
pub list: Vec<FollowedUpper>,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
pub struct FollowedUpper {
|
||||
pub mid: i64,
|
||||
pub uname: String,
|
||||
pub face: String,
|
||||
pub sign: String,
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use analyzer::VideoCodecs;
|
||||
pub use analyzer::{BestStream, FilterOption};
|
||||
use anyhow::{Result, bail, ensure};
|
||||
use arc_swap::ArcSwapOption;
|
||||
@@ -26,6 +24,7 @@ mod credential;
|
||||
mod danmaku;
|
||||
mod error;
|
||||
mod favorite_list;
|
||||
mod me;
|
||||
mod submission;
|
||||
mod subtitle;
|
||||
mod video;
|
||||
|
||||
@@ -38,10 +38,12 @@ pub static ARGS: Lazy<Args> = Lazy::new(Args::parse);
|
||||
pub static CONFIG_DIR: Lazy<PathBuf> =
|
||||
Lazy::new(|| dirs::config_dir().expect("No config path found").join("bili-sync"));
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn load_config() -> Config {
|
||||
if cfg!(test) {
|
||||
return Config::load(&CONFIG_DIR.join("test_config.toml")).unwrap_or(Config::test_default());
|
||||
}
|
||||
info!("开始加载配置文件..");
|
||||
let config = Config::load().unwrap_or_else(|err| {
|
||||
let config = Config::load(&CONFIG_DIR.join("config.toml")).unwrap_or_else(|err| {
|
||||
if err
|
||||
.downcast_ref::<std::io::Error>()
|
||||
.is_none_or(|e| e.kind() != std::io::ErrorKind::NotFound)
|
||||
@@ -58,36 +60,3 @@ fn load_config() -> Config {
|
||||
info!("配置文件检查通过");
|
||||
config
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn load_config() -> Config {
|
||||
use crate::bilibili::{FilterOption, VideoCodecs};
|
||||
|
||||
let credential = match (
|
||||
std::env::var("TEST_SESSDATA"),
|
||||
std::env::var("TEST_BILI_JCT"),
|
||||
std::env::var("TEST_BUVID3"),
|
||||
std::env::var("TEST_DEDEUSERID"),
|
||||
std::env::var("TEST_AC_TIME_VALUE"),
|
||||
) {
|
||||
(Ok(sessdata), Ok(bili_jct), Ok(buvid3), Ok(dedeuserid), Ok(ac_time_value)) => {
|
||||
Some(std::sync::Arc::new(crate::bilibili::Credential {
|
||||
sessdata,
|
||||
bili_jct,
|
||||
buvid3,
|
||||
dedeuserid,
|
||||
ac_time_value,
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
Config {
|
||||
credential: arc_swap::ArcSwapOption::from(credential),
|
||||
cdn_sorting: true,
|
||||
filter_option: FilterOption {
|
||||
codecs: vec![VideoCodecs::HEV, VideoCodecs::AV1, VideoCodecs::AVC],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
@@ -105,10 +105,8 @@ impl Config {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn load() -> Result<Self> {
|
||||
let config_path = CONFIG_DIR.join("config.toml");
|
||||
let config_content = std::fs::read_to_string(config_path)?;
|
||||
fn load(path: &Path) -> Result<Self> {
|
||||
let config_content = std::fs::read_to_string(path)?;
|
||||
Ok(toml::from_str(&config_content)?)
|
||||
}
|
||||
|
||||
@@ -129,7 +127,6 @@ impl Config {
|
||||
params
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
pub fn check(&self) {
|
||||
let mut ok = true;
|
||||
let video_sources = self.as_video_sources();
|
||||
@@ -184,4 +181,11 @@ impl Config {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn test_default() -> Self {
|
||||
Self {
|
||||
cdn_sorting: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use tokio::io::{AsyncWriteExt, BufWriter};
|
||||
|
||||
use crate::config::{CONFIG, NFOTimeType};
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum NFO<'a> {
|
||||
Movie(Movie<'a>),
|
||||
TVShow(TVShow<'a>),
|
||||
|
||||
Reference in New Issue
Block a user