From f5f2998d166408f11152abb3e019e8d5cbfe602b Mon Sep 17 00:00:00 2001 From: ForgQi <34411314+ForgQi@users.noreply.github.com> Date: Mon, 27 Oct 2025 01:28:08 +0800 Subject: [PATCH] Add streamer file info API and improve file listing Introduces a new API endpoint `/v1/streamer-info/files/{id}` to fetch file lists for a specific streamer, and updates the frontend to fetch file lists dynamically per row. Also updates serialization for streamer date fields, improves comments and error handling, and changes the default downloader type for plugins. Minor documentation and code cleanup included. --- Cargo.lock | 4 +- Cargo.toml | 2 +- README.md | 2 +- app/(app)/job/page.tsx | 51 ++++++++++--------- crates/biliup-cli/src/server/api/endpoints.rs | 14 ++++- .../biliup-cli/src/server/common/download.rs | 3 ++ .../core/downloader/ffmpeg_downloader.rs | 28 +++++----- crates/biliup-cli/src/server/core/plugin.rs | 2 +- .../biliup-cli/src/server/core/plugin/yy.rs | 11 ++-- .../src/server/infrastructure/models.rs | 2 + crates/biliup-cli/src/server/router.rs | 10 +--- 11 files changed, 69 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index df32fb6..9e1fb41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,7 +395,7 @@ dependencies = [ [[package]] name = "biliup-cli" -version = "1.1.19" +version = "1.1.20" dependencies = [ "async-channel", "async-trait", @@ -3641,7 +3641,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stream-gears" -version = "1.1.19" +version = "1.1.20" dependencies = [ "async-channel", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 2f29381..fa5046d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ resolver = "3" [workspace.package] -version = "1.1.19" +version = "1.1.20" [workspace.dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index 8fc76a8..49c0821 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Options: 2. 安装:`uv tool install biliup` 3. 启动:`biliup server --auth` 4. 访问 WebUI:`http://your-ip:19159` - +* 后台运行 `nohup biliup server --auth &` ### Termux - 详见[Wiki](https://github.com/biliup/biliup/wiki/Termux-%E4%B8%AD%E4%BD%BF%E7%94%A8-biliup) diff --git a/app/(app)/job/page.tsx b/app/(app)/job/page.tsx index 7c30eb2..1cef77b 100644 --- a/app/(app)/job/page.tsx +++ b/app/(app)/job/page.tsx @@ -20,6 +20,7 @@ import Filter from "@/app/(app)/job/Filter"; export default function Home() { const { Header, Footer, Sider, Content } = Layout const { data: data, error, isLoading } = useSWR('/v1/streamer-info', fetcher) + if (isLoading) { return } @@ -56,29 +57,7 @@ export default function Home() { render: (time: number) => humDate(time), }, ] - const expandRowRender = (record: any, index: number | undefined) => { - return ( - <> - 文件列表: - {record.files.map( - (it: { - id: Key | null | undefined - file: - | string - | number - | boolean - | ReactElement> - | Iterable - | ReactPortal - | null - | undefined - }) => { - return
  文件名:{it.file}
- } - )} - - ) - } + return ( <>
@@ -123,3 +102,29 @@ export default function Home() { ) } + + +// 创建一个子组件 +const FileLists = ({ recordId }: { recordId: string }) => { + const { data: files, isLoading } = useSWR( + `/v1/streamer-info/files/${recordId}`, + fetcher + ) + + if (isLoading) return
加载中...
+ if (!files || files.length === 0) return
暂无文件
+ + return ( + <> + 文件列表: + {files.map((it: any) => ( +
  文件名:{it.file}
+ ))} + + ) +} + +// 在 expandRowRender 中使用 +const expandRowRender = (record: any) => { + return +} diff --git a/crates/biliup-cli/src/server/api/endpoints.rs b/crates/biliup-cli/src/server/api/endpoints.rs index e9184f7..8afd6c0 100644 --- a/crates/biliup-cli/src/server/api/endpoints.rs +++ b/crates/biliup-cli/src/server/api/endpoints.rs @@ -291,14 +291,24 @@ pub async fn get_streamer_info( .await .change_context(AppError::Unknown) .map_err(report_to_response)?; + + Ok(Json(streamer_infos)) +} + +pub async fn get_streamer_info_files( + // Extension(streamers_service): Extension, + State(pool): State, + Path(id): Path, +) -> Result>, Response> { let file_items = FileItem::select() + .where_("streamer_info_id = ?") + .bind(id) .fetch_all(&pool) .await .change_context(AppError::Unknown) .map_err(report_to_response)?; - println!("{:?}", file_items); - Ok(Json(streamer_infos)) + Ok(Json(file_items)) } pub async fn get_upload_streamers_endpoint( diff --git a/crates/biliup-cli/src/server/common/download.rs b/crates/biliup-cli/src/server/common/download.rs index a05a80f..554978c 100644 --- a/crates/biliup-cli/src/server/common/download.rs +++ b/crates/biliup-cli/src/server/common/download.rs @@ -216,6 +216,8 @@ impl SegmentEventProcessor { move |event| { match event { SegmentEvent::Start { next_file_path } => { + unreachable!("应没有任何位置发出此事件"); + // 开始下载时,获取到的是将要下载的文件名,此时文件还未生成 // 触发弹幕滚动保存 if let Some(ref client) = danmaku && let Err(e) = client @@ -225,6 +227,7 @@ impl SegmentEventProcessor { } } SegmentEvent::Segment(event) => { + // 分段时,获取到的是已下载的文件名 // 触发弹幕滚动保存 if let Some(ref client) = danmaku && let Err(e) = client.rolling( diff --git a/crates/biliup-cli/src/server/core/downloader/ffmpeg_downloader.rs b/crates/biliup-cli/src/server/core/downloader/ffmpeg_downloader.rs index c10f008..21b3091 100644 --- a/crates/biliup-cli/src/server/core/downloader/ffmpeg_downloader.rs +++ b/crates/biliup-cli/src/server/core/downloader/ffmpeg_downloader.rs @@ -233,27 +233,25 @@ impl FfmpegDownloader { bail!(AppError::Custom("Process handle not found".to_string())); } }; + // 退出时,重命名文件 + let part_file = format!("{}.part", output_file.display()); + tokio::fs::rename(&part_file, &output_file) + .await + .change_context(AppError::Unknown)?; // let (tx, rx) = bounded(16); // 分段回调 + // 触发分段回调 + callback(SegmentEvent::Segment( + SegmentInfo { + prev_file_path: output_file, + segment_index: 0, + next_file_path: None, + } + )); // 根据退出码判断状态 match status.code() { Some(0) => { - // 正常退出,重命名文件 - let part_file = format!("{}.part", output_file.display()); - tokio::fs::rename(&part_file, &output_file) - .await - .change_context(AppError::Unknown)?; - - // 触发分段回调 - - // segment_callback(SegmentEvent { - // file_path: output_file, - // segment_index: 0, - // start_time: std::time::SystemTime::now(), - // end_time: std::time::SystemTime::now(), - // }); - Ok(DownloadStatus::SegmentCompleted) } Some(255) => Ok(DownloadStatus::StreamEnded), diff --git a/crates/biliup-cli/src/server/core/plugin.rs b/crates/biliup-cli/src/server/core/plugin.rs index 02a43eb..c0e490c 100644 --- a/crates/biliup-cli/src/server/core/plugin.rs +++ b/crates/biliup-cli/src/server/core/plugin.rs @@ -91,7 +91,7 @@ pub trait DownloadPlugin { raw_stream_url, config, Vec::new(), - DownloaderType::FfmpegInternal, + DownloaderType::FfmpegExternal, )) } // Some(DownloaderType::StreamGears) => { diff --git a/crates/biliup-cli/src/server/core/plugin/yy.rs b/crates/biliup-cli/src/server/core/plugin/yy.rs index 7e3b2fa..9ac458c 100644 --- a/crates/biliup-cli/src/server/core/plugin/yy.rs +++ b/crates/biliup-cli/src/server/core/plugin/yy.rs @@ -108,17 +108,15 @@ impl DownloadPlugin for YY { .to_string(); // 构建URL - let url = format!( - "https://stream-manager.yy.com/v3/channel/streams?uid=0&cid={}&sid={}&appid=0&sequence={}&encode=json", - rid, rid, millis_13 - ); - // 发送POST请求并处理响应 let result = ctx .worker .client .client - .post(&url) + .post(&format!( + "https://stream-manager.yy.com/v3/channel/streams?uid=0&cid={}&sid={}&appid=0&sequence={}&encode=json", + rid, rid, millis_13 + )) .timeout(std::time::Duration::from_secs(30)) .headers(self.fake_headers.clone()) .body(data) @@ -129,7 +127,6 @@ impl DownloadPlugin for YY { .await .change_context(AppError::Custom(format!("解析json出错 rid: {rid}")))?; - println!("{:#?}", result); let Some(stream_url) = result .get("avp_info_res") .and_then(|info| info.get("stream_line_addr")) diff --git a/crates/biliup-cli/src/server/infrastructure/models.rs b/crates/biliup-cli/src/server/infrastructure/models.rs index d88611c..f7720f8 100644 --- a/crates/biliup-cli/src/server/infrastructure/models.rs +++ b/crates/biliup-cli/src/server/infrastructure/models.rs @@ -3,6 +3,7 @@ pub mod hook_step; pub mod live_streamer; pub mod upload_streamer; +use chrono::serde::ts_seconds; use chrono::{DateTime, Utc}; use ormlite::{Insert, Model}; use serde::{Deserialize, Serialize}; @@ -19,6 +20,7 @@ pub struct StreamerInfo { pub url: String, /// 直播标题 pub title: String, + #[serde(with = "ts_seconds")] /// 直播开始时间 pub date: DateTime, /// 直播封面路径(可选) diff --git a/crates/biliup-cli/src/server/router.rs b/crates/biliup-cli/src/server/router.rs index 2081e7e..1434e8c 100644 --- a/crates/biliup-cli/src/server/router.rs +++ b/crates/biliup-cli/src/server/router.rs @@ -1,14 +1,7 @@ use crate::server::api::bilibili_endpoints::{ archive_pre_endpoint, get_myinfo_endpoint, get_proxy_endpoint, }; -use crate::server::api::endpoints::{ - add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint, - delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode, get_status, - get_streamer_info, get_streamers_endpoint, get_upload_streamer_endpoint, - get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode, - pause_streamers_endpoint, post_streamers_endpoint, post_uploads, put_configuration, - put_streamers_endpoint, -}; +use crate::server::api::endpoints::{add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint, delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode, get_status, get_streamer_info, get_streamer_info_files, get_streamers_endpoint, get_upload_streamer_endpoint, get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode, pause_streamers_endpoint, post_streamers_endpoint, post_uploads, put_configuration, put_streamers_endpoint}; use crate::server::infrastructure::service_register::ServiceRegister; use axum::Router; use axum::body::Body; @@ -36,6 +29,7 @@ pub fn router(service_register: ServiceRegister) -> Router<()> { ) // 主播信息路由 .route("/v1/streamer-info", get(get_streamer_info)) // 获取主播信息 + .route("/v1/streamer-info/files/{id}", get(get_streamer_info_files)) // 获取主播信息 // 上传模板管理路由 .route("/v1/upload/streamers", get(get_upload_streamers_endpoint)) // 获取上传模板列表 .route(