1、nfo刮削文件优化,修复演员信息(视频作者),建议emby媒体库选影视,或者混合类型-右上角有一个兼容旧数据的按钮一键重置所有刮削文件
2、图文视频 以及动态视频,遇到因版权原因无法下载音频时,需要额外的音频文件,现在放到mp3目录了,docker-compose增加一个映射路径即可 例如: - /vol2/1000/media/dysync/mp3:/app/mp3 3、增加动态视频合成配置(不建议保留原视频,没有额外刮削的,因为有些视频是几十个很短的动图一样的视频拼接成的,保留原视频emby会出现一堆没有封面的视频) 4、容器重启后,手机端会连续跳转登录页很多次的bug修复 5、关注博主保存文件夹路径长度增加,允许_ 6、批量永久删除选中的视频(不再下载,慎重) 7、看板页面 隐藏一个功能:双击作者头像,可以删除作者所有视频(永久删除,不再下载,慎重) 8、手机端 视频播放优化
This commit is contained in:
+128
-162
@@ -25,7 +25,7 @@
|
||||
<a-switch v-model:checked="formState.OnlySyncNew" />
|
||||
<div class="flex items-start mt-1 text-sm text-gray-500">
|
||||
<InfoCircleOutlined class="text-blue-400 mr-1 mt-0.5" />
|
||||
<span>开启后,仅同步最近收藏的20条,以及未来新收藏的,不会去同步之前的视频,默认开启, 避免突然大量下载,导致风控</span>
|
||||
<span>开启后,仅同步最近的20条,以及未来新加入收藏、喜欢、关注的视频,不会去同步之前的视频,默认开启, 避免突然大量下载,导致风控</span>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</div>
|
||||
@@ -113,32 +113,44 @@
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="formState.DownImageVideo" has-feedback label="默认音频" name="AudioFile" :wrapper-col="{ span: 20 }">
|
||||
<!-- 音频上传与播放器容器 -->
|
||||
<div class="audio-upload-player-wrapper" style="display: flex; align-items: center; gap: 16px;">
|
||||
<a-upload :before-upload="beforeUpload" :custom-request="customUpload" :show-upload-list="false" accept=".mp3,.wav">
|
||||
<a-button type="default">
|
||||
<UploadOutlined /> 选择文件
|
||||
</a-button>
|
||||
</a-upload>
|
||||
|
||||
<!-- 新增:启用原生完整控件(controls属性),自带可拖拽进度条 -->
|
||||
<div class="audio-player" v-if="audioUrl" style="flex: 1; max-width: 500px;">
|
||||
<audio ref="audioInstance" :src="audioUrl" controls controlsList="nodownload" @ended="() => isPlaying = false" @pause="() => isPlaying = false" @play="() => isPlaying = true" class="native-audio-player">
|
||||
您的浏览器不支持HTML5音频播放,请升级至现代浏览器。
|
||||
</audio>
|
||||
</div>
|
||||
<a-form-item has-feedback label="默认音频" name="AudioFile" :wrapper-col="{ span: 20 }">
|
||||
<!-- 仅保留检测按钮 -->
|
||||
<div class="audio-check-wrapper" style="display: flex; align-items: center; gap: 16px;">
|
||||
<a-button type="primary" @click="checkAudioList">
|
||||
<play-circle-outlined /> 音频列表
|
||||
</a-button>
|
||||
</div>
|
||||
|
||||
<!-- 提示文本调整 -->
|
||||
<div class="flex items-start mt-1 text-sm text-gray-500" style="color:red">
|
||||
<InfoCircleOutlined class="text-blue-400 mr-1 mt-0.5" />
|
||||
<span>当下载图文视频时,音频因版权原因无法下载时,将用该音频文件作为合成视频的音频</span>
|
||||
</div>
|
||||
<div class="flex items-start mt-1 text-sm text-gray-500">
|
||||
<InfoCircleOutlined class="text-blue-400 mr-1 mt-0.5" />
|
||||
<span>支持格式:MP3、WAV、AAC、FLAC、OGG、M4A、WMA,单个文件最大20MB</span>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-modal v-model:visible="audioListModalVisible" title="发现音频" width="666px" destroyOnClose @ok="closeAudioListModal" @cancel="closeAudioListModal">
|
||||
<div class="audio-list-container" style="max-height: 500px; overflow-y: auto; padding: 10px 0;">
|
||||
<!-- 空列表提示 -->
|
||||
<div v-if="audioList.length === 0" style="text-align: center; padding: 40px; color: #999;">
|
||||
暂无可用音频文件
|
||||
</div>
|
||||
|
||||
<!-- 音频列表(嵌入原生audio播放器) -->
|
||||
<a-list v-else bordered :data-source="audioList" item-layout="horizontal">
|
||||
<template #renderItem="{item}">
|
||||
<a-list-item>
|
||||
<a-list-item-meta :title="item.filename " />
|
||||
<template #actions>
|
||||
<!-- 替换原播放按钮,嵌入原生HTML音频播放器 -->
|
||||
<audio class="native-audio-player" controls :src="'/api/config/getmp3?name='+item.filename" preload="none" @play="handleAudioPlay($event.target)">
|
||||
您的浏览器不支持HTML5音频播放器,请升级浏览器后重试。
|
||||
</audio>
|
||||
</template>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-modal>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
@@ -226,7 +238,7 @@
|
||||
<input ref="importFileInput" type="file" accept=".json" class="import-file-input" @change="handleImportFile">
|
||||
</div>
|
||||
<div class="top-right-float-btn-container">
|
||||
<a-tooltip title="重新对同步好的视频文件进行刮削" placement="bottom">
|
||||
<a-tooltip title="重置所有刮削" placement="bottom">
|
||||
<a-button class="top-right-float-btn" type="primary" shape="circle" @click="renfo">
|
||||
<bulb-outlined />
|
||||
</a-button>
|
||||
@@ -276,9 +288,6 @@ const downImgVideo = ref(true);
|
||||
const floatMenuVisible = ref(false);
|
||||
const importFileInput = ref<HTMLInputElement | null>(null);
|
||||
|
||||
// 新增:文件上传相关状态(已删除 uploadFileList,仅保留 isUploading 可选)
|
||||
const isUploading = ref(false);
|
||||
|
||||
//开启或关闭合成视频
|
||||
const downImageVideoHandler = () => {
|
||||
if (formState.DownImageVideo) {
|
||||
@@ -405,7 +414,11 @@ const getConfig = () => {
|
||||
|
||||
tagData.value = JSON.parse(res.data.priorityLevel || '[]');
|
||||
} else {
|
||||
message.error(res.message || '获取配置失败', 8);
|
||||
if (res.message.indexOf('401') == -1) {
|
||||
message.error(res.message || '获取配置失败', 8);
|
||||
} else {
|
||||
console.error('获取配置失败:', res);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -443,135 +456,65 @@ const updateTagSort = () => {
|
||||
});
|
||||
};
|
||||
|
||||
// ========== 文件上传相关方法(已修改:移除进度条逻辑) ==========
|
||||
// 音频列表相关状态
|
||||
const audioListModalVisible = ref(false); // 弹窗显隐
|
||||
const audioList = ref<any[]>([]); // 存储接口返回的音频列表
|
||||
const currentPlayingAudio = ref<HTMLAudioElement | null>(null); // 核心:存储当前正在播放的音频
|
||||
|
||||
/**
|
||||
* 上传前校验
|
||||
* 音频播放互斥处理(核心:同一时间只播放一个音频)
|
||||
* @param target 事件目标元素(EventTarget 类型)
|
||||
*/
|
||||
const beforeUpload: UploadProps['beforeUpload'] = (file) => {
|
||||
// 1. 校验文件大小(10MB)
|
||||
const isLt20M = file.size / 1024 / 1024 < 20;
|
||||
if (!isLt20M) {
|
||||
message.error('文件大小不能超过20MB!');
|
||||
return false;
|
||||
const handleAudioPlay = (target: EventTarget | null) => {
|
||||
// 1. 校验元素是否存在,且是 HTMLAudioElement 类型
|
||||
if (!target || !(target instanceof HTMLAudioElement)) {
|
||||
return; // 非音频元素,直接返回,避免报错
|
||||
}
|
||||
|
||||
// 2. 校验文件类型
|
||||
const acceptTypes = ['.mp3', '.wav', '.aac', '.flac', '.ogg', '.m4a', '.wma'];
|
||||
const fileExt = '.' + file.name.split('.').pop()?.toLowerCase();
|
||||
if (!acceptTypes.includes(fileExt || '')) {
|
||||
message.error('仅支持MP3、WAV、AAC、FLAC、OGG、M4A、WMA格式的音频文件!');
|
||||
return false;
|
||||
// 2. 此时 target 已被确认是 HTMLAudioElement 类型,可安全操作
|
||||
const audioEl = target;
|
||||
// 3. 原有互斥逻辑
|
||||
if (currentPlayingAudio.value && currentPlayingAudio.value !== audioEl) {
|
||||
currentPlayingAudio.value.pause();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
// 1. 新增音频播放相关状态(放在现有状态定义区域,如 isUploading 下方)
|
||||
const audioUrl = ref('/api/config/defaudio'); // 上传成功后的音频文件URL
|
||||
const isPlaying = ref(false); // 音频是否正在播放
|
||||
const audioInstance = ref<HTMLAudioElement | null>(null); // 音频播放器实例
|
||||
|
||||
// 2. 改造原有 customUpload 方法,保存上传成功后的音频URL
|
||||
const customUpload: UploadProps['customRequest'] = (options) => {
|
||||
const { file, onSuccess, onError } = options;
|
||||
isUploading.value = true;
|
||||
|
||||
// 构造FormData
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
useApiStore()
|
||||
.apiUploadAudio(formData)
|
||||
.then((res) => {
|
||||
console.log(res);
|
||||
if (res.code === 0) {
|
||||
// message.success('音频文件上传成功!');
|
||||
audioUrl.value = `/api/config/defaudio?t=${Date.now()}`;
|
||||
onSuccess(res);
|
||||
} else {
|
||||
message.error(res.message || '文件上传失败!');
|
||||
onError(new Error(res.message || '上传失败'), file);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('文件上传失败:', error);
|
||||
message.error('文件上传失败,请稍后重试!');
|
||||
onError(error, file);
|
||||
})
|
||||
.finally(() => {
|
||||
isUploading.value = false;
|
||||
});
|
||||
currentPlayingAudio.value = audioEl;
|
||||
};
|
||||
|
||||
// 新增:封装 load() 为 Promise (可复用)
|
||||
const audioLoadPromise = (audio: HTMLAudioElement): Promise<void> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 加载成功回调
|
||||
const onLoadSuccess = () => {
|
||||
audio.removeEventListener('canplaythrough', onLoadSuccess);
|
||||
audio.removeEventListener('error', onLoadError);
|
||||
resolve();
|
||||
};
|
||||
|
||||
// 加载失败回调
|
||||
const onLoadError = () => {
|
||||
audio.removeEventListener('canplaythrough', onLoadSuccess);
|
||||
audio.removeEventListener('error', onLoadError);
|
||||
reject(new Error('音频加载失败'));
|
||||
};
|
||||
|
||||
audio.addEventListener('canplaythrough', onLoadSuccess);
|
||||
audio.addEventListener('error', onLoadError);
|
||||
audio.load();
|
||||
});
|
||||
};
|
||||
// 修改 refreshAudioPlayer 为 async 方法
|
||||
const refreshAudioPlayer = async () => {
|
||||
if (!audioInstance.value) return;
|
||||
|
||||
/**
|
||||
* 检测音频列表:调用mp3List接口并展示弹窗
|
||||
*/
|
||||
const checkAudioList = async () => {
|
||||
try {
|
||||
// 1. 暂停播放、重置状态
|
||||
audioInstance.value.pause();
|
||||
audioInstance.value.currentTime = 0;
|
||||
isPlaying.value = false;
|
||||
|
||||
// 2. 等待加载完成(核心:解决时序冲突)
|
||||
await audioLoadPromise(audioInstance.value);
|
||||
|
||||
// 3. 加载完成后,安全调用 play()
|
||||
await audioInstance.value.play();
|
||||
isPlaying.value = true;
|
||||
// message.success('音频已自动播放');
|
||||
} catch (err) {
|
||||
if ((err as Error).message !== '音频加载失败') {
|
||||
// 区分加载失败和自动播放失败
|
||||
message.warning('自动播放失败,请手动点击播放按钮(浏览器限制)');
|
||||
} else {
|
||||
message.error('音频加载失败,无法自动播放');
|
||||
// 步骤1:打开弹窗前,先暂停上一次可能残留的播放音频
|
||||
if (currentPlayingAudio.value) {
|
||||
currentPlayingAudio.value.pause();
|
||||
currentPlayingAudio.value = null;
|
||||
}
|
||||
console.log('错误详情:', err);
|
||||
isPlaying.value = false;
|
||||
|
||||
// 步骤2:调用接口获取音频列表
|
||||
const res = await useApiStore().mp3List();
|
||||
if (res.code === 0) {
|
||||
audioList.value = res.data || [];
|
||||
audioListModalVisible.value = true;
|
||||
} else {
|
||||
message.error(res.message || '获取音频列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('检测音频列表失败:', error);
|
||||
message.error('获取音频列表失败,请稍后重试');
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 关闭音频列表弹窗
|
||||
*/
|
||||
const closeAudioListModal = () => {
|
||||
audioListModalVisible.value = false;
|
||||
// 清理当前播放的音频,避免弹窗关闭后仍在播放或残留实例
|
||||
if (currentPlayingAudio.value) {
|
||||
currentPlayingAudio.value.pause();
|
||||
currentPlayingAudio.value = null;
|
||||
}
|
||||
};
|
||||
// 监听 audioUrl 变化,重置播放状态(可选,优化用户体验)
|
||||
watch(
|
||||
audioUrl,
|
||||
(newVal, oldVal) => {
|
||||
// 排除初始值(仅当 url 发生有效变更时刷新)
|
||||
if (newVal && newVal !== oldVal) {
|
||||
isPlaying.value = false;
|
||||
// 核心:调用刷新方法,强制加载新音频
|
||||
refreshAudioPlayer();
|
||||
} else if (!newVal) {
|
||||
// 若 url 为空,仅重置状态
|
||||
isPlaying.value = false;
|
||||
if (audioInstance.value) {
|
||||
audioInstance.value.currentTime = 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: false }
|
||||
); // 关闭 immediate,避免初始加载时触发
|
||||
|
||||
// 组件挂载时获取配置
|
||||
onMounted(async () => {
|
||||
getConfig();
|
||||
@@ -938,29 +881,24 @@ const renfo = () => {
|
||||
:deep(.ant-upload.ant-upload-select) {
|
||||
display: inline-block;
|
||||
}
|
||||
// 音频列表弹窗样式优化
|
||||
:deep(.audio-list-container .ant-list) {
|
||||
.ant-list-item {
|
||||
padding: 12px 16px;
|
||||
&:hover {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
// 音频上传与播放器容器样式
|
||||
.audio-upload-player-wrapper {
|
||||
flex-wrap: wrap;
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
.ant-list-item-meta-title {
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
// 音频播放器样式优化
|
||||
:deep(.audio-player audio) {
|
||||
// border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
&:hover {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
}
|
||||
|
||||
// 播放/清空按钮 hover 效果
|
||||
:deep(.audio-player .ant-btn-text:hover) {
|
||||
background-color: #f5f5f5;
|
||||
// 检测按钮样式
|
||||
.audio-check-wrapper {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
// 右上角悬浮按钮容器
|
||||
@@ -987,4 +925,32 @@ const renfo = () => {
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); // 悬浮时阴影加深(可选,可删除)
|
||||
}
|
||||
}
|
||||
|
||||
// 原生音频播放器样式优化
|
||||
.native-audio-player {
|
||||
width: 200px; // 固定宽度,适配列表布局
|
||||
height: 32px; // 统一高度,与列表项对齐
|
||||
outline: none; // 移除聚焦轮廓
|
||||
border: 1px solid #d9d9d9; // 添边框,与AntD风格统一
|
||||
border-radius: 4px; // 圆角,与AntD风格统一
|
||||
background-color: #fafafa; // 浅背景色,提升质感
|
||||
|
||||
// 优化播放器内部控件样式(部分浏览器支持)
|
||||
&::-webkit-media-controls {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
// 悬浮效果
|
||||
&:hover {
|
||||
border-color: #1890ff; // 悬浮时边框变主色,提升交互感
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1); // 轻微发光效果
|
||||
}
|
||||
}
|
||||
|
||||
// 音频列表项适配,保证播放器与内容对齐
|
||||
:deep(.ant-list-item-actions) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user