feat: 新增 定时关闭 & 播放速度

This commit is contained in:
imsyy
2025-10-21 00:12:48 +08:00
parent 56045cd338
commit c21f970b86
6 changed files with 115 additions and 80 deletions

3
components.d.ts vendored
View File

@@ -11,7 +11,7 @@ declare module 'vue' {
AboutSetting: typeof import('./src/components/Setting/AboutSetting.vue')['default']
ArtistList: typeof import('./src/components/List/ArtistList.vue')['default']
AutoClose: typeof import('./src/components/Modal/AutoClose.vue')['default']
BatchList: typeof import('./src/components/Modal/BatchList.vue')['default']
BatchList: typeof import('./src/components/Modal/batchList.vue')['default']
ChangeRate: typeof import('./src/components/Modal/ChangeRate.vue')['default']
CloudMatch: typeof import('./src/components/Modal/CloudMatch.vue')['default']
CommentList: typeof import('./src/components/List/CommentList.vue')['default']
@@ -96,7 +96,6 @@ declare module 'vue' {
NP: typeof import('naive-ui')['NP']
NPopconfirm: typeof import('naive-ui')['NPopconfirm']
NPopover: typeof import('naive-ui')['NPopover']
NProgress: typeof import('naive-ui')['NProgress']
NQrCode: typeof import('naive-ui')['NQrCode']
NRadio: typeof import('naive-ui')['NRadio']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']

View File

@@ -11,7 +11,11 @@
</n-text>
</Transition>
</n-flex>
<n-switch v-model:value="statusStore.autoClose.enable" :round="false" />
<n-switch
v-model:value="statusStore.autoClose.enable"
:round="false"
@update:value="handleUpdate"
/>
</n-flex>
</n-card>
<!-- 时间选择 -->
@@ -23,6 +27,7 @@
type="primary"
size="large"
round
@click="player.startAutoCloseTimer(item, item * 60)"
>
{{ item }}min
</n-tag>
@@ -35,13 +40,19 @@
type: 'primary',
}"
:show-icon="false"
@positive-click="player.startAutoCloseTimer(customTime, customTime * 60)"
>
<template #trigger>
<n-tag :bordered="false" type="primary" size="large" round> 自定义时长 </n-tag>
</template>
<n-flex vertical>
<n-text>自定义时长分钟</n-text>
<n-input-number v-model:value="statusStore.autoClose.remainTime" />
<n-input-number
v-model:value="customTime"
:min="1"
:max="120"
placeholder="请输入自定义时长"
/>
</n-flex>
</n-popconfirm>
</n-flex>
@@ -55,8 +66,22 @@
<script setup lang="ts">
import { useStatusStore } from "@/stores";
import { convertSecondsToTime } from "@/utils/time";
import player from "@/utils/player";
const statusStore = useStatusStore();
// 自定义时长
const customTime = ref(1);
// 是否开启
const handleUpdate = (value: boolean) => {
if (value) {
player.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.remainTime);
} else {
statusStore.autoClose.enable = false;
statusStore.autoClose.remainTime = statusStore.autoClose.time * 60;
}
};
</script>
<style scoped lang="scss">

View File

@@ -161,11 +161,34 @@
class="play-menu"
justify="end"
>
<!-- 播放时间 -->
<div class="time">
<n-text depth="2">{{ secondsToTime(statusStore.currentTime) }}</n-text>
<n-text depth="2">{{ secondsToTime(statusStore.duration) }}</n-text>
</div>
<!-- 时间相关 -->
<Transition name="fade" mode="out-in">
<n-flex
:key="statusStore.autoClose.enable ? 'autoClose' : 'time'"
:size="4"
justify="center"
class="time-container"
vertical
>
<div class="time">
<n-text depth="2">{{ secondsToTime(statusStore.currentTime) }}</n-text>
<n-text depth="2">{{ secondsToTime(statusStore.duration) }}</n-text>
</div>
<!-- 定时关闭 -->
<n-tag
v-if="statusStore.autoClose.enable"
size="small"
type="primary"
round
@click="openAutoClose"
>
{{ convertSecondsToTime(statusStore.autoClose.remainTime) }}
<template #icon>
<SvgIcon name="TimeAuto" />
</template>
</n-tag>
</n-flex>
</Transition>
<!-- 功能区 -->
<PlayerRightMenu />
</n-flex>
@@ -176,10 +199,16 @@
<script setup lang="ts">
import type { DropdownOption } from "naive-ui";
import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores";
import { secondsToTime, calculateCurrentTime } from "@/utils/time";
import { secondsToTime, calculateCurrentTime, convertSecondsToTime } from "@/utils/time";
import { renderIcon, coverLoaded } from "@/utils/helper";
import { toLikeSong } from "@/utils/auth";
import { openChangeRate, openDownloadSong, openJumpArtist, openPlaylistAdd } from "@/utils/modal";
import {
openAutoClose,
openChangeRate,
openDownloadSong,
openJumpArtist,
openPlaylistAdd,
} from "@/utils/modal";
import player from "@/utils/player";
const router = useRouter();
@@ -476,11 +505,17 @@ const instantLyrics = computed(() => {
}
}
.play-menu {
.time-container {
margin-right: 8px;
.n-tag {
justify-content: center;
font-size: 12px;
}
}
.time {
display: flex;
align-items: center;
font-size: 12px;
margin-right: 8px;
.n-text {
color: var(--primary-hex);
opacity: 0.8;

View File

@@ -140,7 +140,7 @@ const sliderDragend = () => {
height: 100%;
padding: 0 30px;
transition: opacity 0.3s;
.menu-icon {
:deep(.menu-icon) {
display: flex;
align-items: center;
justify-content: center;
@@ -162,6 +162,12 @@ const sliderDragend = () => {
transform: scale(1);
}
}
:deep(.n-badge-sup) {
background-color: rgba(var(--main-color), 0.14);
.n-base-slot-machine {
color: rgb(var(--main-color));
}
}
}
.center {
height: 100%;

View File

@@ -32,6 +32,10 @@ const init = async () => {
);
// 同步播放模式
player.playModeSyncIpc();
// 初始化自动关闭定时器
if (statusStore.autoClose.enable) {
player.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.remainTime);
}
if (isElectron) {
// 注册全局快捷键

View File

@@ -35,7 +35,7 @@ class Player {
// 定时器
private playerInterval: ReturnType<typeof setInterval> | undefined;
// 自动关闭定时器
private autoCloseTimer: ReturnType<typeof setTimeout> | undefined;
private autoCloseInterval: ReturnType<typeof setInterval> | undefined;
// 频谱数据
private audioContext: AudioContext | null = null;
private analyser: AnalyserNode | null = null;
@@ -54,8 +54,6 @@ class Player {
this.player = new Howl({ src: [""], format: allowPlayFormat, autoplay: false });
// 初始化媒体会话
this.initMediaSession();
// 初始化自动关闭定时器
this.toggleAutoCloseTimer();
}
/**
* 获取当前播放歌曲
@@ -429,15 +427,19 @@ class Player {
if (currentSessionId !== this.playSessionId) return;
// statusStore.playStatus = false;
console.log("⏹️ song end:", playSongData);
// 检查是否需要在歌曲结束时执行自动关闭
const statusStore = useStatusStore();
if (statusStore.autoClose.enable && statusStore.autoClose.waitSongEnd && statusStore.autoClose.remainTime <= 0) {
if (
statusStore.autoClose.enable &&
statusStore.autoClose.waitSongEnd &&
statusStore.autoClose.remainTime <= 0
) {
// 执行自动关闭
this.executeAutoClose();
return;
}
this.nextOrPrev("next");
});
// 错误
@@ -899,12 +901,6 @@ class Player {
statusStore.lyricIndex = -1;
statusStore.currentTime = 0;
statusStore.progress = 0;
// 重置自动关闭计时器(切换歌曲时重新开始计时)
if (statusStore.autoClose.enable) {
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
}
// 暂停
await this.pause(false);
// 初始化播放器不传入seek参数确保从头开始播放
@@ -1197,12 +1193,7 @@ class Player {
statusStore.lyricIndex = -1;
statusStore.currentTime = 0;
statusStore.progress = 0;
// 重置自动关闭计时器(切换歌曲时重新开始计时)
if (statusStore.autoClose.enable) {
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
}
// 清理并播放不传入seek参数确保从头开始播放
await this.initPlayer(true, 0);
}
@@ -1446,51 +1437,36 @@ class Player {
}
/**
* 开始定时关闭
* @param time 关闭时间(
* @param waitSongEnd 是否等待歌曲结束
* @param time 关闭时间(分钟
* @param remainTime 剩余时间(秒)
*/
startAutoCloseTimer(time: number, waitSongEnd: boolean = true) {
startAutoCloseTimer(time: number, remainTime: number) {
const statusStore = useStatusStore();
// 清除之前的定时器
clearTimeout(this.autoCloseTimer);
if (!time || !remainTime) return;
// 如已有定时器在运行,先停止以防叠加
if (this.autoCloseInterval) {
clearInterval(this.autoCloseInterval);
this.autoCloseInterval = undefined;
}
// 重置剩余时间
statusStore.autoClose = {
Object.assign(statusStore.autoClose, {
enable: true,
time,
remainTime: time,
waitSongEnd,
};
remainTime,
});
// 开始减少剩余时间
const { pause } = useIntervalFn(() => {
if (statusStore.autoClose.remainTime <= 0) pause();
this.autoCloseInterval = setInterval(() => {
if (statusStore.autoClose.remainTime <= 0) {
clearInterval(this.autoCloseInterval);
this.autoCloseInterval = undefined;
if (!statusStore.autoClose.waitSongEnd) {
this.executeAutoClose();
}
return;
}
statusStore.autoClose.remainTime--;
}, 1000);
// 设置新的定时器
this.autoCloseTimer = setTimeout(() => {
pause();
statusStore.autoClose.remainTime = 0;
// 根据设置决定如何关闭
if (statusStore.autoClose.waitSongEnd) {
// 等待歌曲结束,不在这里执行关闭,而是在歌曲结束事件中处理
console.log("⏰ 自动关闭计时结束,等待当前歌曲播放完毕");
} else {
// 立即执行关闭
this.executeAutoClose();
}
}, time * 1000);
}
/**
* 开启或者停止自动关闭定时器
*/
toggleAutoCloseTimer() {
const statusStore = useStatusStore();
if (statusStore.autoClose.enable) {
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
} else {
clearTimeout(this.autoCloseTimer);
}
}
/**
* 执行自动关闭操作
*/
@@ -1498,20 +1474,10 @@ class Player {
console.log("🔄 执行自动关闭");
// 暂停播放
this.pause();
// 清除定时器
clearTimeout(this.autoCloseTimer);
// 重置状态
const statusStore = useStatusStore();
statusStore.autoClose.enable = false;
statusStore.autoClose.remainTime = 0;
// 如果是 Electron 环境,关闭应用
if (isElectron) {
window.electron.ipcRenderer.send("app-close");
} else {
// 浏览器环境,显示提示信息
window.$message.info("自动关闭已触发,播放已暂停");
}
const { autoClose } = useStatusStore();
autoClose.enable = false;
autoClose.remainTime = autoClose.time * 60;
}
}