fix: 修复本地测试发现的若干问题 (#392)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-07-12 15:17:54 +08:00
committed by GitHub
parent c8f7a2267d
commit 87fb597ba4
14 changed files with 73 additions and 129 deletions
Generated
+1
View File
@@ -482,6 +482,7 @@ dependencies = [
"assert_matches", "assert_matches",
"async-stream", "async-stream",
"axum", "axum",
"base64",
"bili_sync_entity", "bili_sync_entity",
"bili_sync_migration", "bili_sync_migration",
"built", "built",
+1
View File
@@ -22,6 +22,7 @@ async-std = { version = "1.13.1", features = ["attributes", "tokio1"] }
async-stream = "0.3.6" async-stream = "0.3.6"
async-trait = "0.1.88" async-trait = "0.1.88"
axum = { version = "0.8.4", features = ["macros", "ws"] } axum = { version = "0.8.4", features = ["macros", "ws"] }
base64 = "0.22.1"
built = { version = "0.7.7", features = ["git2", "chrono"] } built = { version = "0.7.7", features = ["git2", "chrono"] }
chrono = { version = "0.4.41", features = ["serde"] } chrono = { version = "0.4.41", features = ["serde"] }
clap = { version = "4.5.41", features = ["env", "string"] } clap = { version = "4.5.41", features = ["env", "string"] }
+1
View File
@@ -14,6 +14,7 @@ anyhow = { workspace = true }
arc-swap = { workspace = true } arc-swap = { workspace = true }
async-stream = { workspace = true } async-stream = { workspace = true }
axum = { workspace = true } axum = { workspace = true }
base64 = { workspace = true }
bili_sync_entity = { workspace = true } bili_sync_entity = { workspace = true }
bili_sync_migration = { workspace = true } bili_sync_migration = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
+5 -33
View File
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::pin::Pin; use std::pin::Pin;
@@ -14,6 +15,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, Collection, CollectionItem, CollectionType, VideoInfo}; use crate::bilibili::{BiliClient, Collection, CollectionItem, CollectionType, VideoInfo};
impl VideoSource for collection::Model { impl VideoSource for collection::Model {
fn display_name(&self) -> Cow<'static, str> {
format!("{}{}", CollectionType::from(self.r#type), self.name).into()
}
fn filter_expr(&self) -> SimpleExpr { fn filter_expr(&self) -> SimpleExpr {
video::Column::CollectionId.eq(self.id) video::Column::CollectionId.eq(self.id)
} }
@@ -58,39 +63,6 @@ impl VideoSource for collection::Model {
None None
} }
fn log_refresh_video_start(&self) {
info!("开始扫描{}「{}」..", CollectionType::from(self.r#type), self.name);
}
fn log_refresh_video_end(&self, count: usize) {
info!(
"扫描{}「{}」完成,获取到 {} 条新视频",
CollectionType::from(self.r#type),
self.name,
count,
);
}
fn log_fetch_video_start(&self) {
info!(
"开始填充{}「{}」视频详情..",
CollectionType::from(self.r#type),
self.name
);
}
fn log_fetch_video_end(&self) {
info!("填充{}「{}」视频详情完成", CollectionType::from(self.r#type), self.name);
}
fn log_download_video_start(&self) {
info!("开始下载{}「{}」视频..", CollectionType::from(self.r#type), self.name);
}
fn log_download_video_end(&self) {
info!("下载{}「{}」视频完成", CollectionType::from(self.r#type), self.name);
}
async fn refresh<'a>( async fn refresh<'a>(
self, self,
bili_client: &'a BiliClient, bili_client: &'a BiliClient,
+5 -24
View File
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::pin::Pin; use std::pin::Pin;
@@ -13,6 +14,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, FavoriteList, VideoInfo}; use crate::bilibili::{BiliClient, FavoriteList, VideoInfo};
impl VideoSource for favorite::Model { impl VideoSource for favorite::Model {
fn display_name(&self) -> Cow<'static, str> {
format!("收藏夹「{}", self.name).into()
}
fn filter_expr(&self) -> SimpleExpr { fn filter_expr(&self) -> SimpleExpr {
video::Column::FavoriteId.eq(self.id) video::Column::FavoriteId.eq(self.id)
} }
@@ -37,30 +42,6 @@ impl VideoSource for favorite::Model {
}) })
} }
fn log_refresh_video_start(&self) {
info!("开始扫描收藏夹「{}」..", self.name);
}
fn log_refresh_video_end(&self, count: usize) {
info!("扫描收藏夹「{}」完成,获取到 {} 条新视频", self.name, count);
}
fn log_fetch_video_start(&self) {
info!("开始填充收藏夹「{}」视频详情..", self.name);
}
fn log_fetch_video_end(&self) {
info!("填充收藏夹「{}」视频详情完成", self.name);
}
fn log_download_video_start(&self) {
info!("开始下载收藏夹「{}」视频..", self.name);
}
fn log_download_video_end(&self) {
info!("下载收藏夹「{}」视频完成", self.name);
}
async fn refresh<'a>( async fn refresh<'a>(
self, self,
bili_client: &'a BiliClient, bili_client: &'a BiliClient,
+30 -14
View File
@@ -3,6 +3,7 @@ mod favorite;
mod submission; mod submission;
mod watch_later; mod watch_later;
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::pin::Pin; use std::pin::Pin;
@@ -10,6 +11,7 @@ use anyhow::Result;
use chrono::Utc; use chrono::Utc;
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use futures::Stream; use futures::Stream;
use sea_orm::ActiveValue::Set;
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
use sea_orm::sea_query::SimpleExpr; use sea_orm::sea_query::SimpleExpr;
@@ -32,6 +34,9 @@ pub enum VideoSourceEnum {
#[enum_dispatch(VideoSourceEnum)] #[enum_dispatch(VideoSourceEnum)]
pub trait VideoSource { pub trait VideoSource {
/// 获取视频源的名称
fn display_name(&self) -> Cow<'static, str>;
/// 获取特定视频列表的筛选条件 /// 获取特定视频列表的筛选条件
fn filter_expr(&self) -> SimpleExpr; fn filter_expr(&self) -> SimpleExpr;
@@ -63,23 +68,29 @@ pub trait VideoSource {
video_info.ok() video_info.ok()
} }
/// 开始刷新视频 fn log_refresh_video_start(&self) {
fn log_refresh_video_start(&self); info!("开始扫描{}..", self.display_name());
}
/// 结束刷新视频 fn log_refresh_video_end(&self, count: usize) {
fn log_refresh_video_end(&self, count: usize); info!("扫描{}完成,获取到 {} 条新视频", self.display_name(), count);
}
/// 开始填充视频 fn log_fetch_video_start(&self) {
fn log_fetch_video_start(&self); info!("开始填充{}视频详情..", self.display_name());
}
/// 结束填充视频 fn log_fetch_video_end(&self) {
fn log_fetch_video_end(&self); info!("填充{}视频详情完成", self.display_name());
}
/// 开始下载视频 fn log_download_video_start(&self) {
fn log_download_video_start(&self); info!("开始下载{}视频..", self.display_name());
}
/// 结束下载视频 fn log_download_video_end(&self) {
fn log_download_video_end(&self); info!("下载{}视频完成", self.display_name());
}
async fn refresh<'a>( async fn refresh<'a>(
self, self,
@@ -110,8 +121,13 @@ impl _ActiveModel {
_ActiveModel::Submission(model) => { _ActiveModel::Submission(model) => {
model.save(connection).await?; model.save(connection).await?;
} }
_ActiveModel::WatchLater(model) => { _ActiveModel::WatchLater(mut model) => {
model.save(connection).await?; if model.id.is_not_set() {
model.id = Set(1);
model.insert(connection).await?;
} else {
model.save(connection).await?;
}
} }
} }
Ok(()) Ok(())
+4 -24
View File
@@ -13,6 +13,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, Submission, VideoInfo}; use crate::bilibili::{BiliClient, Submission, VideoInfo};
impl VideoSource for submission::Model { impl VideoSource for submission::Model {
fn display_name(&self) -> std::borrow::Cow<'static, str> {
format!("{}」投稿", self.upper_name).into()
}
fn filter_expr(&self) -> SimpleExpr { fn filter_expr(&self) -> SimpleExpr {
video::Column::SubmissionId.eq(self.id) video::Column::SubmissionId.eq(self.id)
} }
@@ -37,30 +41,6 @@ impl VideoSource for submission::Model {
}) })
} }
fn log_refresh_video_start(&self) {
info!("开始扫描「{}」投稿..", self.upper_name);
}
fn log_refresh_video_end(&self, count: usize) {
info!("扫描「{}」投稿完成,获取到 {} 条新视频", self.upper_name, count,);
}
fn log_fetch_video_start(&self) {
info!("开始填充「{}」投稿视频详情..", self.upper_name);
}
fn log_fetch_video_end(&self) {
info!("填充「{}」投稿视频详情完成", self.upper_name);
}
fn log_download_video_start(&self) {
info!("开始下载「{}」投稿视频..", self.upper_name);
}
fn log_download_video_end(&self) {
info!("下载「{}」投稿视频完成", self.upper_name);
}
async fn refresh<'a>( async fn refresh<'a>(
self, self,
bili_client: &'a BiliClient, bili_client: &'a BiliClient,
+4 -24
View File
@@ -13,6 +13,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, VideoInfo, WatchLater}; use crate::bilibili::{BiliClient, VideoInfo, WatchLater};
impl VideoSource for watch_later::Model { impl VideoSource for watch_later::Model {
fn display_name(&self) -> std::borrow::Cow<'static, str> {
"稍后再看".into()
}
fn filter_expr(&self) -> SimpleExpr { fn filter_expr(&self) -> SimpleExpr {
video::Column::WatchLaterId.eq(self.id) video::Column::WatchLaterId.eq(self.id)
} }
@@ -37,30 +41,6 @@ impl VideoSource for watch_later::Model {
}) })
} }
fn log_refresh_video_start(&self) {
info!("开始扫描稍后再看..");
}
fn log_refresh_video_end(&self, count: usize) {
info!("扫描稍后再看完成,获取到 {} 条新视频", count);
}
fn log_fetch_video_start(&self) {
info!("开始填充稍后再看视频详情..");
}
fn log_fetch_video_end(&self) {
info!("填充稍后再看视频详情完成");
}
fn log_download_video_start(&self) {
info!("开始下载稍后再看视频..");
}
fn log_download_video_end(&self) {
info!("下载稍后再看视频完成");
}
async fn refresh<'a>( async fn refresh<'a>(
self, self,
bili_client: &'a BiliClient, bili_client: &'a BiliClient,
+7 -2
View File
@@ -8,6 +8,8 @@ use axum::middleware::Next;
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use axum::routing::get; use axum::routing::get;
use axum::{Router, middleware}; use axum::{Router, middleware};
use base64::Engine;
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
use reqwest::{Method, StatusCode, header}; use reqwest::{Method, StatusCode, header};
use super::request::ImageProxyParams; use super::request::ImageProxyParams;
@@ -43,10 +45,13 @@ pub async fn auth(headers: HeaderMap, request: Request, next: Next) -> Result<Re
let token = config.auth_token.as_str(); let token = config.auth_token.as_str();
if headers if headers
.get("Authorization") .get("Authorization")
.is_some_and(|v| v.to_str().is_ok_and(|s| s == token)) .and_then(|v| v.to_str().ok())
.is_some_and(|s| s == token)
|| headers || headers
.get("Sec-WebSocket-Protocol") .get("Sec-WebSocket-Protocol")
.is_some_and(|v| v.to_str().is_ok_and(|s| s == token)) .and_then(|v| v.to_str().ok())
.and_then(|s| BASE64_URL_SAFE_NO_PAD.decode(s).ok())
.is_some_and(|s| s == token.as_bytes())
{ {
return Ok(next.run(request).await); return Ok(next.run(request).await);
} }
@@ -168,7 +168,6 @@ pub async fn update_video_source(
} else { } else {
// 如果没有记录且 id 为 1,插入一个新的稍后再看记录 // 如果没有记录且 id 为 1,插入一个新的稍后再看记录
Some(_ActiveModel::WatchLater(watch_later::ActiveModel { Some(_ActiveModel::WatchLater(watch_later::ActiveModel {
id: Set(1),
path: Set(request.path), path: Set(request.path),
enabled: Set(request.enabled), enabled: Set(request.enabled),
..Default::default() ..Default::default()
+4 -4
View File
@@ -1,6 +1,4 @@
#![allow(unused)] use anyhow::{Result, ensure};
use anyhow::Result;
use reqwest::Method; use reqwest::Method;
use crate::bilibili::{BiliClient, Validate}; use crate::bilibili::{BiliClient, Validate};
@@ -19,6 +17,7 @@ impl<'a> Me<'a> {
} }
pub async fn get_created_favorites(&self) -> Result<Option<Vec<FavoriteItem>>> { pub async fn get_created_favorites(&self) -> Result<Option<Vec<FavoriteItem>>> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID,请确保填写设置中的 B 站认证信息");
let mut resp = self let mut resp = self
.client .client
.request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/created/list-all") .request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/created/list-all")
@@ -34,6 +33,7 @@ impl<'a> Me<'a> {
} }
pub async fn get_followed_collections(&self, page_num: i32, page_size: i32) -> Result<Collections> { pub async fn get_followed_collections(&self, page_num: i32, page_size: i32) -> Result<Collections> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID,请确保填写设置中的 B 站认证信息");
let mut resp = self let mut resp = self
.client .client
.request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/collected/list") .request(Method::GET, "https://api.bilibili.com/x/v3/fav/folder/collected/list")
@@ -54,6 +54,7 @@ impl<'a> Me<'a> {
} }
pub async fn get_followed_uppers(&self, page_num: i32, page_size: i32) -> Result<FollowedUppers> { pub async fn get_followed_uppers(&self, page_num: i32, page_size: i32) -> Result<FollowedUppers> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID,请确保填写设置中的 B 站认证信息");
let mut resp = self let mut resp = self
.client .client
.request(Method::GET, "https://api.bilibili.com/x/relation/followings") .request(Method::GET, "https://api.bilibili.com/x/relation/followings")
@@ -82,7 +83,6 @@ pub struct FavoriteItem {
pub title: String, pub title: String,
pub media_count: i64, pub media_count: i64,
pub id: i64, pub id: i64,
pub fid: i64,
pub mid: i64, pub mid: i64,
} }
+3
View File
@@ -26,6 +26,9 @@ async fn migrate_database() -> Result<()> {
/// 进行数据库迁移并获取数据库连接,供外部使用 /// 进行数据库迁移并获取数据库连接,供外部使用
pub async fn setup_database() -> Result<DatabaseConnection> { pub async fn setup_database() -> Result<DatabaseConnection> {
tokio::fs::create_dir_all(CONFIG_DIR.as_path())
.await
.context("Failed to create config directory")?;
migrate_database().await.context("Failed to migrate database")?; migrate_database().await.context("Failed to migrate database")?;
database_connection().await.context("Failed to connect to database") database_connection().await.context("Failed to connect to database")
} }
@@ -3,6 +3,7 @@ use std::sync::Arc;
use sea_orm::DatabaseConnection; use sea_orm::DatabaseConnection;
use tokio::time; use tokio::time;
use crate::adapter::VideoSource;
use crate::bilibili::{self, BiliClient}; use crate::bilibili::{self, BiliClient};
use crate::config::VersionedConfig; use crate::config::VersionedConfig;
use crate::utils::model::get_enabled_video_sources; use crate::utils::model::get_enabled_video_sources;
@@ -48,8 +49,9 @@ pub async fn video_downloader(connection: Arc<DatabaseConnection>, bili_client:
break 'inner; break 'inner;
} }
for video_source in video_sources { for video_source in video_sources {
let display_name = video_source.display_name();
if let Err(e) = process_video_source(video_source, &bili_client, &connection).await { if let Err(e) = process_video_source(video_source, &bili_client, &connection).await {
error!("处理 {} 时遇到错误:{:#},等待下一轮执行", "test", e); error!("处理 {} 时遇到错误:{:#},等待下一轮执行", display_name, e);
} }
} }
info!("本轮任务执行完毕,等待下一轮执行"); info!("本轮任务执行完毕,等待下一轮执行");
+5 -2
View File
@@ -65,8 +65,11 @@ export class WebSocketManager {
try { try {
const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
this.socket = new WebSocket(`${protocol}${window.location.host}/api/ws`, [token]); // 使用 base64URL no padding 编码 token 以避免特殊字符问题
this.socket = new WebSocket(
`${protocol}${window.location.host}/api/ws`,
btoa(token).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
);
this.socket.onopen = () => { this.socket.onopen = () => {
this.connected = true; this.connected = true;
this.connecting = false; this.connecting = false;