mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 11:29:26 +08:00
✨ feat: 新增 定时关闭 & 播放速度
This commit is contained in:
3
components.d.ts
vendored
3
components.d.ts
vendored
@@ -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']
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -161,11 +161,34 @@
|
||||
class="play-menu"
|
||||
justify="end"
|
||||
>
|
||||
<!-- 播放时间 -->
|
||||
<!-- 时间相关 -->
|
||||
<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;
|
||||
|
||||
@@ -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%;
|
||||
|
||||
@@ -32,6 +32,10 @@ const init = async () => {
|
||||
);
|
||||
// 同步播放模式
|
||||
player.playModeSyncIpc();
|
||||
// 初始化自动关闭定时器
|
||||
if (statusStore.autoClose.enable) {
|
||||
player.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.remainTime);
|
||||
}
|
||||
|
||||
if (isElectron) {
|
||||
// 注册全局快捷键
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
/**
|
||||
* 获取当前播放歌曲
|
||||
@@ -432,7 +430,11 @@ class Player {
|
||||
|
||||
// 检查是否需要在歌曲结束时执行自动关闭
|
||||
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;
|
||||
@@ -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参数,确保从头开始播放)
|
||||
@@ -1198,11 +1194,6 @@ class Player {
|
||||
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();
|
||||
statusStore.autoClose.remainTime--;
|
||||
}, 1000);
|
||||
// 设置新的定时器
|
||||
this.autoCloseTimer = setTimeout(() => {
|
||||
pause();
|
||||
statusStore.autoClose.remainTime = 0;
|
||||
// 根据设置决定如何关闭
|
||||
if (statusStore.autoClose.waitSongEnd) {
|
||||
// 等待歌曲结束,不在这里执行关闭,而是在歌曲结束事件中处理
|
||||
console.log("⏰ 自动关闭计时结束,等待当前歌曲播放完毕");
|
||||
} else {
|
||||
// 立即执行关闭
|
||||
this.autoCloseInterval = setInterval(() => {
|
||||
if (statusStore.autoClose.remainTime <= 0) {
|
||||
clearInterval(this.autoCloseInterval);
|
||||
this.autoCloseInterval = undefined;
|
||||
if (!statusStore.autoClose.waitSongEnd) {
|
||||
this.executeAutoClose();
|
||||
}
|
||||
}, time * 1000);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* 开启或者停止自动关闭定时器
|
||||
*/
|
||||
toggleAutoCloseTimer() {
|
||||
const statusStore = useStatusStore();
|
||||
if (statusStore.autoClose.enable) {
|
||||
this.startAutoCloseTimer(statusStore.autoClose.time, statusStore.autoClose.waitSongEnd);
|
||||
} else {
|
||||
clearTimeout(this.autoCloseTimer);
|
||||
statusStore.autoClose.remainTime--;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行自动关闭操作
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user