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

1
Cargo.lock generated
View File

@@ -482,6 +482,7 @@ dependencies = [
"assert_matches",
"async-stream",
"axum",
"base64",
"bili_sync_entity",
"bili_sync_migration",
"built",

View File

@@ -22,6 +22,7 @@ async-std = { version = "1.13.1", features = ["attributes", "tokio1"] }
async-stream = "0.3.6"
async-trait = "0.1.88"
axum = { version = "0.8.4", features = ["macros", "ws"] }
base64 = "0.22.1"
built = { version = "0.7.7", features = ["git2", "chrono"] }
chrono = { version = "0.4.41", features = ["serde"] }
clap = { version = "4.5.41", features = ["env", "string"] }

View File

@@ -14,6 +14,7 @@ anyhow = { workspace = true }
arc-swap = { workspace = true }
async-stream = { workspace = true }
axum = { workspace = true }
base64 = { workspace = true }
bili_sync_entity = { workspace = true }
bili_sync_migration = { workspace = true }
chrono = { workspace = true }

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::path::Path;
use std::pin::Pin;
@@ -14,6 +15,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, Collection, CollectionItem, CollectionType, VideoInfo};
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 {
video::Column::CollectionId.eq(self.id)
}
@@ -58,39 +63,6 @@ impl VideoSource for collection::Model {
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>(
self,
bili_client: &'a BiliClient,

View File

@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::path::Path;
use std::pin::Pin;
@@ -13,6 +14,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, FavoriteList, VideoInfo};
impl VideoSource for favorite::Model {
fn display_name(&self) -> Cow<'static, str> {
format!("收藏夹「{}", self.name).into()
}
fn filter_expr(&self) -> SimpleExpr {
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>(
self,
bili_client: &'a BiliClient,

View File

@@ -3,6 +3,7 @@ mod favorite;
mod submission;
mod watch_later;
use std::borrow::Cow;
use std::path::Path;
use std::pin::Pin;
@@ -10,6 +11,7 @@ use anyhow::Result;
use chrono::Utc;
use enum_dispatch::enum_dispatch;
use futures::Stream;
use sea_orm::ActiveValue::Set;
use sea_orm::DatabaseConnection;
use sea_orm::entity::prelude::*;
use sea_orm::sea_query::SimpleExpr;
@@ -32,6 +34,9 @@ pub enum VideoSourceEnum {
#[enum_dispatch(VideoSourceEnum)]
pub trait VideoSource {
/// 获取视频源的名称
fn display_name(&self) -> Cow<'static, str>;
/// 获取特定视频列表的筛选条件
fn filter_expr(&self) -> SimpleExpr;
@@ -63,23 +68,29 @@ pub trait VideoSource {
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>(
self,
@@ -110,8 +121,13 @@ impl _ActiveModel {
_ActiveModel::Submission(model) => {
model.save(connection).await?;
}
_ActiveModel::WatchLater(model) => {
model.save(connection).await?;
_ActiveModel::WatchLater(mut model) => {
if model.id.is_not_set() {
model.id = Set(1);
model.insert(connection).await?;
} else {
model.save(connection).await?;
}
}
}
Ok(())

View File

@@ -13,6 +13,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, Submission, VideoInfo};
impl VideoSource for submission::Model {
fn display_name(&self) -> std::borrow::Cow<'static, str> {
format!("{}」投稿", self.upper_name).into()
}
fn filter_expr(&self) -> SimpleExpr {
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>(
self,
bili_client: &'a BiliClient,

View File

@@ -13,6 +13,10 @@ use crate::adapter::{_ActiveModel, VideoSource, VideoSourceEnum};
use crate::bilibili::{BiliClient, VideoInfo, WatchLater};
impl VideoSource for watch_later::Model {
fn display_name(&self) -> std::borrow::Cow<'static, str> {
"稍后再看".into()
}
fn filter_expr(&self) -> SimpleExpr {
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>(
self,
bili_client: &'a BiliClient,

View File

@@ -8,6 +8,8 @@ use axum::middleware::Next;
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use axum::{Router, middleware};
use base64::Engine;
use base64::prelude::BASE64_URL_SAFE_NO_PAD;
use reqwest::{Method, StatusCode, header};
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();
if headers
.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
.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);
}

View File

@@ -168,7 +168,6 @@ pub async fn update_video_source(
} else {
// 如果没有记录且 id 为 1插入一个新的稍后再看记录
Some(_ActiveModel::WatchLater(watch_later::ActiveModel {
id: Set(1),
path: Set(request.path),
enabled: Set(request.enabled),
..Default::default()

View File

@@ -1,6 +1,4 @@
#![allow(unused)]
use anyhow::Result;
use anyhow::{Result, ensure};
use reqwest::Method;
use crate::bilibili::{BiliClient, Validate};
@@ -19,6 +17,7 @@ impl<'a> Me<'a> {
}
pub async fn get_created_favorites(&self) -> Result<Option<Vec<FavoriteItem>>> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID请确保填写设置中的 B 站认证信息");
let mut resp = self
.client
.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> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID请确保填写设置中的 B 站认证信息");
let mut resp = self
.client
.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> {
ensure!(!self.mid.is_empty(), "未获取到用户 ID请确保填写设置中的 B 站认证信息");
let mut resp = self
.client
.request(Method::GET, "https://api.bilibili.com/x/relation/followings")
@@ -82,7 +83,6 @@ pub struct FavoriteItem {
pub title: String,
pub media_count: i64,
pub id: i64,
pub fid: i64,
pub mid: i64,
}

View File

@@ -26,6 +26,9 @@ async fn migrate_database() -> Result<()> {
/// 进行数据库迁移并获取数据库连接,供外部使用
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")?;
database_connection().await.context("Failed to connect to database")
}

View File

@@ -3,6 +3,7 @@ use std::sync::Arc;
use sea_orm::DatabaseConnection;
use tokio::time;
use crate::adapter::VideoSource;
use crate::bilibili::{self, BiliClient};
use crate::config::VersionedConfig;
use crate::utils::model::get_enabled_video_sources;
@@ -48,8 +49,9 @@ pub async fn video_downloader(connection: Arc<DatabaseConnection>, bili_client:
break 'inner;
}
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 {
error!("处理 {} 时遇到错误:{:#},等待下一轮执行", "test", e);
error!("处理 {} 时遇到错误:{:#},等待下一轮执行", display_name, e);
}
}
info!("本轮任务执行完毕,等待下一轮执行");

View File

@@ -65,8 +65,11 @@ export class WebSocketManager {
try {
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.connected = true;
this.connecting = false;