mirror of
https://github.com/Xinrea/bili-shadowreplay.git
synced 2025-11-25 04:22:24 +08:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e3bb014644 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bili-shadowreplay",
|
"name": "bili-shadowreplay",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.7.0",
|
"version": "2.7.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -20,15 +20,31 @@ pub struct VideoRow {
|
|||||||
pub platform: String,
|
pub platform: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, serde::Serialize, sqlx::FromRow)]
|
||||||
|
pub struct VideoNoCover {
|
||||||
|
pub id: i64,
|
||||||
|
pub room_id: u64,
|
||||||
|
pub file: String,
|
||||||
|
pub length: i64,
|
||||||
|
pub size: i64,
|
||||||
|
pub status: i64,
|
||||||
|
pub bvid: String,
|
||||||
|
pub title: String,
|
||||||
|
pub desc: String,
|
||||||
|
pub tags: String,
|
||||||
|
pub area: i64,
|
||||||
|
pub created_at: String,
|
||||||
|
pub platform: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub async fn get_videos(&self, room_id: u64) -> Result<Vec<VideoRow>, DatabaseError> {
|
pub async fn get_videos(&self, room_id: u64) -> Result<Vec<VideoNoCover>, DatabaseError> {
|
||||||
let lock = self.db.read().await.clone().unwrap();
|
let lock = self.db.read().await.clone().unwrap();
|
||||||
Ok(
|
let videos = sqlx::query_as::<_, VideoNoCover>("SELECT * FROM videos WHERE room_id = $1;")
|
||||||
sqlx::query_as::<_, VideoRow>("SELECT * FROM videos WHERE room_id = $1;")
|
.bind(room_id as i64)
|
||||||
.bind(room_id as i64)
|
.fetch_all(&lock)
|
||||||
.fetch_all(&lock)
|
.await?;
|
||||||
.await?,
|
Ok(videos)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_video(&self, id: i64) -> Result<VideoRow, DatabaseError> {
|
pub async fn get_video(&self, id: i64) -> Result<VideoRow, DatabaseError> {
|
||||||
@@ -100,12 +116,21 @@ impl Database {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_videos(&self) -> Result<Vec<VideoRow>, DatabaseError> {
|
pub async fn get_all_videos(&self) -> Result<Vec<VideoNoCover>, DatabaseError> {
|
||||||
let lock = self.db.read().await.clone().unwrap();
|
let lock = self.db.read().await.clone().unwrap();
|
||||||
Ok(
|
let videos =
|
||||||
sqlx::query_as::<_, VideoRow>("SELECT * FROM videos ORDER BY created_at DESC;")
|
sqlx::query_as::<_, VideoNoCover>("SELECT * FROM videos ORDER BY created_at DESC;")
|
||||||
.fetch_all(&lock)
|
.fetch_all(&lock)
|
||||||
.await?,
|
.await?;
|
||||||
)
|
Ok(videos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_video_cover(&self, id: i64) -> Result<String, DatabaseError> {
|
||||||
|
let lock = self.db.read().await.clone().unwrap();
|
||||||
|
let video = sqlx::query_as::<_, VideoRow>("SELECT * FROM videos WHERE id = $1")
|
||||||
|
.bind(id)
|
||||||
|
.fetch_one(&lock)
|
||||||
|
.await?;
|
||||||
|
Ok(video.cover)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::database::task::TaskRow;
|
use crate::database::task::TaskRow;
|
||||||
use crate::database::video::VideoRow;
|
use crate::database::video::{VideoNoCover, VideoRow};
|
||||||
use crate::ffmpeg;
|
use crate::ffmpeg;
|
||||||
use crate::handlers::utils::get_disk_info_inner;
|
use crate::handlers::utils::get_disk_info_inner;
|
||||||
use crate::progress_reporter::{
|
use crate::progress_reporter::{
|
||||||
@@ -301,7 +301,7 @@ pub async fn get_video(state: state_type!(), id: i64) -> Result<VideoRow, String
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "gui", tauri::command)]
|
#[cfg_attr(feature = "gui", tauri::command)]
|
||||||
pub async fn get_videos(state: state_type!(), room_id: u64) -> Result<Vec<VideoRow>, String> {
|
pub async fn get_videos(state: state_type!(), room_id: u64) -> Result<Vec<VideoNoCover>, String> {
|
||||||
state
|
state
|
||||||
.db
|
.db
|
||||||
.get_videos(room_id)
|
.get_videos(room_id)
|
||||||
@@ -310,10 +310,19 @@ pub async fn get_videos(state: state_type!(), room_id: u64) -> Result<Vec<VideoR
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "gui", tauri::command)]
|
#[cfg_attr(feature = "gui", tauri::command)]
|
||||||
pub async fn get_all_videos(state: state_type!()) -> Result<Vec<VideoRow>, String> {
|
pub async fn get_all_videos(state: state_type!()) -> Result<Vec<VideoNoCover>, String> {
|
||||||
state.db.get_all_videos().await.map_err(|e| e.to_string())
|
state.db.get_all_videos().await.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "gui", tauri::command)]
|
||||||
|
pub async fn get_video_cover(state: state_type!(), id: i64) -> Result<String, String> {
|
||||||
|
state
|
||||||
|
.db
|
||||||
|
.get_video_cover(id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "gui", tauri::command)]
|
#[cfg_attr(feature = "gui", tauri::command)]
|
||||||
pub async fn delete_video(state: state_type!(), id: i64) -> Result<(), String> {
|
pub async fn delete_video(state: state_type!(), id: i64) -> Result<(), String> {
|
||||||
// get video info from dbus
|
// get video info from dbus
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ use std::fmt::{self, Display};
|
|||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
database::{
|
database::{
|
||||||
account::AccountRow, message::MessageRow, record::RecordRow, recorder::RecorderRow,
|
account::AccountRow,
|
||||||
task::TaskRow, video::VideoRow,
|
message::MessageRow,
|
||||||
|
record::RecordRow,
|
||||||
|
recorder::RecorderRow,
|
||||||
|
task::TaskRow,
|
||||||
|
video::{VideoNoCover, VideoRow},
|
||||||
},
|
},
|
||||||
handlers::{
|
handlers::{
|
||||||
account::{
|
account::{
|
||||||
@@ -27,8 +31,8 @@ use crate::{
|
|||||||
utils::{console_log, get_disk_info, DiskInfo},
|
utils::{console_log, get_disk_info, DiskInfo},
|
||||||
video::{
|
video::{
|
||||||
cancel, clip_range, delete_video, encode_video_subtitle, generate_video_subtitle,
|
cancel, clip_range, delete_video, encode_video_subtitle, generate_video_subtitle,
|
||||||
get_all_videos, get_video, get_video_subtitle, get_video_typelist, get_videos,
|
get_all_videos, get_video, get_video_cover, get_video_subtitle, get_video_typelist,
|
||||||
update_video_cover, update_video_subtitle, upload_procedure,
|
get_videos, update_video_cover, update_video_subtitle, upload_procedure,
|
||||||
},
|
},
|
||||||
AccountInfo,
|
AccountInfo,
|
||||||
},
|
},
|
||||||
@@ -656,21 +660,36 @@ async fn handler_get_video(
|
|||||||
struct GetVideosRequest {
|
struct GetVideosRequest {
|
||||||
room_id: u64,
|
room_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handler_get_videos(
|
async fn handler_get_videos(
|
||||||
state: axum::extract::State<State>,
|
state: axum::extract::State<State>,
|
||||||
Json(param): Json<GetVideosRequest>,
|
Json(param): Json<GetVideosRequest>,
|
||||||
) -> Result<Json<ApiResponse<Vec<VideoRow>>>, ApiError> {
|
) -> Result<Json<ApiResponse<Vec<VideoNoCover>>>, ApiError> {
|
||||||
let videos = get_videos(state.0, param.room_id).await?;
|
let videos = get_videos(state.0, param.room_id).await?;
|
||||||
Ok(Json(ApiResponse::success(videos)))
|
Ok(Json(ApiResponse::success(videos)))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handler_get_all_videos(
|
async fn handler_get_all_videos(
|
||||||
state: axum::extract::State<State>,
|
state: axum::extract::State<State>,
|
||||||
) -> Result<Json<ApiResponse<Vec<VideoRow>>>, ApiError> {
|
) -> Result<Json<ApiResponse<Vec<VideoNoCover>>>, ApiError> {
|
||||||
let videos = get_all_videos(state.0).await?;
|
let videos = get_all_videos(state.0).await?;
|
||||||
Ok(Json(ApiResponse::success(videos)))
|
Ok(Json(ApiResponse::success(videos)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct GetVideoCoverRequest {
|
||||||
|
id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handler_get_video_cover(
|
||||||
|
state: axum::extract::State<State>,
|
||||||
|
Json(param): Json<GetVideoCoverRequest>,
|
||||||
|
) -> Result<Json<ApiResponse<String>>, ApiError> {
|
||||||
|
let video_cover = get_video_cover(state.0, param.id).await?;
|
||||||
|
Ok(Json(ApiResponse::success(video_cover)))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct DeleteVideoRequest {
|
struct DeleteVideoRequest {
|
||||||
@@ -1236,6 +1255,7 @@ pub async fn start_api_server(state: State) {
|
|||||||
.route("/api/clip_range", post(handler_clip_range))
|
.route("/api/clip_range", post(handler_clip_range))
|
||||||
.route("/api/get_video", post(handler_get_video))
|
.route("/api/get_video", post(handler_get_video))
|
||||||
.route("/api/get_videos", post(handler_get_videos))
|
.route("/api/get_videos", post(handler_get_videos))
|
||||||
|
.route("/api/get_video_cover", post(handler_get_video_cover))
|
||||||
.route("/api/get_all_videos", post(handler_get_all_videos))
|
.route("/api/get_all_videos", post(handler_get_all_videos))
|
||||||
.route("/api/get_video_typelist", post(handler_get_video_typelist))
|
.route("/api/get_video_typelist", post(handler_get_video_typelist))
|
||||||
.route("/api/get_video_subtitle", post(handler_get_video_subtitle))
|
.route("/api/get_video_subtitle", post(handler_get_video_subtitle))
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ fn setup_invoke_handlers(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<
|
|||||||
crate::handlers::video::get_video,
|
crate::handlers::video::get_video,
|
||||||
crate::handlers::video::get_videos,
|
crate::handlers::video::get_videos,
|
||||||
crate::handlers::video::get_all_videos,
|
crate::handlers::video::get_all_videos,
|
||||||
|
crate::handlers::video::get_video_cover,
|
||||||
crate::handlers::video::delete_video,
|
crate::handlers::video::delete_video,
|
||||||
crate::handlers::video::get_video_typelist,
|
crate::handlers::video::get_video_typelist,
|
||||||
crate::handlers::video::update_video_cover,
|
crate::handlers::video::update_video_cover,
|
||||||
|
|||||||
@@ -213,15 +213,19 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function find_video(e) {
|
async function find_video(e) {
|
||||||
if (!e.target) {
|
if (!e.target) {
|
||||||
selected_video = null;
|
selected_video = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const id = parseInt(e.target.value);
|
const id = parseInt(e.target.value);
|
||||||
selected_video = videos.find((v) => {
|
let target_video = videos.find((v) => {
|
||||||
return v.value == id;
|
return v.value == id;
|
||||||
});
|
});
|
||||||
|
if (target_video) {
|
||||||
|
target_video.cover = await invoke("get_video_cover", { id: id });
|
||||||
|
}
|
||||||
|
selected_video = target_video;
|
||||||
console.log("video selected", videos, selected_video, e, id);
|
console.log("video selected", videos, selected_video, e, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@
|
|||||||
await loadVideos();
|
await loadVideos();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let cover_cache: Map<number, string> = new Map();
|
||||||
|
|
||||||
async function loadVideos() {
|
async function loadVideos() {
|
||||||
loading = true;
|
loading = true;
|
||||||
try {
|
try {
|
||||||
@@ -44,6 +46,16 @@
|
|||||||
const roomIdsSet = new Set<number>();
|
const roomIdsSet = new Set<number>();
|
||||||
const platformsSet = new Set<string>();
|
const platformsSet = new Set<string>();
|
||||||
const tempVideos = await invoke<VideoItem[]>("get_all_videos");
|
const tempVideos = await invoke<VideoItem[]>("get_all_videos");
|
||||||
|
for (const video of tempVideos) {
|
||||||
|
if (cover_cache.has(video.id)) {
|
||||||
|
video.cover = cover_cache.get(video.id) || "";
|
||||||
|
} else {
|
||||||
|
video.cover = await invoke<string>("get_video_cover", {
|
||||||
|
id: video.id,
|
||||||
|
});
|
||||||
|
cover_cache.set(video.id, video.cover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const video of tempVideos) {
|
for (const video of tempVideos) {
|
||||||
roomIdsSet.add(video.room_id);
|
roomIdsSet.add(video.room_id);
|
||||||
@@ -536,15 +548,11 @@
|
|||||||
<div
|
<div
|
||||||
class="w-12 h-8 rounded-lg overflow-hidden bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0"
|
class="w-12 h-8 rounded-lg overflow-hidden bg-gray-100 dark:bg-gray-700 flex items-center justify-center flex-shrink-0"
|
||||||
>
|
>
|
||||||
{#if video.cover}
|
<img
|
||||||
<img
|
src={video.cover}
|
||||||
src={video.cover}
|
alt="封面"
|
||||||
alt="封面"
|
class="w-full h-full object-cover"
|
||||||
class="w-full h-full object-cover"
|
/>
|
||||||
/>
|
|
||||||
{:else}
|
|
||||||
<Video class="w-4 h-4 text-gray-400" />
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-1 w-64">
|
<div class="min-w-0 flex-1 w-64">
|
||||||
<p
|
<p
|
||||||
|
|||||||
Reference in New Issue
Block a user