feat: 添加部分简单 API,相应修改程序入口的初始化流程 (#251)

This commit is contained in:
ᴀᴍᴛᴏᴀᴇʀ
2025-02-17 16:58:51 +08:00
committed by GitHub
parent 7251802202
commit 1467c262a1
20 changed files with 648 additions and 133 deletions

View File

@@ -0,0 +1,51 @@
use std::sync::Arc;
use anyhow::{Context, Result};
use axum::extract::Request;
use axum::http::{header, Uri};
use axum::response::IntoResponse;
use axum::routing::get;
use axum::{middleware, Extension, Router, ServiceExt};
use reqwest::StatusCode;
use rust_embed::Embed;
use sea_orm::DatabaseConnection;
use tower::Layer;
use tower_http::normalize_path::NormalizePathLayer;
use crate::api::auth;
use crate::api::handler::{get_video, get_video_list_models, list_videos};
use crate::config::CONFIG;
#[derive(Embed)]
#[folder = "../../web/build"]
struct Asset;
pub async fn http_server(database_connection: Arc<DatabaseConnection>) -> Result<()> {
let app = Router::new()
.route("/api/videos", get(list_videos))
.route("/api/videos/{video_id}", get(get_video))
.route("/api/video-list-models", get(get_video_list_models))
.fallback_service(get(frontend_files))
.layer(Extension(database_connection))
.layer(middleware::from_fn(auth::auth));
let app = NormalizePathLayer::trim_trailing_slash().layer(app);
let listener = tokio::net::TcpListener::bind(&CONFIG.bind_address)
.await
.context("bind address failed")?;
info!("开始监听 http 服务: http://{}", CONFIG.bind_address);
Ok(axum::serve(listener, ServiceExt::<Request>::into_make_service(app)).await?)
}
async fn frontend_files(uri: Uri) -> impl IntoResponse {
let mut path = uri.path().trim_start_matches('/');
if path.is_empty() {
path = "index.html";
}
match Asset::get(path) {
Some(content) => {
let mime = mime_guess::from_path(path).first_or_octet_stream();
([(header::CONTENT_TYPE, mime.as_ref())], content.data).into_response()
}
None => (StatusCode::NOT_FOUND, "404 Not Found").into_response(),
}
}

View File

@@ -0,0 +1,5 @@
mod http_server;
mod video_downloader;
pub use http_server::http_server;
pub use video_downloader::video_downloader;

View File

@@ -0,0 +1,67 @@
use std::path::PathBuf;
use std::sync::Arc;
use sea_orm::DatabaseConnection;
use tokio::time;
use crate::adapter::Args;
use crate::bilibili::{self, BiliClient};
use crate::config::CONFIG;
use crate::workflow::process_video_list;
/// 启动周期下载视频的任务
pub async fn video_downloader(connection: Arc<DatabaseConnection>) {
let mut anchor = chrono::Local::now().date_naive();
let bili_client = BiliClient::new();
let params = collect_task_params();
loop {
'inner: {
match bili_client.wbi_img().await.map(|wbi_img| wbi_img.into()) {
Ok(Some(mixin_key)) => bilibili::set_global_mixin_key(mixin_key),
Ok(_) => {
error!("解析 mixin key 失败,等待下一轮执行");
break 'inner;
}
Err(e) => {
error!("获取 mixin key 遇到错误:{e},等待下一轮执行");
break 'inner;
}
};
if anchor != chrono::Local::now().date_naive() {
if let Err(e) = bili_client.check_refresh().await {
error!("检查刷新 Credential 遇到错误:{e},等待下一轮执行");
break 'inner;
}
anchor = chrono::Local::now().date_naive();
}
for (args, path) in &params {
if let Err(e) = process_video_list(*args, &bili_client, path, &connection).await {
error!("处理过程遇到错误:{e}");
}
}
info!("本轮任务执行完毕,等待下一轮执行");
}
time::sleep(time::Duration::from_secs(CONFIG.interval)).await;
}
}
/// 构造下载视频任务执行所需的参数(下载类型和保存路径)
fn collect_task_params() -> Vec<(Args<'static>, &'static PathBuf)> {
let mut params = Vec::new();
CONFIG
.favorite_list
.iter()
.for_each(|(fid, path)| params.push((Args::Favorite { fid }, path)));
CONFIG
.collection_list
.iter()
.for_each(|(collection_item, path)| params.push((Args::Collection { collection_item }, path)));
if CONFIG.watch_later.enabled {
params.push((Args::WatchLater, &CONFIG.watch_later.path));
}
CONFIG
.submission_list
.iter()
.for_each(|(upper_id, path)| params.push((Args::Submission { upper_id }, path)));
params
}