refactor: 引入 enum_dispatch 静态分发,提升性能 (#232)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-01-24 13:44:27 +08:00
committed by GitHub
parent 9e5a8b0573
commit 40cf22a7fa
10 changed files with 79 additions and 67 deletions

13
Cargo.lock generated
View File

@@ -392,6 +392,7 @@ dependencies = [
"cookie",
"cow-utils",
"dirs",
"enum_dispatch",
"float-ord",
"futures",
"handlebars",
@@ -889,6 +890,18 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "enum_dispatch"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.96",
]
[[package]]
name = "equivalent"
version = "1.0.1"

View File

@@ -26,6 +26,7 @@ clap = { version = "4.5.26", features = ["env"] }
cookie = "0.18.1"
cow-utils = "0.1.3"
dirs = "6.0.0"
enum_dispatch = "0.3.13"
float-ord = "0.3.2"
futures = "0.3.31"
handlebars = "6.3.0"

View File

@@ -19,6 +19,7 @@ clap = { workspace = true }
cookie = { workspace = true }
cow-utils = { workspace = true }
dirs = { workspace = true }
enum_dispatch = { workspace = true }
float-ord = { workspace = true }
futures = { workspace = true }
handlebars = { workspace = true }

View File

@@ -9,7 +9,7 @@ use sea_orm::sea_query::{OnConflict, SimpleExpr};
use sea_orm::ActiveValue::Set;
use sea_orm::{DatabaseConnection, Unchanged};
use crate::adapter::{VideoListModel, _ActiveModel};
use crate::adapter::{VideoListModel, VideoListModelEnum, _ActiveModel};
use crate::bilibili::{BiliClient, Collection, CollectionItem, CollectionType, VideoInfo};
impl VideoListModel for collection::Model {
@@ -98,10 +98,7 @@ pub(super) async fn collection_from<'a>(
path: &Path,
bili_client: &'a BiliClient,
connection: &DatabaseConnection,
) -> Result<(
Box<dyn VideoListModel>,
Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
)> {
) -> Result<(VideoListModelEnum, Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>)> {
let collection = Collection::new(bili_client, collection_item);
let collection_info = collection.get_info().await?;
collection::Entity::insert(collection::ActiveModel {
@@ -124,18 +121,17 @@ pub(super) async fn collection_from<'a>(
.exec(connection)
.await?;
Ok((
Box::new(
collection::Entity::find()
.filter(
collection::Column::SId
.eq(collection_item.sid.clone())
.and(collection::Column::MId.eq(collection_item.mid.clone()))
.and(collection::Column::Type.eq(Into::<i32>::into(collection_item.collection_type.clone()))),
)
.one(connection)
.await?
.context("collection not found")?,
),
collection::Entity::find()
.filter(
collection::Column::SId
.eq(collection_item.sid.clone())
.and(collection::Column::MId.eq(collection_item.mid.clone()))
.and(collection::Column::Type.eq(Into::<i32>::into(collection_item.collection_type.clone()))),
)
.one(connection)
.await?
.context("collection not found")?
.into(),
Box::pin(collection.into_video_stream()),
))
}

View File

@@ -9,7 +9,7 @@ use sea_orm::sea_query::{OnConflict, SimpleExpr};
use sea_orm::ActiveValue::Set;
use sea_orm::{DatabaseConnection, Unchanged};
use crate::adapter::{VideoListModel, _ActiveModel};
use crate::adapter::{VideoListModel, VideoListModelEnum, _ActiveModel};
use crate::bilibili::{BiliClient, FavoriteList, VideoInfo};
impl VideoListModel for favorite::Model {
@@ -70,10 +70,7 @@ pub(super) async fn favorite_from<'a>(
path: &Path,
bili_client: &'a BiliClient,
connection: &DatabaseConnection,
) -> Result<(
Box<dyn VideoListModel>,
Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
)> {
) -> Result<(VideoListModelEnum, Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>)> {
let favorite = FavoriteList::new(bili_client, fid.to_owned());
let favorite_info = favorite.get_info().await?;
favorite::Entity::insert(favorite::ActiveModel {
@@ -90,13 +87,12 @@ pub(super) async fn favorite_from<'a>(
.exec(connection)
.await?;
Ok((
Box::new(
favorite::Entity::find()
.filter(favorite::Column::FId.eq(favorite_info.id))
.one(connection)
.await?
.context("favorite not found")?,
),
favorite::Entity::find()
.filter(favorite::Column::FId.eq(favorite_info.id))
.one(connection)
.await?
.context("favorite not found")?
.into(),
Box::pin(favorite.into_video_stream()),
))
}

View File

@@ -7,17 +7,33 @@ use std::path::Path;
use std::pin::Pin;
use anyhow::Result;
use enum_dispatch::enum_dispatch;
use futures::Stream;
use sea_orm::entity::prelude::*;
use sea_orm::sea_query::SimpleExpr;
use sea_orm::DatabaseConnection;
#[rustfmt::skip]
use bili_sync_entity::collection::Model as Collection;
use bili_sync_entity::favorite::Model as Favorite;
use bili_sync_entity::submission::Model as Submission;
use bili_sync_entity::watch_later::Model as WatchLater;
use crate::adapter::collection::collection_from;
use crate::adapter::favorite::favorite_from;
use crate::adapter::submission::submission_from;
use crate::adapter::watch_later::watch_later_from;
use crate::bilibili::{BiliClient, CollectionItem, VideoInfo};
#[enum_dispatch]
pub enum VideoListModelEnum {
Favorite,
Collection,
Submission,
WatchLater,
}
#[enum_dispatch(VideoListModelEnum)]
pub trait VideoListModel {
/// 获取特定视频列表的筛选条件
fn filter_expr(&self) -> SimpleExpr;
@@ -67,10 +83,7 @@ pub async fn video_list_from<'a>(
path: &Path,
bili_client: &'a BiliClient,
connection: &DatabaseConnection,
) -> Result<(
Box<dyn VideoListModel>,
Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
)> {
) -> Result<(VideoListModelEnum, Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>)> {
match args {
Args::Favorite { fid } => favorite_from(fid, path, bili_client, connection).await,
Args::Collection { collection_item } => collection_from(collection_item, path, bili_client, connection).await,

View File

@@ -9,7 +9,7 @@ use sea_orm::sea_query::{OnConflict, SimpleExpr};
use sea_orm::ActiveValue::Set;
use sea_orm::{DatabaseConnection, Unchanged};
use crate::adapter::{VideoListModel, _ActiveModel};
use crate::adapter::{VideoListModel, VideoListModelEnum, _ActiveModel};
use crate::bilibili::{BiliClient, Submission, VideoInfo};
impl VideoListModel for submission::Model {
@@ -82,10 +82,7 @@ pub(super) async fn submission_from<'a>(
path: &Path,
bili_client: &'a BiliClient,
connection: &DatabaseConnection,
) -> Result<(
Box<dyn VideoListModel>,
Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
)> {
) -> Result<(VideoListModelEnum, Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>)> {
let submission = Submission::new(bili_client, upper_id.to_owned());
let upper = submission.get_info().await?;
submission::Entity::insert(submission::ActiveModel {
@@ -102,13 +99,12 @@ pub(super) async fn submission_from<'a>(
.exec(connection)
.await?;
Ok((
Box::new(
submission::Entity::find()
.filter(submission::Column::UpperId.eq(upper.mid))
.one(connection)
.await?
.context("submission not found")?,
),
submission::Entity::find()
.filter(submission::Column::UpperId.eq(upper.mid))
.one(connection)
.await?
.context("submission not found")?
.into(),
Box::pin(submission.into_video_stream()),
))
}

View File

@@ -9,7 +9,7 @@ use sea_orm::sea_query::{OnConflict, SimpleExpr};
use sea_orm::ActiveValue::Set;
use sea_orm::{DatabaseConnection, Unchanged};
use crate::adapter::{VideoListModel, _ActiveModel};
use crate::adapter::{VideoListModel, VideoListModelEnum, _ActiveModel};
use crate::bilibili::{BiliClient, VideoInfo, WatchLater};
impl VideoListModel for watch_later::Model {
@@ -66,10 +66,7 @@ pub(super) async fn watch_later_from<'a>(
path: &Path,
bili_client: &'a BiliClient,
connection: &DatabaseConnection,
) -> Result<(
Box<dyn VideoListModel>,
Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
)> {
) -> Result<(VideoListModelEnum, Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>)> {
let watch_later = WatchLater::new(bili_client);
watch_later::Entity::insert(watch_later::ActiveModel {
id: Set(1),
@@ -84,13 +81,12 @@ pub(super) async fn watch_later_from<'a>(
.exec(connection)
.await?;
Ok((
Box::new(
watch_later::Entity::find()
.filter(watch_later::Column::Id.eq(1))
.one(connection)
.await?
.context("watch_later not found")?,
),
watch_later::Entity::find()
.filter(watch_later::Column::Id.eq(1))
.one(connection)
.await?
.context("watch_later not found")?
.into(),
Box::pin(watch_later.into_video_stream()),
))
}

View File

@@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
use sea_orm::sea_query::{OnConflict, SimpleExpr};
use sea_orm::DatabaseTransaction;
use crate::adapter::VideoListModel;
use crate::adapter::{VideoListModel, VideoListModelEnum};
use crate::bilibili::{PageInfo, VideoInfo};
use crate::utils::status::STATUS_COMPLETED;
@@ -50,7 +50,7 @@ pub async fn filter_unhandled_video_pages(
/// 尝试创建 Video Model如果发生冲突则忽略
pub async fn create_videos(
videos_info: Vec<VideoInfo>,
video_list_model: &dyn VideoListModel,
video_list_model: &VideoListModelEnum,
connection: &DatabaseConnection,
) -> Result<()> {
let video_models = videos_info

View File

@@ -12,7 +12,7 @@ use sea_orm::TransactionTrait;
use tokio::fs;
use tokio::sync::{Mutex, Semaphore};
use crate::adapter::{video_list_from, Args, VideoListModel};
use crate::adapter::{video_list_from, Args, VideoListModel, VideoListModelEnum};
use crate::bilibili::{BestStream, BiliClient, BiliError, Dimension, PageInfo, Video, VideoInfo};
use crate::config::{PathSafeTemplate, ARGS, CONFIG, TEMPLATE};
use crate::downloader::Downloader;
@@ -35,21 +35,21 @@ pub async fn process_video_list(
// 从参数中获取视频列表的 Model 与视频流
let (video_list_model, video_streams) = video_list_from(args, path, bili_client, connection).await?;
// 从视频流中获取新视频的简要信息,写入数据库
refresh_video_list(video_list_model.as_ref(), video_streams, connection).await?;
refresh_video_list(&video_list_model, video_streams, connection).await?;
// 单独请求视频详情接口,获取视频的详情信息与所有的分页,写入数据库
fetch_video_details(bili_client, video_list_model.as_ref(), connection).await?;
fetch_video_details(bili_client, &video_list_model, connection).await?;
if ARGS.scan_only {
warn!("已开启仅扫描模式,跳过视频下载...");
} else {
// 从数据库中查找所有未下载的视频与分页,下载并处理
download_unprocessed_videos(bili_client, video_list_model.as_ref(), connection).await?;
download_unprocessed_videos(bili_client, &video_list_model, connection).await?;
}
Ok(())
}
/// 请求接口,获取视频列表中所有新添加的视频信息,将其写入数据库
pub async fn refresh_video_list<'a>(
video_list_model: &dyn VideoListModel,
video_list_model: &VideoListModelEnum,
video_streams: Pin<Box<dyn Stream<Item = Result<VideoInfo>> + 'a>>,
connection: &DatabaseConnection,
) -> Result<()> {
@@ -98,7 +98,7 @@ pub async fn refresh_video_list<'a>(
/// 筛选出所有未获取到全部信息的视频,尝试补充其详细信息
pub async fn fetch_video_details(
bili_client: &BiliClient,
video_list_model: &dyn VideoListModel,
video_list_model: &VideoListModelEnum,
connection: &DatabaseConnection,
) -> Result<()> {
video_list_model.log_fetch_video_start();
@@ -143,7 +143,7 @@ pub async fn fetch_video_details(
/// 下载所有未处理成功的视频
pub async fn download_unprocessed_videos(
bili_client: &BiliClient,
video_list_model: &dyn VideoListModel,
video_list_model: &VideoListModelEnum,
connection: &DatabaseConnection,
) -> Result<()> {
video_list_model.log_download_video_start();
@@ -201,7 +201,7 @@ pub async fn download_unprocessed_videos(
#[allow(clippy::too_many_arguments)]
pub async fn download_video_pages(
bili_client: &BiliClient,
video_list_model: &dyn VideoListModel,
video_list_model: &VideoListModelEnum,
video_model: video::Model,
pages: Vec<page::Model>,
connection: &DatabaseConnection,