mirror of
https://github.com/Xinrea/bili-shadowreplay.git
synced 2025-11-25 04:22:24 +08:00
fix: file export in web
This commit is contained in:
2
src-tauri/.gitignore
vendored
2
src-tauri/.gitignore
vendored
@@ -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
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user