Refactor CLI, update dependencies, and improve server logic

Refactored CLI argument parsing and main entrypoint in both Python and Rust, updated pyo3 and pythonize dependencies, and improved modularization in biliup-cli. Added an 'auth' flag to the server command, fixed several import and ownership issues, and improved plugin and server initialization logic. Also cleaned up unused imports and streamlined code for better maintainability.
This commit is contained in:
ForgQi committed 2025-10-24 03:11:27 +08:00
1 parent a0b6800c0a
commit b829731ad9
30 files changed
+233 -188

No files matched your search

Generated
+13 -14
View File
@@ -395,7 +395,7 @@ dependencies = [
[[package]]
name = "biliup-cli"
version = "1.1.17"
version = "1.1.18"
dependencies = [
"async-channel",
"async-trait",
@@ -2552,9 +2552,9 @@ dependencies = [
[[package]]
name = "pyo3"
version = "0.26.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383"
checksum = "37a6df7eab65fc7bee654a421404947e10a0f7085b6951bf2ea395f4659fb0cf"
dependencies = [
"anyhow",
"indoc",
@@ -2570,18 +2570,18 @@ dependencies = [
[[package]]
name = "pyo3-build-config"
version = "0.26.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f"
checksum = "f77d387774f6f6eec64a004eac0ed525aab7fa1966d94b42f743797b3e395afb"
dependencies = [
"target-lexicon 0.13.2",
]
[[package]]
name = "pyo3-ffi"
version = "0.26.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105"
checksum = "2dd13844a4242793e02df3e2ec093f540d948299a6a77ea9ce7afd8623f542be"
dependencies = [
"libc",
"pyo3-build-config",
@@ -2589,9 +2589,9 @@ dependencies = [
[[package]]
name = "pyo3-macros"
version = "0.26.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded"
checksum = "eaf8f9f1108270b90d3676b8679586385430e5c0bb78bb5f043f95499c821a71"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
@@ -2601,9 +2601,9 @@ dependencies = [
[[package]]
name = "pyo3-macros-backend"
version = "0.26.0"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf"
checksum = "70a3b2274450ba5288bc9b8c1b69ff569d1d61189d4bff38f8d22e03d17f932b"
dependencies = [
"heck",
"proc-macro2",
@@ -2615,8 +2615,7 @@ dependencies = [
[[package]]
name = "pythonize"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11e06e4cff9be2bbf2bddf28a486ae619172ea57e79787f856572878c62dcfe2"
source = "git+https://github.com/davidhewitt/pythonize.git?rev=6140fe00f601f418371d969bedbcd27944a6956a#6140fe00f601f418371d969bedbcd27944a6956a"
dependencies = [
"pyo3",
"serde",
@@ -3642,7 +3641,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stream-gears"
version = "1.1.17"
version = "1.1.18"
dependencies = [
"async-channel",
"async-trait",
+1 -1
View File
@@ -7,7 +7,7 @@ members = [
resolver = "3"
[workspace.package]
version = "1.1.17"
version = "1.1.18"
[workspace.dependencies]
serde = { version = "1.0", features = ["derive"] }
+2 -37
View File
@@ -15,48 +15,13 @@ from biliup.common.log import DebugLevelFilter
def arg_parser():
daemon = Daemon('watch_process.pid', lambda: main(args))
parser = argparse.ArgumentParser(description='Stream download and upload, not only for bilibili.')
parser.add_argument('--version', action='version', version=f"v{__version__}")
parser.add_argument('-H', help='web api host [default: 0.0.0.0]', dest='host')
if IS_FROZEN:
parser.add_argument(
'-P',
help='web api port (REQUIRED)',
dest='port',
required=True,
type=int
)
else:
parser.add_argument('-P', help='web api port [default: 19159]', default=19159, dest='port')
parser.add_argument('--no-http', action='store_true', help='disable web api')
parser.add_argument('--static-dir', help='web static files directory for custom ui')
parser.add_argument('--password', help='web ui password ,default username is biliup', dest='password')
parser.add_argument('-v', '--verbose', action="store_const", const=logging.DEBUG, help="Increase output verbosity")
parser.add_argument('--config', type=argparse.FileType(mode='rb'),
help='Location of the configuration file (default "./config.yaml")')
parser.add_argument('--no-access-log', action='store_true', help='disable web access log')
subparsers = parser.add_subparsers(help='Windows does not support this sub-command.')
# create the parser for the "start" command
parser_start = subparsers.add_parser('start', help='Run as a daemon process.')
parser_start.set_defaults(func=daemon.start)
parser_stop = subparsers.add_parser('stop', help='Stop daemon according to "watch_process.pid".')
parser_stop.set_defaults(func=daemon.stop)
parser_restart = subparsers.add_parser('restart')
parser_restart.set_defaults(func=daemon.restart)
parser.set_defaults(func=lambda: asyncio.run(main(args)))
args = parser.parse_args()
if args.verbose:
LOG_CONF['loggers']['biliup']['level'] = args.verbose
LOG_CONF['root']['level'] = args.verbose
logging.config.dictConfig(LOG_CONF)
logging.getLogger('httpx').addFilter(DebugLevelFilter())
args.func()
asyncio.run(main())
async def main(args):
async def main():
# from biliup.app import event_manager
# event_manager.start()
+6 -2
View File
@@ -2,7 +2,7 @@ use biliup::uploader::bilibili::{Studio, Vid};
use biliup::uploader::util::SubmitOption;
use clap::{Parser, Subcommand};
use biliup_cli::UploadLine;
use crate::UploadLine;
use std::path::PathBuf;
#[derive(Parser)]
@@ -123,7 +123,11 @@ pub enum Commands {
/// Port to use
#[arg(short, long, default_value = "19159")]
port: u16,
port: u16,
/// Port to use
#[arg(short, long, default_value = "false")]
auth: bool,
},
/// 列出所有已上传的视频
List {
+1 -1
View File
@@ -7,7 +7,7 @@ use biliup::downloader::flv_writer;
use biliup::downloader::flv_writer::{FlvTag, TagDataHeader};
use biliup::downloader::httpflv::map_parse_err;
use biliup::downloader::util::Segmentable;
use biliup_cli::server::errors::{AppError, AppResult};
use crate::server::errors::{AppError, AppResult};
use bytes::{Buf, BufMut, Bytes, BytesMut};
use error_stack::ResultExt;
use std::io::{BufReader, BufWriter, ErrorKind, Read};
+5 -2
View File
@@ -1,4 +1,7 @@
pub mod server;
pub mod cli;
pub mod downloader;
pub mod uploader;
// use crate::server::api::router::ApplicationController;
use crate::server::app::ApplicationController;
@@ -12,7 +15,7 @@ use error_stack::ResultExt;
use std::net::ToSocketAddrs;
use std::sync::{Arc, RwLock};
pub async fn run(addr: (&str, u16)) -> AppResult<()> {
pub async fn run(addr: (&str, u16), auth: bool) -> AppResult<()> {
// let config = Arc::new(AppConfig::parse());
tracing::info!(
@@ -37,7 +40,7 @@ pub async fn run(addr: (&str, u16)) -> AppResult<()> {
.change_context(AppError::Unknown)?
.next()
.unwrap();
ApplicationController::serve(&addr, service_register)
ApplicationController::serve(&addr, auth, service_register)
.await
.attach("could not initialize application routes")?;
Ok(())
+4 -8
View File
@@ -1,12 +1,8 @@
mod cli;
mod downloader;
mod uploader;
use time::macros::format_description;
use crate::cli::{Cli, Commands};
use crate::downloader::{download, generate_json};
use crate::uploader::{append, list, login, renew, show, upload_by_command, upload_by_config};
use biliup_cli::cli::{Cli, Commands};
use biliup_cli::downloader::{download, generate_json};
use biliup_cli::uploader::{append, list, login, renew, show, upload_by_command, upload_by_config};
use biliup::uploader::util::SubmitOption;
use clap::Parser;
@@ -101,7 +97,7 @@ async fn main() -> AppResult<()> {
split_size,
split_time,
} => download(&url, output, split_size, split_time).await?,
Commands::Server { bind, port } => biliup_cli::run((&bind, port)).await?,
Commands::Server { bind, port, auth } => biliup_cli::run((&bind, port)).await?,
Commands::List {
is_pubing,
pubed,
@@ -10,7 +10,6 @@ use bytes::Bytes;
use error_stack::{Report, ResultExt};
use ormlite::Model;
use std::collections::HashMap;
use tracing::info;
/// B站投稿预处理端点
pub async fn archive_pre_endpoint(
+9 -14
View File
@@ -6,7 +6,7 @@ use crate::server::core::download_manager::ActorHandle;
use crate::server::core::downloader::Downloader;
use crate::server::errors::{AppError, report_to_response};
use crate::server::infrastructure::connection_pool::ConnectionPool;
use crate::server::infrastructure::context::{Stage, Worker, WorkerStatus};
use crate::server::infrastructure::context::{Worker, WorkerStatus};
use crate::server::infrastructure::dto::LiveStreamerResponse;
use crate::server::infrastructure::models::live_streamer::{InsertLiveStreamer, LiveStreamer};
use crate::server::infrastructure::models::upload_streamer::{
@@ -19,28 +19,23 @@ use crate::server::infrastructure::repositories::{
del_streamer, get_all_streamer, get_upload_config,
};
use crate::server::infrastructure::service_register::ServiceRegister;
use axum::extract::ws::{Message, Utf8Bytes, WebSocket};
use axum::extract::{Path, Query, State, WebSocketUpgrade};
use axum::extract::{Path, State};
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::{Json, debug_handler};
use axum::Json;
use biliup::credential::Credential;
use chrono::{Local, Utc};
use chrono::Utc;
use clap::ValueEnum;
use error_stack::{Report, ResultExt, bail};
use error_stack::{Report, ResultExt};
use ormlite::{Insert, Model};
use serde::Deserialize;
use serde_json::json;
use std::collections::VecDeque;
use std::io;
use std::io::ErrorKind;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::time::{Duration, UNIX_EPOCH};
use tokio::fs;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, BufReader};
use tokio::time::{MissedTickBehavior, interval};
use tracing::{debug, error, info};
use tokio::io::{AsyncWriteExt};
use tracing::info;
pub async fn get_streamers_endpoint(
State(pool): State<ConnectionPool>,
@@ -92,7 +87,7 @@ pub async fn post_streamers_endpoint(
.await
.map_err(report_to_response)?;
service_register
.add_room(&manager, live_streamers.clone(), upload_config)
.add_room(manager, live_streamers.clone(), upload_config)
.await
.map_err(report_to_response)?;
@@ -125,7 +120,7 @@ pub async fn put_streamers_endpoint(
.map_err(report_to_response)?;
service_register
.add_room(&manager, streamer.clone(), upload_config)
.add_room(manager, streamer.clone(), upload_config)
.await
.map_err(report_to_response)?;
+1 -1
View File
@@ -156,7 +156,7 @@ async fn send_last_lines(
let file_size = meta.len();
let file = fs::File::open(path).await?;
let mut reader = BufReader::new(file);
let reader = BufReader::new(file);
let mut lines = reader.lines();
let mut buf: VecDeque<String> = VecDeque::with_capacity(n);
+2 -2
View File
@@ -26,7 +26,7 @@ pub struct ApplicationController;
impl ApplicationController {
/// 启动Web服务器
pub async fn serve(addr: &SocketAddr, service_register: ServiceRegister) -> AppResult<()> {
pub async fn serve(addr: &SocketAddr, enable_login_guard: bool, service_register: ServiceRegister) -> AppResult<()> {
// 会话层配置
// 使用 tower-sessions 建立会话层,将会话作为请求扩展提供
let session_store = SqliteStore::new(service_register.pool.clone());
@@ -58,7 +58,7 @@ impl ApplicationController {
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
// 构建应用程序路由
let enable_login_guard = true; // 是否启用登录保护
// 是否启用登录保护
let mut app = server::router::router(service_register);
if enable_login_guard {
app = app
@@ -1,12 +1,10 @@
use crate::server::common::upload::UploaderMessage;
use crate::server::common::util::Recorder;
use crate::server::core::downloader::{DownloadStatus, Downloader, SegmentEvent, SegmentInfo};
use crate::server::core::monitor::RoomsHandle;
use crate::server::core::plugin::{DownloadPlugin, StreamInfoExt};
use crate::server::core::plugin::DownloadPlugin;
use crate::server::errors::{AppError, AppResult};
use crate::server::infrastructure::context::{Context, Stage, Worker, WorkerStatus};
use crate::server::infrastructure::context::{Context, Worker, WorkerStatus};
use async_channel::{Receiver, Sender};
use chrono::Local;
use error_stack::{FutureExt, ResultExt, bail};
use std::fs;
use std::path::Path;
@@ -59,11 +57,10 @@ impl DownloadGuard {
let rooms_handle = self.rooms_handle.clone();
let worker = self.worker.clone();
if let Some(client) = danmaku {
if let Err(e) = client.stop().await {
if let Some(client) = danmaku
&& let Err(e) = client.stop().await {
error!("Error stopping danmaku client: {}", e);
}
}
// 确保状态更新和资源清理
rooms_handle
@@ -219,18 +216,17 @@ impl SegmentEventProcessor {
match event {
SegmentEvent::Start { next_file_path } => {
// 触发弹幕滚动保存
if let Some(ref client) = danmaku {
if let Err(e) = client
if let Some(ref client) = danmaku
&& let Err(e) = client
.rolling(&next_file_path.with_extension("xml").display().to_string())
{
error!("Danmaku rolling error: {}", e);
}
}
}
SegmentEvent::Segment(event) => {
// 触发弹幕滚动保存
if let Some(ref client) = danmaku {
if let Err(e) = client.rolling(
if let Some(ref client) = danmaku
&& let Err(e) = client.rolling(
&event
.prev_file_path
.with_extension("xml")
@@ -239,7 +235,6 @@ impl SegmentEventProcessor {
) {
error!("Danmaku rolling error: {}", e);
}
}
// 异步处理事件
let processor = processor.clone();
if let Err(e) = processor.process(event) {
@@ -1,20 +1,19 @@
use crate::UploadLine;
use crate::server::common::util::Recorder;
use crate::server::core::downloader::{SegmentEvent, SegmentInfo};
use crate::server::core::downloader::SegmentInfo;
use crate::server::errors::{AppError, AppResult};
use crate::server::infrastructure::context::{Context, Stage, Worker, WorkerStatus};
use crate::server::infrastructure::models::InsertFileItem;
use crate::server::infrastructure::models::hook_step::process_video;
use crate::server::infrastructure::models::upload_streamer::UploadStreamer;
use async_channel::Receiver;
use biliup::bilibili::{BiliBili, Credit, ResponseData, Studio, Video};
use biliup::bilibili::{BiliBili, ResponseData, Studio, Video};
use biliup::client::StatelessClient;
use biliup::credential::login_by_cookies;
use biliup::error::Kind;
use biliup::uploader::line::{Line, Probe};
use biliup::uploader::util::SubmitOption;
use biliup::uploader::{VideoFile, line};
use chrono::Local;
use error_stack::ResultExt;
use futures::StreamExt;
use futures::stream::Inspect;
@@ -73,7 +72,7 @@ where
// 4. 执行后处理
if !uploaded_videos.paths.is_empty() {
execute_postprocessor(uploaded_videos.paths, &ctx).await?;
execute_postprocessor(uploaded_videos.paths, ctx).await?;
}
Ok(())
@@ -119,7 +118,7 @@ async fn get_upload_line(client: &StatelessClient, line: &str) -> AppResult<Line
}
async fn pipeline_upload_videos<F>(
mut rx: Inspect<Receiver<SegmentInfo>, F>,
rx: Inspect<Receiver<SegmentInfo>, F>,
context: &UploadContext,
) -> AppResult<UploadedVideos>
where
@@ -215,11 +214,11 @@ pub async fn submit_to_bilibili(
let result = match submit_option {
SubmitOption::BCutAndroid => bilibili
.submit_by_bcut_android(&studio, None)
.submit_by_bcut_android(studio, None)
.await
.change_context(AppError::Unknown)?,
_ => bilibili
.submit_by_app(&studio, None)
.submit_by_app(studio, None)
.await
.change_context(AppError::Unknown)?,
};
@@ -324,7 +323,7 @@ pub async fn upload(
.to_str()
);
info!("{line:?}");
let video_file = VideoFile::new(&video_path).change_context_lazy(|| AppError::Unknown)?;
let video_file = VideoFile::new(video_path).change_context_lazy(|| AppError::Unknown)?;
let total_size = video_file.total_size;
let file_name = video_file.file_name.clone();
let uploader = line
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::server::infrastructure::models::StreamerInfo;
use chrono::{DateTime, Duration, Local};
use chrono::{Duration, Local};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use url::Url;
@@ -1,12 +1,10 @@
use crate::server::common::download::{DActor, DownloaderMessage};
use crate::server::common::upload::{UActor, UploaderMessage};
use crate::server::core::downloader::Downloader;
use crate::server::core::monitor::Monitor;
use crate::server::core::plugin::DownloadPlugin;
use crate::server::infrastructure::connection_pool::ConnectionPool;
use async_channel::{Sender, bounded};
use core::fmt;
use error_stack::ResultExt;
use std::sync::{Arc, Mutex};
use tokio::task::JoinHandle;
/// 下载管理器
@@ -11,7 +11,6 @@ use error_stack::{ResultExt, bail};
use nom::Err;
use std::path::PathBuf;
use std::sync::{Arc, RwLock};
use std::time::SystemTime;
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, info};
+3 -4
View File
@@ -6,14 +6,13 @@ use crate::server::infrastructure::connection_pool::ConnectionPool;
use crate::server::infrastructure::context::{Context, Stage, Worker, WorkerStatus};
use crate::server::infrastructure::models::StreamerInfo;
use async_channel::{Receiver, Sender, bounded};
use chrono::Local;
use ormlite::Model;
use ormlite::model::ModelBuilder;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
use tracing::{debug, error, info};
use tracing::{error, info};
/// 启动客户端监控循环
///
@@ -45,7 +44,7 @@ async fn start_client(
.url(sql_no_id.url.clone())
.name(room.live_streamer.remark.clone())
.title(sql_no_id.title.clone())
.date(sql_no_id.date.clone())
.date(sql_no_id.date)
.live_cover_path(sql_no_id.live_cover_path.clone())
.insert(&ctx.pool)
.await
@@ -76,7 +75,7 @@ async fn start_client(
&suffix,
);
// 修改 ctx
ctx.stream_info = stream_info;
ctx.stream_info = *stream_info;
ctx.recorder = recorder;
// 发送下载开始消息
if actor_handle
+3 -5
View File
@@ -1,17 +1,15 @@
use crate::server::common::util::Recorder;
use crate::server::config::Config;
use crate::server::core::downloader::Downloader;
use crate::server::errors::{AppError, AppResult};
use crate::server::infrastructure::context::{Context, Worker};
use crate::server::errors::AppError;
use crate::server::infrastructure::context::Context;
use crate::server::infrastructure::models::StreamerInfo;
use async_trait::async_trait;
use chrono::{DateTime, Local};
use error_stack::Report;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use time::OffsetDateTime;
/// 流信息结构
/// 包含直播流的详细信息
@@ -33,7 +31,7 @@ pub struct StreamInfoExt {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StreamStatus {
/// 正在直播,包含流信息
Live { stream_info: StreamInfoExt },
Live { stream_info: Box<StreamInfoExt> },
/// 离线状态
Offline,
/// 未知状态
@@ -34,6 +34,7 @@ impl ConnectionManager {
std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(false)
.open(path)
.change_context(AppError::Unknown)?;
@@ -2,17 +2,13 @@ use crate::server::common::util::Recorder;
use crate::server::config::Config;
use crate::server::core::downloader::Downloader;
use crate::server::core::plugin::StreamInfoExt;
use crate::server::errors::{AppError, AppResult};
use crate::server::infrastructure::connection_pool::ConnectionPool;
use crate::server::infrastructure::models::live_streamer::LiveStreamer;
use crate::server::infrastructure::models::upload_streamer::UploadStreamer;
use crate::server::infrastructure::repositories::{get_config, get_streamer};
use axum::http::Extensions;
use biliup::client::StatelessClient;
use core::fmt;
use error_stack::ResultExt;
use ormlite::Model;
use serde::{Deserialize, Serialize, Serializer};
use std::sync::{Arc, RwLock};
use tracing::info;
@@ -1,6 +1,5 @@
use crate::server::infrastructure::context::WorkerStatus;
use crate::server::infrastructure::models::live_streamer::LiveStreamer;
use serde::{Deserialize, Serialize};
use serde::Serialize;
/// 直播主播响应数据传输对象
/// 包含主播信息和当前工作状态
@@ -89,7 +89,7 @@ impl HookStep {
let status = process.wait().await.change_context(AppError::Unknown)?;
if !status.success() {
bail!(AppError::Custom(
format!("Command failed with status: {}", status).into()
format!("Command failed with status: {}", status)
));
}
@@ -1,9 +1,8 @@
use crate::server::config::Config;
use crate::server::core::download_manager::{ActorHandle, DownloadManager};
use crate::server::core::monitor::Monitor;
use crate::server::errors::{AppError, AppResult};
use crate::server::infrastructure::connection_pool::ConnectionPool;
use crate::server::infrastructure::context::{Context, Worker, WorkerStatus};
use crate::server::infrastructure::context::{Worker, WorkerStatus};
use crate::server::infrastructure::models::live_streamer::LiveStreamer;
use crate::server::infrastructure::models::upload_streamer::UploadStreamer;
use axum::extract::FromRef;
+2 -2
View File
@@ -5,8 +5,8 @@ use biliup::uploader::credential::{Credential, LoginInfo};
use biliup::uploader::line::Probe;
use biliup::uploader::util::SubmitOption;
use biliup::uploader::{VideoFile, credential, line, load_config};
use biliup_cli::UploadLine;
use biliup_cli::server::errors::{AppError, AppResult};
use crate::UploadLine;
use crate::server::errors::{AppError, AppResult};
use bytes::{Buf, Bytes};
use clap::ValueEnum;
use dialoguer::Input;
+2 -2
View File
@@ -9,8 +9,8 @@ name = "stream_gears"
crate-type = ["cdylib", "lib"]
[dependencies]
pyo3 = { version = "0.26", features = ["abi3-py38", "anyhow"] }
pythonize = { version = "0.26.0" }
pyo3 = { version = "0.27.1", features = ["abi3-py38", "anyhow"] }
pythonize = { git = "https://github.com/davidhewitt/pythonize.git", rev = "6140fe00f601f418371d969bedbcd27944a6956a" }
bytes = { workspace = true }
ormlite = { version = "0.24.1", features = ["sqlite", "chrono"] }
rust-embed = "8.7.2"
-2
View File
@@ -3,8 +3,6 @@ use biliup_cli::server::core::downloader::{DownloadStatus, Downloader, SegmentEv
use biliup_cli::server::errors::{AppError, AppResult};
use error_stack::ResultExt;
use pyo3::prelude::*;
use pyo3::types::PyDict;
use std::collections::HashMap;
use std::sync::Arc;
/// DanmakuClient provides Rust bindings to the Python DanmakuClient
+28 -2
View File
@@ -16,12 +16,14 @@ use biliup::credential::Credential;
use biliup::downloader::extractor::CallbackFn;
use biliup::downloader::util::{LifecycleFile, Segmentable};
use biliup::downloader::{hls, httpflv};
use pyo3::types::PyType;
use pyo3::types::{PyList, PyType};
use std::collections::HashMap;
use std::env::args;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Mutex, Once};
use std::time::Duration;
use clap::Parser;
use tracing::{debug, error, info};
use tracing_subscriber::EnvFilter;
@@ -461,7 +463,31 @@ fn upload(
#[pyfunction]
pub fn main_loop(py: Python<'_>) -> PyResult<()> {
py.detach(|| server::_main().map_err(|e| PyRuntimeError::new_err(format!("{e:?}"))))
// 获取 Python 的 sys.argv
let sys = py.import("sys")?;
let bound = sys.getattr("argv")?;
let argv: &Bound<PyList> = bound.downcast()?;
let mut args: Vec<String> = argv
.iter()
.map(|x| x.extract::<String>())
.collect::<PyResult<Vec<_>>>()?;
// if args.len() == 1 {
// args.push("server".to_string());
// }
match args.as_slice() {
&[] => todo!(),
&[_] => {
args.push("server".to_string());
},
[_, command, ..] => if command == "start" {
args[1] = "server".to_string();
}
}
py.detach(|| server::_main(args.as_slice()).map_err(|e| PyRuntimeError::new_err(format!("{e:?}"))))
// for item in plugin_list.iter() {
// // 每个元素都是一个类(type)
+127 -39
View File
@@ -1,4 +1,4 @@
use crate::{DanmakuClient, construct_headers};
use crate::{DanmakuClient, construct_headers, download};
use async_trait::async_trait;
use biliup::downloader::util::Segmentable;
use biliup_cli::server::app::ApplicationController;
@@ -11,15 +11,14 @@ use biliup_cli::server::core::downloader::{DownloadConfig, Downloader, Downloade
use biliup_cli::server::core::plugin::{DownloadPlugin, StreamInfoExt, StreamStatus};
use biliup_cli::server::errors::{AppError, AppResult};
use biliup_cli::server::infrastructure::connection_pool::ConnectionManager;
use biliup_cli::server::infrastructure::context::{Context, Worker};
use biliup_cli::server::infrastructure::context::Context;
use biliup_cli::server::infrastructure::models::StreamerInfo;
use biliup_cli::server::infrastructure::repositories;
use biliup_cli::server::infrastructure::repositories::get_upload_config;
use biliup_cli::server::infrastructure::service_register::ServiceRegister;
use chrono::{DateTime, Local, NaiveDateTime, Utc};
use chrono::Utc;
use error_stack::{Report, ResultExt};
use fancy_regex::Regex;
use pyo3::exceptions::PyRuntimeError;
use pyo3::prelude::PyDictMethods;
use pyo3::prelude::{PyAnyMethods, PyListMethods, PyModule};
use pyo3::types::PyDict;
@@ -32,8 +31,12 @@ use std::net::ToSocketAddrs;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Arc, LazyLock, RwLock};
use time::OffsetDateTime;
use clap::Parser;
use tracing::{debug, info};
use biliup::uploader::util::SubmitOption;
use biliup_cli::cli::{Cli, Commands};
use biliup_cli::downloader::generate_json;
use biliup_cli::uploader::{append, list, login, renew, show, upload_by_command, upload_by_config};
#[derive(Debug)]
pub struct PyPlugin {
@@ -82,7 +85,7 @@ impl DownloadPlugin for PyPlugin {
Arc::new(DanmakuClient::new(Arc::new(danmaku))) as Arc<dyn Downloader>;
ctx.extension.insert(danmaku);
}
Ok(StreamStatus::Live { stream_info: info })
Ok(StreamStatus::Live { stream_info: Box::new(info) })
}
None => Ok(StreamStatus::Offline),
}
@@ -102,7 +105,7 @@ impl DownloadPlugin for PyPlugin {
segment_time: config.segment_time.or_else(default_segment_time),
file_size: Some(config.file_size), // 2GB
headers: stream_info.stream_headers.clone(),
recorder: recorder,
recorder,
// output_dir: PathBuf::from("./downloads")
output_dir: PathBuf::from("."),
};
@@ -349,44 +352,129 @@ fn cfg_arc() -> &'static Arc<RwLock<Config>> {
}
#[tokio::main]
pub(crate) async fn _main() -> AppResult<()> {
info!(
pub(crate) async fn _main(args: &[String]) -> AppResult<()> {
let cli = Cli::try_parse_from(args).change_context(AppError::Unknown)?;
match cli.command {
Commands::Login => login(cli.user_cookie, cli.proxy.as_deref()).await?,
Commands::Renew => {
renew(cli.user_cookie, cli.proxy.as_deref()).await?;
}
Commands::Upload {
video_path,
config: None,
line,
limit,
studio,
submit,
} => {
upload_by_command(
studio,
cli.user_cookie,
video_path,
line,
limit,
submit.unwrap_or(SubmitOption::App),
cli.proxy.as_deref(),
)
.await?
}
Commands::Upload {
video_path: _,
config: Some(config),
submit,
..
} => {
upload_by_config(config, cli.user_cookie, submit, cli.proxy.as_deref()).await?;
}
Commands::Append {
video_path,
vid,
line,
limit,
studio: _,
submit,
} => {
append(
cli.user_cookie,
vid,
video_path,
line,
limit,
submit.unwrap_or(SubmitOption::App),
cli.proxy.as_deref(),
)
.await?
}
Commands::Show { vid } => show(cli.user_cookie, vid, cli.proxy.as_deref()).await?,
Commands::DumpFlv { file_name } => generate_json(file_name)?,
Commands::Download {
url,
output,
split_size,
split_time,
} => biliup_cli::downloader::download(&url, output, split_size, split_time).await?,
Commands::Server { bind, port, auth } => {
info!(
"environment loaded and configuration parsed, initializing Postgres connection and running migrations..."
);
let conn_pool = ConnectionManager::new_pool("data/data.sqlite3")
.await
.expect("could not initialize the database connection pool");
let conn_pool = ConnectionManager::new_pool("data/data.sqlite3")
.await
.expect("could not initialize the database connection pool");
*CONFIG.write().unwrap() = repositories::get_config(&conn_pool).await?;
let actor_handle = Arc::new(ActorHandle::new(
CONFIG.read().unwrap().pool1_size,
CONFIG.read().unwrap().pool2_size,
));
let vec = from_py(actor_handle.clone()).unwrap();
*CONFIG.write().unwrap() = repositories::get_config(&conn_pool).await?;
let actor_handle = Arc::new(ActorHandle::new(
CONFIG.read().unwrap().pool1_size,
CONFIG.read().unwrap().pool2_size,
));
let vec = from_py(actor_handle.clone()).unwrap();
let service_register = ServiceRegister::new(conn_pool, CONFIG.clone(), actor_handle, vec);
let service_register = ServiceRegister::new(conn_pool, CONFIG.clone(), actor_handle, vec);
let all_streamer = repositories::get_all_streamer(&service_register.pool).await?;
let all_streamer = repositories::get_all_streamer(&service_register.pool).await?;
for streamer in all_streamer {
// workers.push(Arc::new(Worker::new(streamer.id, service_register.pool.clone())));
if let Some(manager) = service_register.get_manager(&streamer.url) {
let upload_config = get_upload_config(&service_register.pool, streamer.id).await?;
let _ = service_register
.add_room(&manager, streamer, upload_config)
.await?;
};
}
for streamer in all_streamer {
// workers.push(Arc::new(Worker::new(streamer.id, service_register.pool.clone())));
if let Some(manager) = service_register.get_manager(&streamer.url) {
let upload_config = get_upload_config(&service_register.pool, streamer.id).await?;
let _ = service_register
.add_room(manager, streamer, upload_config)
.await?;
};
}
info!("migrations successfully ran, initializing axum server...");
let addr = (bind, port);
let addr = addr
.to_socket_addrs()
.change_context(AppError::Unknown)?
.next()
.unwrap();
ApplicationController::serve(&addr, auth, service_register)
.await
.attach("could not initialize application routes")?;
// biliup_cli::run((&bind, port)).await?
},
Commands::List {
is_pubing,
pubed,
not_pubed,
from_page,
max_pages,
} => {
list(
cli.user_cookie,
is_pubing,
pubed,
not_pubed,
cli.proxy.as_deref(),
from_page,
max_pages,
)
.await?
}
};
info!("migrations successfully ran, initializing axum server...");
let addr = ("0.0.0.0", 19159);
let addr = addr
.to_socket_addrs()
.change_context(AppError::Unknown)?
.next()
.unwrap();
ApplicationController::serve(&addr, service_register)
.await
.attach("could not initialize application routes")?;
Ok(())
}
+4 -14
View File
@@ -1,11 +1,4 @@
use biliup::client::StatelessClient;
use biliup::error::Kind;
use biliup::uploader::bilibili::{Credit, ResponseData, Studio};
use biliup::uploader::credential::login_by_cookies;
use biliup::uploader::line::Probe;
use biliup::uploader::util::SubmitOption;
use biliup::uploader::{VideoFile, line};
use futures::StreamExt;
use pyo3::prelude::*;
use pyo3::pyclass;
@@ -16,9 +9,6 @@ use bon::Builder;
use error_stack::ResultExt;
use std::collections::HashMap;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::Instant;
use tracing::info;
#[pyclass]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
@@ -40,11 +30,11 @@ pub enum UploadLine {
Alia,
}
impl Into<biliup_cli::UploadLine> for UploadLine {
fn into(self) -> biliup_cli::UploadLine {
impl From<UploadLine> for biliup_cli::UploadLine {
fn from(val: UploadLine) -> Self {
use UploadLine as P;
use biliup_cli::UploadLine as C;
match self {
match val {
P::Bldsa => C::Bldsa,
P::Cnbldsa => C::Cnbldsa,
P::Andsa => C::Andsa,
@@ -190,5 +180,5 @@ pub async fn upload(
studio.cover = url;
}
Ok(submit_to_bilibili(&bilibili, &studio, submit).await?)
submit_to_bilibili(&bilibili, &studio, submit).await
}
-1
View File
@@ -51,7 +51,6 @@ fn spawn_and_monitor_sidecar(app_handle: tauri::AppHandle) -> Result<(), String>
let exe_dir = exe_path.parent().unwrap();
println!("[tauri] Sidecar directory: {}", exe_dir.display());
let (mut rx, child) = sidecar_command
.args(["-P", "19159"])
.current_dir(exe_dir)
// .env("PYTHONUTF8", "1")
.spawn()