Add streamer pause/resume feature and UI updates
Introduces a pause/resume button for streamers in the UI and backend, including API endpoint and status handling. Refactors streamer status display, updates related TypeScript interfaces, and improves modal forms. Also updates @douyinfe/semi-ui and related dependencies to v2.86.0, and bumps workspace version to 1.1.10. Minor logging and trait changes in Rust backend for improved clarity and error handling.
This commit is contained in:
1 parent
d4b6a5fb22
commit
92b5a30df5
21 files changed
+270
-148
No files matched your search
Generated
+2
-2
@@ -395,7 +395,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "biliup-cli"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-trait",
|
||||
@@ -3641,7 +3641,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||
|
||||
[[package]]
|
||||
name = "stream-gears"
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
dependencies = [
|
||||
"async-channel",
|
||||
"async-trait",
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ members = [
|
||||
resolver = "3"
|
||||
|
||||
[workspace.package]
|
||||
version = "1.1.9"
|
||||
version = "1.1.10"
|
||||
|
||||
[workspace.dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
'use client'
|
||||
import {
|
||||
Layout,
|
||||
Nav,
|
||||
Button,
|
||||
Tag,
|
||||
Typography,
|
||||
Popconfirm,
|
||||
Notification,
|
||||
Card,
|
||||
Layout,
|
||||
Nav,
|
||||
Button,
|
||||
Tag,
|
||||
Typography,
|
||||
Popconfirm,
|
||||
Notification,
|
||||
Card, Dropdown,
|
||||
} from '@douyinfe/semi-ui'
|
||||
import {
|
||||
IconHelpCircle,
|
||||
IconPlusCircle,
|
||||
IconVideoListStroked,
|
||||
IconEdit2Stroked,
|
||||
IconDeleteStroked,
|
||||
IconWrench,
|
||||
IconHelpCircle,
|
||||
IconPlusCircle,
|
||||
IconVideoListStroked,
|
||||
IconEdit2Stroked,
|
||||
IconDeleteStroked,
|
||||
IconWrench, IconTreeTriangleDown, IconPause, IconPlay,
|
||||
} from '@douyinfe/semi-icons'
|
||||
import { List, ButtonGroup } from '@douyinfe/semi-ui'
|
||||
import React, { useState } from 'react'
|
||||
@@ -24,6 +24,7 @@ import TemplateModal from '../../ui/TemplateModal'
|
||||
import OverrideModal from '../../ui/OverrideModal'
|
||||
import { LiveStreamerEntity, put, requestDelete, sendRequest } from '../../lib/api-streamer'
|
||||
import useSWRMutation from 'swr/mutation'
|
||||
import {PauseButton} from "@/app/ui/StreamerActions/PauseButton";
|
||||
|
||||
export default function Home() {
|
||||
const { Header, Content } = Layout
|
||||
@@ -54,25 +55,28 @@ export default function Home() {
|
||||
return values
|
||||
}
|
||||
const data: LiveStreamerEntity[] | undefined = streamers?.map(live => {
|
||||
let status
|
||||
let statusTag
|
||||
switch (live.status) {
|
||||
case 'Working':
|
||||
status = <Tag color="red">直播中</Tag>
|
||||
statusTag = <Tag color="red">直播中</Tag>
|
||||
break
|
||||
case 'Idle':
|
||||
status = <Tag color="green">空闲</Tag>
|
||||
statusTag = <Tag color="green">空闲</Tag>
|
||||
break
|
||||
case 'Pending':
|
||||
status = <Tag color="grey">未知</Tag>
|
||||
statusTag = <Tag color="grey">等待下载</Tag>
|
||||
break
|
||||
case 'Inspecting':
|
||||
status = <Tag color="indigo">检测/上传中</Tag>
|
||||
statusTag = <Tag color="indigo">检测/上传中</Tag>
|
||||
break
|
||||
case 'OutOfSchedule':
|
||||
status = <Tag color="green">非录播时间</Tag>
|
||||
statusTag = <Tag color="green">非录播时间</Tag>
|
||||
break
|
||||
case 'Pause':
|
||||
statusTag = <Tag color="pink">暂停中</Tag>
|
||||
break
|
||||
}
|
||||
return { ...handleEntityPostprocessor(live), status }
|
||||
return { ...handleEntityPostprocessor(live), statusTag }
|
||||
})
|
||||
|
||||
const handleOk = async (values: any) => {
|
||||
@@ -96,6 +100,7 @@ export default function Home() {
|
||||
const handleUpdate = async (values: any) => {
|
||||
console.log(values);
|
||||
delete values.status
|
||||
delete values.statusTag
|
||||
if (values?.postprocessor) {
|
||||
values.postprocessor = values.postprocessor.map(
|
||||
({ cmd, value }: { cmd: string; value: string }) => (cmd === 'rm' ? 'rm' : { [cmd]: value })
|
||||
@@ -112,6 +117,7 @@ export default function Home() {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header
|
||||
@@ -214,7 +220,7 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
>
|
||||
<div style={{ position: 'absolute', right: 20, top: 9 }}>{item.status}</div>
|
||||
<div style={{ position: 'absolute', right: 20, top: 9 }}>{item.statusTag}</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<h3
|
||||
style={{
|
||||
@@ -248,9 +254,7 @@ export default function Home() {
|
||||
<Button theme="borderless" icon={<IconEdit2Stroked />}></Button>
|
||||
</TemplateModal>
|
||||
<span className="semi-button-group-line semi-button-group-line-borderless semi-button-group-line-primary"></span>
|
||||
<OverrideModal onOk={handleUpdate} entity={item}>
|
||||
<Button theme="borderless" icon={<IconWrench />}></Button>
|
||||
</OverrideModal>
|
||||
<PauseButton streamer={item}/>
|
||||
<span className="semi-button-group-line semi-button-group-line-borderless semi-button-group-line-primary"></span>
|
||||
<Popconfirm
|
||||
title="确定是否要删除?"
|
||||
@@ -260,6 +264,18 @@ export default function Home() {
|
||||
>
|
||||
<Button theme="borderless" icon={<IconDeleteStroked />}></Button>
|
||||
</Popconfirm>
|
||||
<span className="semi-button-group-line semi-button-group-line-borderless semi-button-group-line-primary"></span>
|
||||
<Dropdown render={
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item>
|
||||
<OverrideModal onOk={handleUpdate} entity={item}>
|
||||
<Button theme="borderless" icon={<IconWrench />}></Button>
|
||||
</OverrideModal>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
} trigger="click" position="bottomRight">
|
||||
<Button theme="borderless" type="primary" icon={<IconTreeTriangleDown />}></Button>
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -100,7 +100,8 @@ export interface LiveStreamerEntity {
|
||||
split_time?: number;
|
||||
split_size?: number;
|
||||
upload_id?: number;
|
||||
status?: string | React.ReactNode;
|
||||
status?: string;
|
||||
statusTag?: React.ReactNode;
|
||||
format?: string;
|
||||
time_range?: string | Date[];
|
||||
excluded_keywords?: string[];
|
||||
|
||||
@@ -222,12 +222,6 @@ const OverrideModal: React.FC<TemplateModalProps> = ({ children, entity, onOk })
|
||||
stopValidateWithError={true}
|
||||
/>
|
||||
|
||||
<Form.Input
|
||||
field="filename_prefix"
|
||||
label="文件名模板(filename_prefix)"
|
||||
placeholder="{streamer}%Y-%m-%dT%H_%M_%S"
|
||||
/>
|
||||
|
||||
<Form.InputNumber
|
||||
field="filtering_threshold"
|
||||
label="碎片过滤(filtering_threshold)"
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useSWRConfig } from 'swr';
|
||||
import useSWRMutation from 'swr/mutation';
|
||||
import {Button} from "@douyinfe/semi-ui";
|
||||
import {IconPause, IconPlay} from "@douyinfe/semi-icons";
|
||||
import {API_BASE, LiveStreamerEntity} from "@/app/lib/api-streamer";
|
||||
|
||||
interface PauseButtonProps {
|
||||
streamer: LiveStreamerEntity;
|
||||
onSuccess?: () => void;
|
||||
onError?: (error: Error) => void;
|
||||
}
|
||||
|
||||
// 暂停主播
|
||||
export const pauseStreamer = async (url: string, ) => {
|
||||
const response = await fetch(API_BASE + url,
|
||||
{
|
||||
method: 'PUT',
|
||||
// headers: {'Content-Type': 'application/json'},
|
||||
}
|
||||
);
|
||||
return response;
|
||||
};
|
||||
|
||||
export const PauseButton: React.FC<PauseButtonProps> = ({
|
||||
streamer,
|
||||
onSuccess,
|
||||
onError
|
||||
}) => {
|
||||
const { mutate } = useSWRConfig();
|
||||
|
||||
const { trigger: pauseTrigger } = useSWRMutation(
|
||||
`/v1/streamers/${streamer.id}/pause`,
|
||||
pauseStreamer
|
||||
);
|
||||
|
||||
const handlePause = async () => {
|
||||
try {
|
||||
await pauseTrigger();
|
||||
// 重新加载列表数据
|
||||
await mutate('/v1/streamers');
|
||||
onSuccess?.();
|
||||
} catch (error) {
|
||||
console.error('暂停失败:', error);
|
||||
onError?.(error as Error);
|
||||
}
|
||||
};
|
||||
console.log("????????????", streamer.status)
|
||||
return (
|
||||
<Button onClick={handlePause} icon={streamer.status === 'Pause'? <IconPlay />: <IconPause />} theme="borderless" aria-label="暂停" />
|
||||
);
|
||||
};
|
||||
@@ -155,6 +155,12 @@ const TemplateModal: React.FC<TemplateModalProps> = ({ children, entity, onOk })
|
||||
rules={[{ required: true, message }]}
|
||||
/>
|
||||
|
||||
<Form.Input
|
||||
field="filename_prefix"
|
||||
label="文件名模板(可选)"
|
||||
placeholder="{streamer}%Y-%m-%dT%H_%M_%S"
|
||||
/>
|
||||
|
||||
<Form.Select
|
||||
showClear
|
||||
field="upload_streamers_id"
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::server::config::Config;
|
||||
use crate::server::core::download_manager::ActorHandle;
|
||||
use crate::server::errors::{report_to_response, AppError};
|
||||
use crate::server::infrastructure::connection_pool::ConnectionPool;
|
||||
use crate::server::infrastructure::context::Worker;
|
||||
use crate::server::infrastructure::context::{Worker, WorkerStatus};
|
||||
use crate::server::infrastructure::dto::LiveStreamerResponse;
|
||||
use crate::server::infrastructure::models::{
|
||||
Configuration, FileItem, InsertConfiguration,
|
||||
@@ -22,7 +22,7 @@ use axum::response::{IntoResponse, Response};
|
||||
use axum::{debug_handler, Json};
|
||||
use biliup::credential::Credential;
|
||||
use clap::ValueEnum;
|
||||
use error_stack::{Report, ResultExt};
|
||||
use error_stack::{bail, Report, ResultExt};
|
||||
use ormlite::{Insert, Model};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
@@ -36,6 +36,7 @@ use tokio::fs;
|
||||
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, BufReader};
|
||||
use tokio::time::{interval, MissedTickBehavior};
|
||||
use tracing::{debug, error, info};
|
||||
use crate::server::core::downloader::Downloader;
|
||||
use crate::server::infrastructure::models::live_streamer::{InsertLiveStreamer, LiveStreamer};
|
||||
use crate::server::infrastructure::models::upload_streamer::{InsertUploadStreamer, UploadStreamer};
|
||||
|
||||
@@ -94,7 +95,7 @@ pub async fn post_streamers_endpoint(
|
||||
.await
|
||||
.map_err(report_to_response)?;
|
||||
|
||||
info!(workers=?service_register.workers, "successfully inserted new live streamers");
|
||||
info!(workers=?live_streamers, "successfully inserted new live streamers");
|
||||
Ok(Json(live_streamers))
|
||||
}
|
||||
|
||||
@@ -141,10 +142,60 @@ pub async fn delete_streamers_endpoint(
|
||||
.await
|
||||
.map_err(report_to_response)?;
|
||||
let live_streamers = del_streamer(&pool, id).await.map_err(report_to_response)?;
|
||||
info!(workers=?service_register.workers, "successfully inserted new live streamers");
|
||||
info!(workers=?live_streamers, "successfully inserted new live streamers");
|
||||
Ok(Json(live_streamers))
|
||||
}
|
||||
|
||||
#[axum::debug_handler(state = ServiceRegister)]
|
||||
pub async fn pause_streamers_endpoint(
|
||||
State(service_register): State<ServiceRegister>,
|
||||
State(pool): State<ConnectionPool>,
|
||||
State(workers): State<Arc<RwLock<Vec<Arc<Worker>>>>>,
|
||||
Path(id): Path<i64>,
|
||||
) -> Result<Json<()>, Response> {
|
||||
let option = workers
|
||||
.read()
|
||||
.unwrap()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.find(|worker| {
|
||||
worker.live_streamer.id == id
|
||||
});
|
||||
if let Some(w) = option {
|
||||
let manager = service_register.get_manager(&w.live_streamer.url).ok_or(AppError::Unknown).map_err(report_to_response)?;
|
||||
let monitor = manager.ensure_monitor(pool.clone());
|
||||
let worker_status = w.downloader_status.read().unwrap().clone();
|
||||
let d = match worker_status {
|
||||
WorkerStatus::Working(d) => {
|
||||
*w.downloader_status.write().unwrap() = WorkerStatus::Pause;
|
||||
Some(d.clone())
|
||||
},
|
||||
WorkerStatus::Pending => {
|
||||
monitor.rooms_handle.toggle(w.clone()).await;
|
||||
*w.downloader_status.write().unwrap() = WorkerStatus::Pause;
|
||||
None
|
||||
}
|
||||
WorkerStatus::Idle => {
|
||||
monitor.rooms_handle.toggle(w.clone()).await;
|
||||
*w.downloader_status.write().unwrap() = WorkerStatus::Pause;
|
||||
None
|
||||
}
|
||||
WorkerStatus::Pause => {
|
||||
monitor.rooms_handle.toggle(w.clone()).await;
|
||||
*w.downloader_status.write().unwrap() = WorkerStatus::Idle;
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(d) = d {
|
||||
d.stop().await.map_err(report_to_response)?;
|
||||
}
|
||||
info!(workers=?service_register.workers, "successfully pause live streamers");
|
||||
}
|
||||
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
pub async fn get_configuration(
|
||||
State(config): State<Arc<RwLock<Config>>>,
|
||||
) -> Result<Json<Config>, Response> {
|
||||
|
||||
@@ -57,10 +57,6 @@ impl DownloadGuard {
|
||||
|
||||
impl Drop for DownloadGuard {
|
||||
fn drop(&mut self) {
|
||||
// 确保状态更新和资源清理
|
||||
self.worker
|
||||
.change_status(Stage::Download, WorkerStatus::Idle);
|
||||
|
||||
// 异步清理任务
|
||||
let danmaku = self.danmaku_client.clone();
|
||||
let rooms_handle = self.rooms_handle.clone();
|
||||
@@ -72,7 +68,13 @@ impl Drop for DownloadGuard {
|
||||
error!("Error stopping danmaku client: {}", e);
|
||||
}
|
||||
}
|
||||
rooms_handle.toggle(worker).await
|
||||
if let WorkerStatus::Pause = *worker.downloader_status.read().unwrap() {
|
||||
} else {
|
||||
// 确保状态更新和资源清理
|
||||
worker
|
||||
.change_status(Stage::Download, WorkerStatus::Idle);
|
||||
rooms_handle.toggle(worker).await;
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -318,7 +320,7 @@ impl DownloadTask {
|
||||
.unwrap_or_else(|| stream_info.suffix.clone());
|
||||
// 创建录制器
|
||||
let recorder = Recorder::new(
|
||||
streamer.filename_prefix,
|
||||
streamer.filename_prefix.or(config.filename_prefix.clone()),
|
||||
&streamer.remark,
|
||||
&stream_info.streamer_info.title,
|
||||
&suffix,
|
||||
|
||||
@@ -151,13 +151,13 @@ async fn upload_single_file(file_path: &Path, context: &UploadContext) -> AppRes
|
||||
let video_path = file_path;
|
||||
|
||||
info!(
|
||||
"{:?}",
|
||||
"开始上传文件:{:?}",
|
||||
video_path
|
||||
.canonicalize()
|
||||
.change_context(AppError::Unknown)?
|
||||
.to_str()
|
||||
);
|
||||
info!("{line:?}");
|
||||
info!("线路选择:{line:?}");
|
||||
let video_file = VideoFile::new(video_path).change_context(AppError::Unknown)?;
|
||||
let total_size = video_file.total_size;
|
||||
let file_name = video_file.file_name.clone();
|
||||
@@ -274,11 +274,11 @@ pub async fn execute_postprocessor(video_paths: Vec<PathBuf>, ctx: &Context) ->
|
||||
let result = InsertFileItem {
|
||||
file,
|
||||
streamer_info_id: 0,
|
||||
}.insert(&pool).await;
|
||||
}.insert(&pool).await;
|
||||
info!(result=?result);
|
||||
});
|
||||
|
||||
p.as_path()
|
||||
|
||||
p.as_path()
|
||||
}).collect();
|
||||
process_video(&paths, &processor).await?;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ pub trait Downloader: Send + Sync {
|
||||
) -> AppResult<DownloadStatus>;
|
||||
|
||||
/// 停止下载
|
||||
async fn stop(&self) -> Result<(), Box<dyn std::error::Error>>;
|
||||
async fn stop(&self) -> AppResult<()>;
|
||||
|
||||
/// 滚动保存(用于弹幕等)
|
||||
///
|
||||
|
||||
@@ -400,10 +400,10 @@ impl Downloader for FfmpegDownloader {
|
||||
}
|
||||
}
|
||||
|
||||
async fn stop(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn stop(&self) -> AppResult<()> {
|
||||
let mut handle = self.process_handle.write().await;
|
||||
if let Some(child) = &mut *handle {
|
||||
child.kill().await?;
|
||||
child.kill().await.change_context(AppError::Unknown)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AppError::Custom("Process handle not found".to_string()).into())
|
||||
|
||||
@@ -149,7 +149,7 @@ impl Downloader for StreamGears {
|
||||
}
|
||||
|
||||
/// 停止下载
|
||||
async fn stop(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn stop(&self) -> AppResult<()> {
|
||||
// 仅发出取消信号并更新状态
|
||||
// 如果底层下载函数不支持取消,这里不能真正中断正在进行的下载
|
||||
self.token.cancel();
|
||||
|
||||
@@ -31,9 +31,8 @@ async fn start_client(
|
||||
loop {
|
||||
// 获取下一个要检查的房间
|
||||
if let Some(room) = rooms_handle.next().await {
|
||||
let ulr = room.get_streamer().url;
|
||||
let url = room.get_streamer().url;
|
||||
interval = room.get_config().event_loop_interval;
|
||||
info!("[{platform_name}] room: {ulr}");
|
||||
let mut ctx = Context::new(room.clone(), Default::default(), pool.clone());
|
||||
// 检查直播状态
|
||||
match plugin.check_status(&mut ctx).await.unwrap() {
|
||||
@@ -52,7 +51,7 @@ async fn start_client(
|
||||
continue
|
||||
}
|
||||
};
|
||||
info!("room: {ulr} is live -> {:?}", stream_info);
|
||||
info!(url=url, "room: is live -> 开播了");
|
||||
// 更新状态为等待中
|
||||
room.change_status(Stage::Download, WorkerStatus::Pending);
|
||||
stream_info.streamer_info = insert;
|
||||
|
||||
@@ -147,7 +147,7 @@ pub enum Stage {
|
||||
}
|
||||
|
||||
/// 工作器状态枚举
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Clone)]
|
||||
pub enum WorkerStatus {
|
||||
/// 正在工作
|
||||
Working(Arc<dyn Downloader>),
|
||||
@@ -156,6 +156,8 @@ pub enum WorkerStatus {
|
||||
/// 空闲状态(默认)
|
||||
#[default]
|
||||
Idle,
|
||||
/// 下载暂停中
|
||||
Pause
|
||||
}
|
||||
|
||||
// 简单 Debug:打印状态名,忽略内部 downloader
|
||||
@@ -165,6 +167,7 @@ impl fmt::Debug for WorkerStatus {
|
||||
WorkerStatus::Working(_) => "Working",
|
||||
WorkerStatus::Pending => "Pending",
|
||||
WorkerStatus::Idle => "Idle",
|
||||
WorkerStatus::Pause => "Pause"
|
||||
};
|
||||
f.write_str(name)
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
use crate::server::api::bilibili_endpoints::{
|
||||
archive_pre_endpoint, get_myinfo_endpoint, get_proxy_endpoint,
|
||||
};
|
||||
use crate::server::api::endpoints::{
|
||||
add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint,
|
||||
delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode, get_status,
|
||||
get_streamer_info, get_streamers_endpoint, get_upload_streamer_endpoint,
|
||||
get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode,
|
||||
post_streamers_endpoint, post_uploads, put_configuration, put_streamers_endpoint,
|
||||
};
|
||||
use crate::server::api::endpoints::{add_upload_streamer_endpoint, add_user_endpoint, delete_streamers_endpoint, delete_template_endpoint, delete_user_endpoint, get_configuration, get_qrcode, get_status, get_streamer_info, get_streamers_endpoint, get_upload_streamer_endpoint, get_upload_streamers_endpoint, get_users_endpoint, get_videos, login_by_qrcode, pause_streamers_endpoint, post_streamers_endpoint, post_uploads, put_configuration, put_streamers_endpoint};
|
||||
use crate::server::infrastructure::service_register::ServiceRegister;
|
||||
use axum::Router;
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::{delete, get, post};
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use tower::ServiceExt;
|
||||
use tower_http::services::ServeFile;
|
||||
/// 创建应用程序路由
|
||||
@@ -27,6 +21,7 @@ pub fn router(service_register: ServiceRegister) -> Router<()> {
|
||||
.put(put_streamers_endpoint), // 更新主播
|
||||
)
|
||||
.route("/v1/streamers/{id}", delete(delete_streamers_endpoint)) // 删除主播
|
||||
.route("/v1/streamers/{id}/pause", put(pause_streamers_endpoint)) // 删除主播
|
||||
// 配置管理路由
|
||||
.route(
|
||||
"/v1/configuration",
|
||||
|
||||
@@ -15,7 +15,7 @@ use rsa::{Pkcs1v15Encrypt, RsaPublicKey, pkcs8::DecodePublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Value, json};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use tracing::info;
|
||||
use tracing::{debug, info};
|
||||
use url::Url;
|
||||
|
||||
// const APP_KEY: &str = "ae57252b0c09105d";
|
||||
@@ -63,7 +63,7 @@ pub fn bilibili_from_cookies(file: impl AsRef<Path>, proxy: Option<&str>) -> Res
|
||||
pub fn bilibili_from_info(login_info: LoginInfo, proxy: Option<&str>) -> Result<BiliBili> {
|
||||
let client = Credential::new(proxy);
|
||||
client.set_cookie(&login_info.cookie_info);
|
||||
info!("通过cookie登录");
|
||||
debug!("通过cookie登录");
|
||||
Ok(BiliBili {
|
||||
client: client.0.client,
|
||||
login_info,
|
||||
@@ -85,7 +85,7 @@ pub async fn login_by_cookies(file: impl AsRef<Path>, proxy: Option<&str>) -> Re
|
||||
serde_json::to_writer_pretty(std::io::BufWriter::new(&file), &new_info)?;
|
||||
bilibili_from_info(new_info, proxy)
|
||||
} else {
|
||||
info!("无需更新cookie");
|
||||
debug!("无需更新cookie");
|
||||
bilibili_from_info(login_info, proxy)
|
||||
}
|
||||
}
|
||||
@@ -174,7 +174,7 @@ impl Credential {
|
||||
_ => return Err(Kind::Custom(response.to_string())),
|
||||
};
|
||||
|
||||
info!("验证cookie");
|
||||
debug!("验证cookie");
|
||||
Ok(refresh)
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ impl Downloader for DanmakuClient {
|
||||
}
|
||||
|
||||
/// Stops the danmaku recording
|
||||
async fn stop(&self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn stop(&self) -> AppResult<()> {
|
||||
let py_client = self.py_client.clone();
|
||||
// Call the DanmakuClient's stop method (not the trait method)
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -59,7 +59,7 @@ impl Downloader for DanmakuClient {
|
||||
Ok::<_, PyErr>(())
|
||||
})
|
||||
})
|
||||
.await??;
|
||||
.await.change_context(AppError::Unknown)?.change_context(AppError::Unknown)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ impl DownloadPlugin for PyPlugin {
|
||||
}
|
||||
|
||||
async fn check_status(&self, ctx: &mut Context) -> Result<StreamStatus, Report<AppError>> {
|
||||
info!("Checking status");
|
||||
match call_via_threads(self.plugin.clone(), &ctx.worker.live_streamer.url)
|
||||
.await
|
||||
.change_context(AppError::Unknown)?
|
||||
@@ -250,7 +249,7 @@ pub fn stream_info_from_py(
|
||||
// lt.to_object(py)
|
||||
// }
|
||||
// };self.update_headers(self.stream_headers)
|
||||
|
||||
|
||||
Ok((
|
||||
StreamInfoExt {
|
||||
streamer_info: StreamerInfo {
|
||||
|
||||
Generated
+77
-73
@@ -8,7 +8,7 @@
|
||||
"name": "my-app",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@douyinfe/semi-ui": "^2.75.0",
|
||||
"@douyinfe/semi-ui": "^2.86.0",
|
||||
"artplayer": "^5.2.1",
|
||||
"mpegts.js": "^1.8.0",
|
||||
"next": "^14.2.26",
|
||||
@@ -97,39 +97,39 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-animation": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation/-/semi-animation-2.77.1.tgz",
|
||||
"integrity": "sha512-Q1D7whvQe0D+mPov8hXeH/e1uR/iBhpGGcW1LCTL2pSVMEZEYGJLf2KeXTTiCIgRVWm0PRH3Sux7auJ64zg7vw==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation/-/semi-animation-2.86.0.tgz",
|
||||
"integrity": "sha512-+MerLT6TJnJSbTnKQ5Za1HwxiM7snhfMdg2IX/0KXxwFYhkHsnkj07krZBNKMKWDJpfG2qkb9z7KD/NQH7KL6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bezier-easing": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-animation-react": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation-react/-/semi-animation-react-2.77.1.tgz",
|
||||
"integrity": "sha512-imELR02pufgGFkZURfTd9oBUtZPYhHvXv9WsYoRvEoBM9U7yzxrR6Fb/Lc3TH+WHVJ2oZHH2S0APS5t1MceEOw==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation-react/-/semi-animation-react-2.86.0.tgz",
|
||||
"integrity": "sha512-nThSOLD6iU8gyNALDYAsbXgcmM9eAJe7F1Sb5zvOi56cA4M52Wk50AuS9InAbGTJjzxNY7FkBu1KMepxXDHEIg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@douyinfe/semi-animation": "2.77.1",
|
||||
"@douyinfe/semi-animation-styled": "2.77.1",
|
||||
"@douyinfe/semi-animation": "2.86.0",
|
||||
"@douyinfe/semi-animation-styled": "2.86.0",
|
||||
"classnames": "^2.2.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-animation-styled": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.77.1.tgz",
|
||||
"integrity": "sha512-FBRroqVJroel1CXmBgV58ulZHG2xUVInJF7k0FAag54noKKaToEobSxRjiTJ6JHne3ZDU1M6sBqpbzYJElFnPQ==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-animation-styled/-/semi-animation-styled-2.86.0.tgz",
|
||||
"integrity": "sha512-5x5WA8i1vuwb7rygScWmqxwgNggvpGd0rtDwAqBX8l5KoDSml2ikmR015w+SaoOD/UGwJB44IJ02UYYcxZw9kg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@douyinfe/semi-foundation": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.77.1.tgz",
|
||||
"integrity": "sha512-DAXRy8ryLNzbKAiTAv+RrivGCoMU0asv2cO7PNV5aBq0ICB8XXn97FHyZo6Wb5NpqpyMhOaOr8Ro1bfpd0FeaA==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-foundation/-/semi-foundation-2.86.0.tgz",
|
||||
"integrity": "sha512-IX6Vzcen16u+Z1fbYPCAnZIqxfnAk7kI9CBJ9u6rZM8LIEP/HMYSEdMmCg8Cigw4GiSbjdlDfy6miy5tXWJmAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@douyinfe/semi-animation": "2.77.1",
|
||||
"@douyinfe/semi-json-viewer-core": "2.77.1",
|
||||
"@douyinfe/semi-animation": "2.86.0",
|
||||
"@douyinfe/semi-json-viewer-core": "2.86.0",
|
||||
"@mdx-js/mdx": "^3.0.1",
|
||||
"async-validator": "^3.5.0",
|
||||
"classnames": "^2.2.6",
|
||||
@@ -145,9 +145,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-icons": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-icons/-/semi-icons-2.77.1.tgz",
|
||||
"integrity": "sha512-IbGqYzbjzCoSd+//HlO/Gn1c3XmbulQwGys+JgDfQhYIbPeGyhQfLk56Q7ku3vJGC8BGy7dUmR9MbeTf1UQGtw==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-icons/-/semi-icons-2.86.0.tgz",
|
||||
"integrity": "sha512-KEDlYYP1wdOqN28Ck0YcdCx7mSks8SRY4w4KKbXPaROzYNEyT2BRcJxwysMHfxL2IDfsroHrRPJsX9pnrmQqTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6"
|
||||
@@ -157,44 +157,44 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-illustrations": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.77.1.tgz",
|
||||
"integrity": "sha512-FlESLOPaY0SadiSIFcP4gqJUk+CYkd4rHK6YP9bfjmU26v7h1S02H7pGLLV1lS0WnY4j0ad4zqRV9tbXFvba9g==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-illustrations/-/semi-illustrations-2.86.0.tgz",
|
||||
"integrity": "sha512-ZRgtgPor1X9lZP/8IOT4xZOmio+tWBUb9Sb2MAjTj52ZtJB6u4jz34y4TnoqRjoGRYWwNvOQmNjBk9NiHoAYkg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-json-viewer-core": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-json-viewer-core/-/semi-json-viewer-core-2.77.1.tgz",
|
||||
"integrity": "sha512-LOW+7ga2OzFIL9pGKftwHfl1kKLTV3x6Cs857iyvq9GIF/GHbAboiHcKUy2OZIHfy66zvP+Focs+yhfZG7IcZw==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-json-viewer-core/-/semi-json-viewer-core-2.86.0.tgz",
|
||||
"integrity": "sha512-Xk8CkQ/VKACUZfLg9D5vds9vy2WvWzI5ezioBNKRF5GPnonYeL6vwFz0rpzSL4dKdS17Ndg5KexWV4g9mExgLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jsonc-parser": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@douyinfe/semi-theme-default": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-theme-default/-/semi-theme-default-2.77.1.tgz",
|
||||
"integrity": "sha512-Rug75C7jjSqmCP2L2tBI0K4dnXuo4GardzwSzdSjxDkiaIXwOwR5KE0K1FRbKWkQ7xmxbyRu4S6Pff+CDEJ/lA==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-theme-default/-/semi-theme-default-2.86.0.tgz",
|
||||
"integrity": "sha512-l1zVriNXVxBribjTL7hGyiHrymum3UOPpiurWZdleK7oKWPVtkWdDh+cdVkDS/QynEY7P+m0zFCye1GbjNPkNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@douyinfe/semi-ui": {
|
||||
"version": "2.77.1",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-ui/-/semi-ui-2.77.1.tgz",
|
||||
"integrity": "sha512-eIy7kr9OleCwlNRby3VICSGScHM23Zt2u7TJpID68qN3WrfQowGaB4wQ/0k5bvpLzv463HQnVWFk5aak+v46yw==",
|
||||
"version": "2.86.0",
|
||||
"resolved": "https://registry.npmjs.org/@douyinfe/semi-ui/-/semi-ui-2.86.0.tgz",
|
||||
"integrity": "sha512-UAwM3+qO25Zkvy3a+7Jk8xBt5/103eP/t9x2pqvRITLeP2XqIikXruXV3bSLO1HDlCt9VyORqxC9UEYFOutk5Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.0.8",
|
||||
"@dnd-kit/sortable": "^7.0.2",
|
||||
"@dnd-kit/utilities": "^3.2.1",
|
||||
"@douyinfe/semi-animation": "2.77.1",
|
||||
"@douyinfe/semi-animation-react": "2.77.1",
|
||||
"@douyinfe/semi-foundation": "2.77.1",
|
||||
"@douyinfe/semi-icons": "2.77.1",
|
||||
"@douyinfe/semi-illustrations": "2.77.1",
|
||||
"@douyinfe/semi-theme-default": "2.77.1",
|
||||
"@douyinfe/semi-animation": "2.86.0",
|
||||
"@douyinfe/semi-animation-react": "2.86.0",
|
||||
"@douyinfe/semi-foundation": "2.86.0",
|
||||
"@douyinfe/semi-icons": "2.86.0",
|
||||
"@douyinfe/semi-illustrations": "2.86.0",
|
||||
"@douyinfe/semi-theme-default": "2.86.0",
|
||||
"async-validator": "^3.5.0",
|
||||
"classnames": "^2.2.6",
|
||||
"copy-text-to-clipboard": "^2.1.1",
|
||||
@@ -414,15 +414,16 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@mdx-js/mdx": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.0.tgz",
|
||||
"integrity": "sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-3.1.1.tgz",
|
||||
"integrity": "sha512-f6ZO2ifpwAQIpzGWaBQT2TXxPv6z3RBzQKpVftEWN78Vl/YweF1uwussDx8ECAXVtr3Rs89fKyG9YlzUs9DyGQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"@types/estree-jsx": "^1.0.0",
|
||||
"@types/hast": "^3.0.0",
|
||||
"@types/mdx": "^2.0.0",
|
||||
"acorn": "^8.0.0",
|
||||
"collapse-white-space": "^2.0.0",
|
||||
"devlop": "^1.0.0",
|
||||
"estree-util-is-identifier-name": "^3.0.0",
|
||||
@@ -1895,9 +1896,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/decode-named-character-reference": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.1.0.tgz",
|
||||
"integrity": "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
|
||||
"integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"character-entities": "^2.0.0"
|
||||
@@ -4055,9 +4056,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lottie-web": {
|
||||
"version": "5.12.2",
|
||||
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.12.2.tgz",
|
||||
"integrity": "sha512-uvhvYPC8kGPjXT3MyKMrL3JitEAmDMp30lVkuq/590Mw9ok6pWcFCwXJveo0t5uqYw1UREQHofD+jVpdjBv8wg==",
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz",
|
||||
"integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/markdown-extensions": {
|
||||
@@ -5647,9 +5648,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/property-information": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz",
|
||||
"integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==",
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
|
||||
"integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -5862,9 +5863,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/recma-jsx": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.0.tgz",
|
||||
"integrity": "sha512-5vwkv65qWwYxg+Atz95acp8DMu1JDSqdGkA2Of1j6rCreyFUE/gp15fC8MnGEuG1W68UKjM6x6+YTWIh7hZM/Q==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/recma-jsx/-/recma-jsx-1.0.1.tgz",
|
||||
"integrity": "sha512-huSIy7VU2Z5OLv6oFLosQGGDqPqdO1iq6bWNAdhzMxSJP7RAso4fCZ1cKu8j9YHCZf3TPrq4dw3okhrylgcd7w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"acorn-jsx": "^5.0.0",
|
||||
@@ -5876,6 +5877,9 @@
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recma-parse": {
|
||||
@@ -5992,9 +5996,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/remark-mdx": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.0.tgz",
|
||||
"integrity": "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA==",
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-3.1.1.tgz",
|
||||
"integrity": "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mdast-util-mdx": "^3.0.0",
|
||||
@@ -6022,9 +6026,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/remark-rehype": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz",
|
||||
"integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==",
|
||||
"version": "11.1.2",
|
||||
"resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
|
||||
"integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hast": "^3.0.0",
|
||||
@@ -6415,12 +6419,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.7.4",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
||||
"version": "0.7.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
|
||||
"integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
@@ -6653,18 +6657,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-js": {
|
||||
"version": "1.1.16",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.16.tgz",
|
||||
"integrity": "sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==",
|
||||
"version": "1.1.17",
|
||||
"resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
|
||||
"integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"style-to-object": "1.0.8"
|
||||
"style-to-object": "1.0.9"
|
||||
}
|
||||
},
|
||||
"node_modules/style-to-object": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz",
|
||||
"integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==",
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
|
||||
"integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inline-style-parser": "0.2.4"
|
||||
@@ -7108,9 +7112,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vfile-message": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
|
||||
"integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz",
|
||||
"integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "^3.0.0",
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@douyinfe/semi-ui": "^2.75.0",
|
||||
"@douyinfe/semi-ui": "^2.86.0",
|
||||
"artplayer": "^5.2.1",
|
||||
"mpegts.js": "^1.8.0",
|
||||
"next": "^14.2.26",
|
||||
|
||||
Reference in new issue
Block a user