diff --git a/package.json b/package.json index 2606646..39665cb 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "lazysizes": "^5.3.2", "naive-ui": "^2.42.0", "pinia": "^3.0.3", + "virtua": "^0.48.6", "vue": "^3.5.13", "vue-draggable-plus": "^0.6.0", "z-vue-scan": "^0.0.35" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 25751cb..82e2151 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: pinia: specifier: ^3.0.3 version: 3.0.3(typescript@5.6.3)(vue@3.5.17(typescript@5.6.3)) + virtua: + specifier: ^0.48.6 + version: 0.48.6(vue@3.5.17(typescript@5.6.3)) vue: specifier: ^3.5.13 version: 3.5.17(typescript@5.6.3) @@ -1843,6 +1846,26 @@ packages: peerDependencies: vue: ^3.0.11 + virtua@0.48.6: + resolution: {integrity: sha512-Cl4uMvMV5c9RuOy9zhkFMYwx/V4YLBMYLRSWkO8J46opQZ3P7KMq0CqCVOOAKUckjl/r//D2jWTBGYWzmgtzrQ==} + peerDependencies: + react: '>=16.14.0' + react-dom: '>=16.14.0' + solid-js: '>=1.0' + svelte: '>=5.0' + vue: '>=3.2' + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + solid-js: + optional: true + svelte: + optional: true + vue: + optional: true + vite-hot-client@2.1.0: resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} peerDependencies: @@ -3816,6 +3839,10 @@ snapshots: evtd: 0.2.4 vue: 3.5.17(typescript@5.6.3) + virtua@0.48.6(vue@3.5.17(typescript@5.6.3)): + optionalDependencies: + vue: 3.5.17(typescript@5.6.3) + vite-hot-client@2.1.0(vite@6.3.5(jiti@2.4.2)): dependencies: vite: 6.3.5(jiti@2.4.2) diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 506c291..358ecdd 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -1,3 +1,8 @@ +use std::{ + fs::File, + io::{BufRead, BufReader}, +}; + use eyre::WrapErr; use parking_lot::RwLock; use tauri::AppHandle; @@ -25,6 +30,7 @@ use crate::{ get_normal_info_params::GetNormalInfoParams, get_user_video_info_params::GetUserVideoInfoParams, history_info::HistoryInfo, + log_metadata::LogMetadata, normal_info::NormalInfo, qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, @@ -467,3 +473,36 @@ pub async fn get_available_media_formats( Ok(result) } + +#[allow(clippy::needless_pass_by_value)] +#[tauri::command(async)] +#[specta::specta] +#[instrument(level = "error", skip_all, fields(path = path))] +pub fn open_log_file(path: &str) -> CommandResult> { + let log_file = File::open(path).map_err(|err| CommandError::from("打开日志文件失败", err))?; + + let reader = BufReader::new(log_file); + + let mut logs = Vec::new(); + let mut line_num = 0; + + for line_result in reader.lines() { + line_num += 1; + + let line = line_result + .wrap_err(format!("读取日志文件的第`{line_num}`行失败")) + .map_err(|err| CommandError::from("打开日志文件失败", err))?; + + if line.trim().is_empty() { + continue; + } + + let log: LogMetadata = serde_json::from_str(&line) + .wrap_err(format!("将日志文件的第`{line_num}`行解析为LogMetadata失败")) + .map_err(|err| CommandError::from("打开日志文件失败", err))?; + + logs.push(log); + } + + Ok(logs) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index e80eaba..0d2ab22 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -30,10 +30,10 @@ use tauri::{Manager, Wry}; use crate::{ bili_client::BiliClient, + commands::open_log_file, downloader::download_manager::DownloadManager, errors::install_custom_eyre_handler, events::{DownloadEvent, LogEvent}, - types::log_metadata::LogMetadata, }; fn generate_context() -> tauri::Context { @@ -72,9 +72,9 @@ pub fn run() { show_path_in_file_manager, get_skip_segments, get_available_media_formats, + open_log_file, ]) - .events(tauri_specta::collect_events![LogEvent, DownloadEvent]) - .typ::(); + .events(tauri_specta::collect_events![LogEvent, DownloadEvent]); #[cfg(debug_assertions)] builder diff --git a/src-tauri/src/logger.rs b/src-tauri/src/logger.rs index 73039df..f82dfdb 100644 --- a/src-tauri/src/logger.rs +++ b/src-tauri/src/logger.rs @@ -1,5 +1,9 @@ use std::{io::Write, sync::OnceLock}; +use crate::{ + events::LogEvent, + extensions::{AppHandleExt, EyreReportToMessage}, +}; use eyre::{OptionExt, WrapErr}; use notify::{RecommendedWatcher, Watcher}; use tauri::{AppHandle, Manager}; @@ -13,17 +17,12 @@ use tracing_error::ErrorLayer; use tracing_subscriber::{ Layer, Registry, filter::{FilterExt, Targets, filter_fn}, - fmt::{MakeWriter, layer, time::LocalTime}, + fmt::{MakeWriter, format::JsonFields, layer, time::LocalTime}, layer::SubscriberExt, registry::LookupSpan, util::SubscriberInitExt, }; -use crate::{ - events::LogEvent, - extensions::{AppHandleExt, EyreReportToMessage}, -}; - struct LogEventWriter { app: AppHandle, } @@ -73,7 +72,8 @@ pub fn init(app: &AppHandle) -> eyre::Result<()> { .with_writer(std::io::stdout) .with_timer(LocalTime::rfc_3339()) .with_file(true) - .with_line_number(true); + .with_line_number(true) + .pretty(); // 发送到前端 let log_event_factory = LogEventWriterFactory { app: app.clone() }; let log_event_layer = layer() @@ -92,7 +92,7 @@ pub fn init(app: &AppHandle) -> eyre::Result<()> { .with(reloadable_file_layer) .with(console_layer) .with(log_event_layer) - .with(ErrorLayer::default()) + .with(ErrorLayer::new(JsonFields::default())) .init(); GUARD.get_or_init(|| parking_lot::Mutex::new(guard)); @@ -138,7 +138,8 @@ where .with_timer(LocalTime::rfc_3339()) .with_ansi(false) .with_file(true) - .with_line_number(true); + .with_line_number(true) + .json(); return Ok((Box::new(sink_layer), None)); } let logs_dir = logs_dir(app).wrap_err("获取日志目录失败")?; @@ -154,7 +155,8 @@ where .with_timer(LocalTime::rfc_3339()) .with_ansi(false) .with_file(true) - .with_line_number(true); + .with_line_number(true) + .json(); Ok((Box::new(file_layer), Some(guard))) } diff --git a/src/bindings.ts b/src/bindings.ts index b5b219a..286bf38 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -169,6 +169,14 @@ async getAvailableMediaFormats(params: GetAvailableMediaFormatsParams) : Promise if(e instanceof Error) throw e; else return { status: "error", error: e as any }; } +}, +async openLogFile(path: string) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("open_log_file", { path }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} } } diff --git a/src/dialogs/LogDialog.vue b/src/dialogs/LogDialog.vue index b8f8761..446924f 100644 --- a/src/dialogs/LogDialog.vue +++ b/src/dialogs/LogDialog.vue @@ -1,24 +1,54 @@