fix: file export in web

This commit is contained in:
Xinrea
2025-04-30 21:33:09 +08:00
parent 4d3e069a81
commit 32b7e9c3c2
8 changed files with 60 additions and 25 deletions

View File

@@ -1,6 +1,8 @@
# Generated by Cargo # Generated by Cargo
# will have compiled files and executables # will have compiled files and executables
/target/ /target/
cache
output
tmps tmps
clips clips
data data

View File

@@ -54,6 +54,7 @@ url = "2.5.4"
custom-protocol = ["tauri/custom-protocol"] custom-protocol = ["tauri/custom-protocol"]
cuda = ["whisper-rs/cuda"] cuda = ["whisper-rs/cuda"]
headless = [] headless = []
default = ["headless"]
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
whisper-rs = { version = "0.14.2", default-features = false } whisper-rs = { version = "0.14.2", default-features = false }

View File

@@ -5,7 +5,7 @@ live_end_notify = true
clip_notify = true clip_notify = true
post_notify = true post_notify = true
auto_subtitle = false auto_subtitle = false
whisper_model = "" whisper_model = "./whisper_model.bin"
whisper_prompt = "这是一段中文 你们好" whisper_prompt = "这是一段中文 你们好"
clip_name_format = "[{room_id}][{live_id}][{title}][{created_at}].mp4" clip_name_format = "[{room_id}][{live_id}][{title}][{created_at}].mp4"

View File

@@ -38,7 +38,7 @@ fn default_auto_subtitle() -> bool {
} }
fn default_whisper_model() -> String { fn default_whisper_model() -> String {
"".to_string() "whisper_model.bin".to_string()
} }
fn default_whisper_prompt() -> String { fn default_whisper_prompt() -> String {

View File

@@ -1,3 +1,4 @@
use crate::danmu2ass;
use crate::database::record::RecordRow; use crate::database::record::RecordRow;
use crate::database::recorder::RecorderRow; use crate::database::recorder::RecorderRow;
use crate::recorder::danmu::DanmuEntry; use crate::recorder::danmu::DanmuEntry;
@@ -7,10 +8,11 @@ use crate::recorder_manager::RecorderList;
use crate::state::State; use crate::state::State;
use crate::state_type; use crate::state_type;
#[cfg(not(feature = "headless"))]
use auri::State as TauriState;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
#[cfg(not(feature = "headless"))]
use {crate::danmu2ass, tauri::State as TauriState};
#[cfg_attr(not(feature = "headless"), tauri::command)] #[cfg_attr(not(feature = "headless"), tauri::command)]
pub async fn get_recorder_list(state: state_type!()) -> Result<RecorderList, ()> { pub async fn get_recorder_list(state: state_type!()) -> Result<RecorderList, ()> {
@@ -158,6 +160,7 @@ pub async fn get_danmu_record(
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExportDanmuOptions { pub struct ExportDanmuOptions {
platform: String, platform: String,
room_id: u64, room_id: u64,
@@ -167,8 +170,7 @@ pub struct ExportDanmuOptions {
ass: bool, ass: bool,
} }
#[cfg(not(feature = "headless"))] #[cfg_attr(not(feature = "headless"), tauri::command)]
#[tauri::command]
pub async fn export_danmu( pub async fn export_danmu(
state: state_type!(), state: state_type!(),
options: ExportDanmuOptions, options: ExportDanmuOptions,

View File

@@ -17,10 +17,10 @@ use crate::{
}, },
message::{delete_message, get_messages, read_message}, message::{delete_message, get_messages, read_message},
recorder::{ recorder::{
add_recorder, delete_archive, fetch_hls, force_start, force_stop, get_archive, add_recorder, delete_archive, export_danmu, fetch_hls, force_start, force_stop,
get_archives, get_danmu_record, get_recent_record, get_recorder_list, get_room_info, get_archive, get_archives, get_danmu_record, get_recent_record, get_recorder_list,
get_today_record_count, get_total_length, remove_recorder, send_danmaku, get_room_info, get_today_record_count, get_total_length, remove_recorder, send_danmaku,
set_auto_start, set_auto_start, ExportDanmuOptions,
}, },
utils::{get_disk_info, DiskInfo}, utils::{get_disk_info, DiskInfo},
video::{ video::{
@@ -43,9 +43,9 @@ use crate::{
recorder_manager::{ClipRangeParams, RecorderList}, recorder_manager::{ClipRangeParams, RecorderList},
state::State, state::State,
}; };
use axum::response::sse; use axum::{extract::Query, response::sse};
use axum::{ use axum::{
extract::{Json, Path, Query}, extract::{Json, Path},
http::StatusCode, http::StatusCode,
response::{IntoResponse, Sse}, response::{IntoResponse, Sse},
routing::{get, post}, routing::{get, post},
@@ -834,6 +834,20 @@ async fn handler_fetch(
Ok((status, response_headers, body)) Ok((status, response_headers, body))
} }
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
struct ExportDanmuRequest {
options: ExportDanmuOptions,
}
async fn handler_export_danmu(
state: axum::extract::State<State>,
Json(params): Json<ExportDanmuRequest>,
) -> Result<Json<ApiResponse<String>>, ApiError> {
let result = export_danmu(state.0, params.options).await?;
Ok(Json(ApiResponse::success(result)))
}
async fn handler_hls( async fn handler_hls(
state: axum::extract::State<State>, state: axum::extract::State<State>,
Path(uri): Path<String>, Path(uri): Path<String>,
@@ -1138,6 +1152,7 @@ pub async fn start_api_server(state: State) {
"/api/encode_video_subtitle", "/api/encode_video_subtitle",
post(handler_encode_video_subtitle), post(handler_encode_video_subtitle),
) )
.route("/api/export_danmu", post(handler_export_danmu))
// Utils commands // Utils commands
.route("/api/get_disk_info", post(handler_get_disk_info)) .route("/api/get_disk_info", post(handler_get_disk_info))
.route("/api/fetch", post(handler_fetch)) .route("/api/fetch", post(handler_fetch))

View File

@@ -8,7 +8,7 @@
import type { Marker } from "./interface"; import type { Marker } from "./interface";
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import { Tooltip } from "flowbite-svelte"; import { Tooltip } from "flowbite-svelte";
import { invoke } from "../lib/invoker"; import { invoke, TAURI_ENV } from "../lib/invoker";
import { save } from "@tauri-apps/plugin-dialog"; import { save } from "@tauri-apps/plugin-dialog";
import type { RecordItem } from "./db"; import type { RecordItem } from "./db";
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
@@ -47,12 +47,19 @@
.split(" ")[0] .split(" ")[0]
.replaceAll("/", "-")}]${archive.title}.txt`; .replaceAll("/", "-")}]${archive.title}.txt`;
console.log("export to file", file_name); console.log("export to file", file_name);
const path = await save({ if (TAURI_ENV) {
title: "导出标记列表", const path = await save({
defaultPath: file_name, title: "导出标记列表",
}); defaultPath: file_name,
if (!path) return; });
await invoke("export_to_file", { fileName: path, content: r }); if (!path) return;
await invoke("export_to_file", { fileName: path, content: r });
} else {
const a = document.createElement("a");
a.href = "data:text/plain;charset=utf-8," + encodeURIComponent(r);
a.download = file_name;
a.click();
}
} }
</script> </script>

View File

@@ -814,12 +814,20 @@
})) as string; })) as string;
let file_name = `danmu_${room_id}_${live_id}.${ass ? "ass" : "txt"}`; let file_name = `danmu_${room_id}_${live_id}.${ass ? "ass" : "txt"}`;
const path = await save({ if (TAURI_ENV) {
title: "导出弹幕", const path = await save({
defaultPath: file_name, title: "导出弹幕",
}); defaultPath: file_name,
if (!path) return; });
await invoke("export_to_file", { fileName: path, content: assContent }); if (!path) return;
await invoke("export_to_file", { fileName: path, content: assContent });
} else {
const a = document.createElement("a");
a.href =
"data:text/plain;charset=utf-8," + encodeURIComponent(assContent);
a.download = file_name;
a.click();
}
} }
</script> </script>