fix: BGM 混音链路修复——URL 先下载到本地缓存再混音
- fix: 删除 BGM 预览硬编码开发者路径,改为使用 url 字段 - fix: BGM 混音前检测是否为 URL,先下载到 bgm_cache 本地缓存 - fix: Rust mix_bgm_to_video 恢复 validate_safe_path 校验,拒绝 URL - feat: 新增 bgm_cache 目录及自动清理策略(30天/200MB上限) - feat: Settings 缓存清理扩展为媒体缓存(video + BGM 统一清理) - chore: BGM url 字段改为后端必填,同步 schema/model/seed/迁移
This commit is contained in:
@@ -25,6 +25,8 @@ jobs:
|
|||||||
name: Build macOS (Universal)
|
name: Build macOS (Universal)
|
||||||
if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'macos' }}
|
if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'macos' }}
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -121,6 +123,8 @@ jobs:
|
|||||||
name: Build Windows (x64)
|
name: Build Windows (x64)
|
||||||
if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'windows' }}
|
if: ${{ github.event_name == 'push' || inputs.platform == 'all' || inputs.platform == 'windows' }}
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
"""make_bgm_music_url_non_nullable
|
||||||
|
|
||||||
|
Revision ID: 7149f61a2f9c
|
||||||
|
Revises: 7172a476e5b2
|
||||||
|
Create Date: 2026-05-21 10:45:00.000000
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '7149f61a2f9c'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '100366516fbd'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# BGM 云端化改造后,url 字段为必填(七牛云 CDN 地址)
|
||||||
|
op.alter_column(
|
||||||
|
'mjk_bgm_musics',
|
||||||
|
'url',
|
||||||
|
existing_type=sa.String(length=1024),
|
||||||
|
nullable=False,
|
||||||
|
comment='七牛云 URL',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
op.alter_column(
|
||||||
|
'mjk_bgm_musics',
|
||||||
|
'url',
|
||||||
|
existing_type=sa.String(length=1024),
|
||||||
|
nullable=True,
|
||||||
|
comment='七牛云 URL',
|
||||||
|
)
|
||||||
@@ -36,8 +36,8 @@ class BgmMusic(BaseModelBigInt):
|
|||||||
file_path: Mapped[str] = mapped_column(
|
file_path: Mapped[str] = mapped_column(
|
||||||
String(512), nullable=False, comment="相对文件路径"
|
String(512), nullable=False, comment="相对文件路径"
|
||||||
)
|
)
|
||||||
url: Mapped[str | None] = mapped_column(
|
url: Mapped[str] = mapped_column(
|
||||||
String(1024), nullable=True, comment="七牛云 URL"
|
String(1024), nullable=False, comment="七牛云 URL"
|
||||||
)
|
)
|
||||||
duration: Mapped[float] = mapped_column(
|
duration: Mapped[float] = mapped_column(
|
||||||
Float, nullable=True, comment="时长(秒)"
|
Float, nullable=True, comment="时长(秒)"
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class BgmMusicItem(BaseModel):
|
|||||||
artist: str | None = Field(default=None, description="艺术家")
|
artist: str | None = Field(default=None, description="艺术家")
|
||||||
category: str = Field(description="场景分类")
|
category: str = Field(description="场景分类")
|
||||||
file_path: str = Field(description="相对文件路径")
|
file_path: str = Field(description="相对文件路径")
|
||||||
url: str | None = Field(default=None, description="七牛云 URL")
|
url: str = Field(description="七牛云 URL")
|
||||||
duration: float | None = Field(default=None, description="时长(秒)")
|
duration: float | None = Field(default=None, description="时长(秒)")
|
||||||
sort_order: int = Field(default=0, description="排序权重")
|
sort_order: int = Field(default=0, description="排序权重")
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ async def seed_bgm():
|
|||||||
artist=item.get("artist"),
|
artist=item.get("artist"),
|
||||||
category=item["category"],
|
category=item["category"],
|
||||||
file_path=item["file_path"],
|
file_path=item["file_path"],
|
||||||
url=item.get("url"),
|
url=item["url"],
|
||||||
duration=item.get("duration"),
|
duration=item.get("duration"),
|
||||||
status=item.get("status", "active"),
|
status=item.get("status", "active"),
|
||||||
sort_order=item.get("sort_order", idx),
|
sort_order=item.get("sort_order", idx),
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ pub async fn burn_ass_subtitle_with_fonts(
|
|||||||
* 使用 FFmpeg amix 滤镜将 BGM 与原视频音频混合。
|
* 使用 FFmpeg amix 滤镜将 BGM 与原视频音频混合。
|
||||||
* 如果 BGM 短于视频,会自动循环;如果长于视频,会截断到视频长度。
|
* 如果 BGM 短于视频,会自动循环;如果长于视频,会截断到视频长度。
|
||||||
*
|
*
|
||||||
* 注意:bgm_path 来自应用资源目录,不做 validate_safe_path 检查。
|
* BGM 路径必须是本地文件(由前端下载到 bgm_cache 后传入)。
|
||||||
*/
|
*/
|
||||||
pub async fn mix_bgm_to_video(
|
pub async fn mix_bgm_to_video(
|
||||||
app: &AppHandle,
|
app: &AppHandle,
|
||||||
@@ -515,8 +515,7 @@ pub async fn mix_bgm_to_video(
|
|||||||
bgm_volume: f64,
|
bgm_volume: f64,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let safe_video = validate_safe_path(video_path)?;
|
let safe_video = validate_safe_path(video_path)?;
|
||||||
// BGM 来自应用资源目录,直接传递(路径由前端通过 Tauri path API 解析)
|
let safe_bgm = validate_safe_path(bgm_path)?;
|
||||||
let safe_bgm = bgm_path.to_string();
|
|
||||||
let safe_output = sanitize_output_path(output_path)?;
|
let safe_output = sanitize_output_path(output_path)?;
|
||||||
|
|
||||||
// 构建 filter_complex:
|
// 构建 filter_complex:
|
||||||
|
|||||||
@@ -165,6 +165,133 @@ fn get_video_cache_size_cmd() -> Result<u64, String> {
|
|||||||
Ok(total)
|
Ok(total)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// BGM 缓存清理
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
/// 清理 bgm_cache 目录
|
||||||
|
///
|
||||||
|
/// 策略:和视频缓存一致
|
||||||
|
/// 1. 删除超过 30 天未修改的文件
|
||||||
|
/// 2. 总容量超过 200MB 时,按修改时间删最旧文件直到 100MB
|
||||||
|
fn clean_bgm_cache(app_data_dir: &std::path::Path) {
|
||||||
|
let cache_dir = app_data_dir.join("bgm_cache");
|
||||||
|
if !cache_dir.exists() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let max_age = std::time::Duration::from_secs(30 * 24 * 60 * 60);
|
||||||
|
let max_total_size: u64 = 200 * 1024 * 1024;
|
||||||
|
let target_size: u64 = 100 * 1024 * 1024;
|
||||||
|
let now = std::time::SystemTime::now();
|
||||||
|
|
||||||
|
let mut entries: Vec<(std::path::PathBuf, std::time::SystemTime, u64)> = Vec::new();
|
||||||
|
let mut total_size: u64 = 0;
|
||||||
|
|
||||||
|
let read_dir = match std::fs::read_dir(&cache_dir) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("[bgm_cache] 无法读取缓存目录: {}", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in read_dir.flatten() {
|
||||||
|
let Ok(metadata) = entry.metadata() else { continue };
|
||||||
|
if !metadata.is_file() { continue; }
|
||||||
|
let mtime = metadata.modified().unwrap_or(now);
|
||||||
|
let size = metadata.len();
|
||||||
|
total_size += size;
|
||||||
|
entries.push((entry.path(), mtime, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 删除超过 30 天的文件
|
||||||
|
let mut deleted_size: u64 = 0;
|
||||||
|
for (path, mtime, size) in &entries {
|
||||||
|
if now.duration_since(*mtime).unwrap_or_default() > max_age {
|
||||||
|
if std::fs::remove_file(path).is_ok() {
|
||||||
|
deleted_size += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 容量超限,删最旧的
|
||||||
|
let remaining_size = total_size.saturating_sub(deleted_size);
|
||||||
|
if remaining_size > max_total_size {
|
||||||
|
let mut sorted = entries;
|
||||||
|
sorted.sort_by_key(|(_, mtime, _)| *mtime);
|
||||||
|
let mut to_free = remaining_size.saturating_sub(target_size);
|
||||||
|
for (path, _, size) in sorted {
|
||||||
|
if to_free == 0 { break; }
|
||||||
|
if path.exists() && std::fs::remove_file(&path).is_ok() {
|
||||||
|
to_free = to_free.saturating_sub(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 手动清理 bgm_cache 目录,返回释放的字节数
|
||||||
|
#[tauri::command]
|
||||||
|
fn clear_bgm_cache_cmd() -> Result<u64, String> {
|
||||||
|
let app_data_dir = crate::storage::paths::get_app_data_dir()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let cache_dir = app_data_dir.join("bgm_cache");
|
||||||
|
if !cache_dir.exists() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut freed: u64 = 0;
|
||||||
|
let read_dir = match std::fs::read_dir(&cache_dir) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(e) => return Err(format!("无法读取缓存目录: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in read_dir.flatten() {
|
||||||
|
let Ok(metadata) = entry.metadata() else { continue };
|
||||||
|
if !metadata.is_file() { continue; }
|
||||||
|
let size = metadata.len();
|
||||||
|
if std::fs::remove_file(entry.path()).is_ok() {
|
||||||
|
freed += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(freed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 bgm_cache 目录当前占用大小(字节)
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_bgm_cache_size_cmd() -> Result<u64, String> {
|
||||||
|
let app_data_dir = crate::storage::paths::get_app_data_dir()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
let cache_dir = app_data_dir.join("bgm_cache");
|
||||||
|
if !cache_dir.exists() {
|
||||||
|
return Ok(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut total: u64 = 0;
|
||||||
|
let read_dir = match std::fs::read_dir(&cache_dir) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(e) => return Err(format!("无法读取缓存目录: {}", e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in read_dir.flatten() {
|
||||||
|
let Ok(metadata) = entry.metadata() else { continue };
|
||||||
|
if metadata.is_file() {
|
||||||
|
total += metadata.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(total)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 BGM 缓存目录路径(供前端下载 BGM 时使用)
|
||||||
|
#[tauri::command]
|
||||||
|
fn get_bgm_cache_dir() -> Result<String, String> {
|
||||||
|
crate::storage::paths::get_bgm_cache_dir()
|
||||||
|
.map(|p| p.to_string_lossy().to_string())
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 应用入口
|
// 应用入口
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -189,8 +316,12 @@ pub fn run() {
|
|||||||
// 初始化应用数据目录(所有业务数据的根目录)
|
// 初始化应用数据目录(所有业务数据的根目录)
|
||||||
if let Ok(app_data_dir) = app.path().app_local_data_dir() {
|
if let Ok(app_data_dir) = app.path().app_local_data_dir() {
|
||||||
crate::storage::init_app_data_dir(app_data_dir.clone());
|
crate::storage::init_app_data_dir(app_data_dir.clone());
|
||||||
// 后台清理过期视频缓存,不阻塞首屏
|
// 后台清理过期视频缓存和 BGM 缓存,不阻塞首屏
|
||||||
std::thread::spawn(move || clean_video_cache(&app_data_dir));
|
let app_data_dir_clone = app_data_dir.clone();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
clean_video_cache(&app_data_dir_clone);
|
||||||
|
clean_bgm_cache(&app_data_dir_clone);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 窗口初始 visible=false,setup 阶段先显示窗口
|
// 窗口初始 visible=false,setup 阶段先显示窗口
|
||||||
@@ -330,6 +461,9 @@ pub fn run() {
|
|||||||
// 缓存清理
|
// 缓存清理
|
||||||
get_video_cache_size_cmd,
|
get_video_cache_size_cmd,
|
||||||
clear_video_cache_cmd,
|
clear_video_cache_cmd,
|
||||||
|
get_bgm_cache_size_cmd,
|
||||||
|
clear_bgm_cache_cmd,
|
||||||
|
get_bgm_cache_dir,
|
||||||
])
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
@@ -120,3 +120,12 @@ pub fn get_cover_avatars_json_path() -> Result<PathBuf, StorageError> {
|
|||||||
let base = get_app_data_dir()?;
|
let base = get_app_data_dir()?;
|
||||||
Ok(base.join("cover_avatars.json"))
|
Ok(base.join("cover_avatars.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 获取 BGM 缓存目录
|
||||||
|
/// {app_local_data_dir}/bgm_cache/
|
||||||
|
pub fn get_bgm_cache_dir() -> Result<PathBuf, StorageError> {
|
||||||
|
let base = get_app_data_dir()?;
|
||||||
|
let path = base.join("bgm_cache");
|
||||||
|
crate::storage::engine::ensure_dir(&path)?;
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface BgmMusicItem {
|
|||||||
artist: string | null;
|
artist: string | null;
|
||||||
category: string;
|
category: string;
|
||||||
filePath: string;
|
filePath: string;
|
||||||
url: string | null;
|
url: string;
|
||||||
duration: number | null;
|
duration: number | null;
|
||||||
sortOrder: number;
|
sortOrder: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,11 +132,14 @@ export default function Settings() {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 获取缓存大小
|
// 获取媒体缓存大小(视频缓存 + BGM 缓存)
|
||||||
const fetchCacheSize = useCallback(async () => {
|
const fetchCacheSize = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const size = await invoke<number>('get_video_cache_size_cmd');
|
const [videoSize, bgmSize] = await Promise.all([
|
||||||
setCacheSize(size);
|
invoke<number>('get_video_cache_size_cmd'),
|
||||||
|
invoke<number>('get_bgm_cache_size_cmd'),
|
||||||
|
]);
|
||||||
|
setCacheSize(videoSize + bgmSize);
|
||||||
} catch {
|
} catch {
|
||||||
setCacheSize(0);
|
setCacheSize(0);
|
||||||
}
|
}
|
||||||
@@ -151,7 +154,7 @@ export default function Settings() {
|
|||||||
};
|
};
|
||||||
}, [fetchCacheSize]);
|
}, [fetchCacheSize]);
|
||||||
|
|
||||||
// 清理缓存
|
// 清理媒体缓存(视频缓存 + BGM 缓存)
|
||||||
const handleClearCache = async () => {
|
const handleClearCache = async () => {
|
||||||
if (cacheSize === 0) {
|
if (cacheSize === 0) {
|
||||||
toast.info('暂无缓存需要清理');
|
toast.info('暂无缓存需要清理');
|
||||||
@@ -159,9 +162,13 @@ export default function Settings() {
|
|||||||
}
|
}
|
||||||
setClearingCache(true);
|
setClearingCache(true);
|
||||||
try {
|
try {
|
||||||
const freed = await invoke<number>('clear_video_cache_cmd');
|
const [videoFreed, bgmFreed] = await Promise.all([
|
||||||
|
invoke<number>('clear_video_cache_cmd'),
|
||||||
|
invoke<number>('clear_bgm_cache_cmd'),
|
||||||
|
]);
|
||||||
|
const totalFreed = videoFreed + bgmFreed;
|
||||||
setCacheSize(0);
|
setCacheSize(0);
|
||||||
toast.success(`已清理 ${formatBytes(freed)} 缓存`);
|
toast.success(`已清理 ${formatBytes(totalFreed)} 缓存`);
|
||||||
} catch {
|
} catch {
|
||||||
toast.error('清理缓存失败');
|
toast.error('清理缓存失败');
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { useState, useEffect, useRef } from 'react';
|
|||||||
import { listen } from '@tauri-apps/api/event';
|
import { listen } from '@tauri-apps/api/event';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import { save } from '@tauri-apps/plugin-dialog';
|
import { save } from '@tauri-apps/plugin-dialog';
|
||||||
|
import { exists } from '@tauri-apps/plugin-fs';
|
||||||
import { compositeApi } from '../../api/modules/videoComposite';
|
import { compositeApi } from '../../api/modules/videoComposite';
|
||||||
|
import { downloadFile } from '../../api/modules/videoCompose';
|
||||||
import { bgmMusicApi, type BgmMusicItem } from '../../api/modules/bgmMusic';
|
import { bgmMusicApi, type BgmMusicItem } from '../../api/modules/bgmMusic';
|
||||||
import { pointsApi } from '../../api/modules/points';
|
import { pointsApi } from '../../api/modules/points';
|
||||||
import { useProjectStore, saveMetaToLocalFile } from '../../store';
|
import { useProjectStore, saveMetaToLocalFile } from '../../store';
|
||||||
@@ -110,7 +112,11 @@ export default function VideoCompose() {
|
|||||||
if (audioRef.current) {
|
if (audioRef.current) {
|
||||||
audioRef.current.onended = null;
|
audioRef.current.onended = null;
|
||||||
}
|
}
|
||||||
const audioSrc = item.url || (item.filePath ? `/Users/0fun/work/meijiaka-zy/mixkit_bgm/${item.filePath}` : '');
|
const audioSrc = item.url;
|
||||||
|
if (!audioSrc) {
|
||||||
|
toast.warning('该音乐暂无可用的试听链接');
|
||||||
|
return;
|
||||||
|
}
|
||||||
const audio = new Audio(audioSrc);
|
const audio = new Audio(audioSrc);
|
||||||
audio.play().catch(() => {});
|
audio.play().catch(() => {});
|
||||||
audio.onended = () => setPreviewId(null);
|
audio.onended = () => setPreviewId(null);
|
||||||
@@ -264,11 +270,25 @@ export default function VideoCompose() {
|
|||||||
// 4. 如果选择了 BGM,混合背景音乐
|
// 4. 如果选择了 BGM,混合背景音乐
|
||||||
if (bgmMusicPath) {
|
if (bgmMusicPath) {
|
||||||
useProgressStore.getState().update('正在混合背景音乐...');
|
useProgressStore.getState().update('正在混合背景音乐...');
|
||||||
const bgmFullPath = bgmMusicPath;
|
let finalBgmPath = bgmMusicPath;
|
||||||
|
|
||||||
|
// BGM 为 URL 时,先下载到本地缓存
|
||||||
|
if (bgmMusicPath.startsWith('http')) {
|
||||||
|
const cacheDir = await invoke<string>('get_bgm_cache_dir');
|
||||||
|
const ext = bgmMusicPath.split('.').pop() || 'mp3';
|
||||||
|
const cachedPath = `${cacheDir}/bgm_${bgmMusicId}.${ext}`;
|
||||||
|
const fileExists = await exists(cachedPath);
|
||||||
|
if (!fileExists) {
|
||||||
|
useProgressStore.getState().update('正在下载背景音乐...');
|
||||||
|
await downloadFile(bgmMusicPath, cachedPath);
|
||||||
|
}
|
||||||
|
finalBgmPath = cachedPath;
|
||||||
|
}
|
||||||
|
|
||||||
const mixRes = await invoke<{ code: number; data?: string; message: string }>('mix_bgm_to_video', {
|
const mixRes = await invoke<{ code: number; data?: string; message: string }>('mix_bgm_to_video', {
|
||||||
args: {
|
args: {
|
||||||
videoPath: result,
|
videoPath: result,
|
||||||
bgmPath: bgmFullPath,
|
bgmPath: finalBgmPath,
|
||||||
outputPath: result,
|
outputPath: result,
|
||||||
videoVolume: 1.0,
|
videoVolume: 1.0,
|
||||||
bgmVolume: (bgmVolume ?? 0.25),
|
bgmVolume: (bgmVolume ?? 0.25),
|
||||||
@@ -730,7 +750,7 @@ export default function VideoCompose() {
|
|||||||
<div
|
<div
|
||||||
className="modal-bgm-info"
|
className="modal-bgm-info"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setBgmMusic(item.id, item.title, item.url || item.filePath);
|
setBgmMusic(item.id, item.title, item.url);
|
||||||
setBgmModalOpen(false);
|
setBgmModalOpen(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user