diff --git a/publish/changeLog.md b/publish/changeLog.md
index a1408a96..bc4ee3f8 100644
--- a/publish/changeLog.md
+++ b/publish/changeLog.md
@@ -2,6 +2,7 @@
- 新增“双击列表里的歌曲时自动切换到当前列表播放”设置,此功能仅对歌单、排行榜有效,默认关闭
- 新增打开收藏的在线列表的对应平台详情页功能,可以在我的列表-列表右键菜单中使用
+- 新增定时暂停播放功能,由于此功能大多数人可能不常用,所以将其放在设置-基本设置中
### 优化
diff --git a/src/common/defaultSetting.js b/src/common/defaultSetting.js
index 965c9686..f7ab6228 100644
--- a/src/common/defaultSetting.js
+++ b/src/common/defaultSetting.js
@@ -2,7 +2,7 @@ const path = require('path')
const os = require('os')
const defaultSetting = {
- version: '1.0.49',
+ version: '1.0.50',
player: {
togglePlayMethod: 'listLoop',
highQuality: false,
@@ -16,6 +16,8 @@ const defaultSetting = {
isPlayLxlrc: true,
isSavePlayTime: false,
audioVisualization: false,
+ waitPlayEndStop: true,
+ waitPlayEndStopTime: 0,
},
desktopLyric: {
enable: false,
diff --git a/src/common/ipcNames.js b/src/common/ipcNames.js
index 7586e083..2293a819 100644
--- a/src/common/ipcNames.js
+++ b/src/common/ipcNames.js
@@ -11,6 +11,9 @@ const names = {
clear_env_params_deeplink: 'clear_env_params_deeplink',
wait: 'wait',
wait_cancel: 'wait_cancel',
+ interval: 'interval',
+ interval_callback: 'interval_callback',
+ interval_cancel: 'interval_cancel',
open_dev_tools: 'open_dev_tools',
set_music_meta: 'set_music_meta',
diff --git a/src/lang/en-us.json b/src/lang/en-us.json
index 875eb1db..e45e2e78 100644
--- a/src/lang/en-us.json
+++ b/src/lang/en-us.json
@@ -143,6 +143,12 @@
"pagination__next": "Next page",
"pagination__page": "Page {num}",
"pagination__prev": "Previous page",
+ "play_timeout": "Timed pause",
+ "play_timeout_close": "Close",
+ "play_timeout_confirm": "Confirm",
+ "play_timeout_end": "Wait for the song to finish before pausing",
+ "play_timeout_unit": "minute",
+ "play_timeout_update": "Update timing",
"player__add_music_to": "Add the current song to...",
"player__buffering": "Buffering...",
"player__desktop_lyric_lock": "Right click to lock lyrics",
@@ -322,6 +328,7 @@
"setting__play_quality": "Play 320K quality songs first (if supported)",
"setting__play_save_play_time": "Remember playback progress",
"setting__play_task_bar": "Show playing progress on the taskbar",
+ "setting__play_timeout": "Timed pause",
"setting__search": "Search",
"setting__search_focus_search_box": "Automatically focus the search box on startup",
"setting__search_history": "Search history",
diff --git a/src/lang/zh-cn.json b/src/lang/zh-cn.json
index 0e3b4abd..1599efa0 100644
--- a/src/lang/zh-cn.json
+++ b/src/lang/zh-cn.json
@@ -143,6 +143,14 @@
"pagination__next": "下一页",
"pagination__page": "第 {num} 页",
"pagination__prev": "上一页",
+ "play_timeout": "定时暂停",
+ "play_timeout_close": "关闭",
+ "play_timeout_confirm": "确认",
+ "play_timeout_end": "等待歌曲播放完毕再暂停",
+ "play_timeout_stop": "取消定时",
+ "play_timeout_tip": "{time} 后暂停播放",
+ "play_timeout_unit": "分钟",
+ "play_timeout_update": "更新定时",
"player__add_music_to": "添加当前歌曲到...",
"player__buffering": "缓冲中...",
"player__desktop_lyric_lock": "右击锁定歌词",
@@ -322,6 +330,7 @@
"setting__play_quality": "优先播放320K品质的歌曲(如果支持)",
"setting__play_save_play_time": "记住播放进度",
"setting__play_task_bar": "在任务栏上显示当前歌曲播放进度",
+ "setting__play_timeout": "定时暂停",
"setting__search": "搜索设置",
"setting__search_focus_search_box": "启动时自动聚焦搜索框",
"setting__search_history": "显示历史搜索记录",
diff --git a/src/lang/zh-tw.json b/src/lang/zh-tw.json
index 4aedd277..fad40b87 100644
--- a/src/lang/zh-tw.json
+++ b/src/lang/zh-tw.json
@@ -143,6 +143,12 @@
"pagination__next": "下一頁",
"pagination__page": "第 {num} 頁",
"pagination__prev": "上一頁",
+ "play_timeout": "定時暫停",
+ "play_timeout_close": "關閉",
+ "play_timeout_confirm": "確認",
+ "play_timeout_end": "等待歌曲播放完畢再暫停",
+ "play_timeout_unit": "分鐘",
+ "play_timeout_update": "更新定時",
"player__add_music_to": "添加當前歌曲到...",
"player__album": "專輯名:",
"player__buffering": "緩衝中...",
@@ -322,6 +328,7 @@
"setting__play_quality": "優先播放320K品質的歌曲(如果支持)",
"setting__play_save_play_time": "記住播放進度",
"setting__play_task_bar": "在任務欄上顯示當前歌曲播放進度",
+ "setting__play_timeout": "定時暫停",
"setting__search": "搜索設置",
"setting__search_focus_search_box": "啟動時自動聚焦搜索框",
"setting__search_history": "顯示歷史搜索記錄",
diff --git a/src/main/rendererEvents/wait.js b/src/main/rendererEvents/wait.js
index a6d99368..ca8d842d 100644
--- a/src/main/rendererEvents/wait.js
+++ b/src/main/rendererEvents/wait.js
@@ -1,4 +1,4 @@
-const { mainOn, mainHandle, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc')
+const { mainOn, mainHandle, mainSend, NAMES: { mainWindow: ipcMainWindowNames } } = require('@common/ipc')
const timeoutMap = new Map()
@@ -23,3 +23,24 @@ mainOn(ipcMainWindowNames.wait_cancel, (event, id) => {
clearTimeout(timeout.timeout)
timeout.reject('cancelled')
})
+
+mainOn(ipcMainWindowNames.interval, (event, { time, id }) => {
+ if (timeoutMap.has(id)) return
+ const timeout = setInterval(() => {
+ if (global.modules.mainWindow) mainSend(global.modules.mainWindow, ipcMainWindowNames.interval_callback, id)
+ }, time)
+
+ timeoutMap.set(id, {
+ timeout,
+ type: 'interval',
+ time,
+ })
+})
+
+mainOn(ipcMainWindowNames.interval_cancel, (event, id) => {
+ if (!timeoutMap.has(id)) return
+ const timeout = timeoutMap.get(id)
+ timeoutMap.delete(id)
+ if (timeout.type != 'interval') return
+ clearInterval(timeout.timeout)
+})
diff --git a/src/renderer/components/base/Input.vue b/src/renderer/components/base/Input.vue
index ec3a5a22..9ae89be4 100644
--- a/src/renderer/components/base/Input.vue
+++ b/src/renderer/components/base/Input.vue
@@ -24,7 +24,7 @@ export default {
default: false,
},
modelValue: {
- type: String,
+ type: [String, Number],
default: '',
},
type: {
diff --git a/src/renderer/core/useApp/usePlayer/index.js b/src/renderer/core/useApp/usePlayer/index.js
index 72c59941..479df21c 100644
--- a/src/renderer/core/useApp/usePlayer/index.js
+++ b/src/renderer/core/useApp/usePlayer/index.js
@@ -4,6 +4,7 @@ import {
import useMediaDevice from './useMediaDevice'
import usePlayerEvent from './usePlayerEvent'
import usePlayer from './usePlayer'
+import { init as initPlayTimeoutStop } from '@renderer/utils/timeoutStop'
export default ({ setting }) => {
createAudio()
@@ -11,5 +12,7 @@ export default ({ setting }) => {
usePlayerEvent()
useMediaDevice({ setting }) // 初始化音频驱动输出设置
usePlayer({ setting })
+
+ initPlayTimeoutStop()
}
diff --git a/src/renderer/core/useApp/usePlayer/usePlayEvent.js b/src/renderer/core/useApp/usePlayer/usePlayEvent.js
index be0ae5f1..76f9836c 100644
--- a/src/renderer/core/useApp/usePlayer/usePlayEvent.js
+++ b/src/renderer/core/useApp/usePlayer/usePlayEvent.js
@@ -49,6 +49,7 @@ export default ({
}
const handleLoadstart = () => {
+ if (global.isPlayedStop) return
startLoadingTimeout()
setAllStatus(t('player__loading'))
}
@@ -77,6 +78,7 @@ export default ({
const handleError = errCode => {
if (!musicInfo.songmid) return
clearLoadingTimeout()
+ if (global.isPlayedStop) return
if (playMusicInfo.listId != 'download' && errCode !== 1 && retryNum < 2) { // 若音频URL无效则尝试刷新2次URL
// console.log(this.retryNum)
retryNum++
@@ -96,6 +98,11 @@ export default ({
clearLoadingTimeout()
}
+ const handlePlayedStop = () => {
+ clearDelayNextTimeout()
+ clearLoadingTimeout()
+ }
+
window.eventHub.on(eventPlayerNames.player_loadstart, handleLoadstart)
window.eventHub.on(eventPlayerNames.player_loadeddata, handleLoadeddata)
@@ -105,6 +112,7 @@ export default ({
window.eventHub.on(eventPlayerNames.player_emptied, handleEmpied)
window.eventHub.on(eventPlayerNames.error, handleError)
window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
+ window.eventHub.on(eventPlayerNames.playedStop, handlePlayedStop)
onBeforeUnmount(() => {
window.eventHub.off(eventPlayerNames.player_loadstart, handleLoadstart)
@@ -115,5 +123,6 @@ export default ({
window.eventHub.off(eventPlayerNames.player_emptied, handleEmpied)
window.eventHub.off(eventPlayerNames.error, handleError)
window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
+ window.eventHub.off(eventPlayerNames.playedStop, handlePlayedStop)
})
}
diff --git a/src/renderer/core/useApp/usePlayer/usePlayer.js b/src/renderer/core/useApp/usePlayer/usePlayer.js
index f0a64c9b..8c436c2c 100644
--- a/src/renderer/core/useApp/usePlayer/usePlayer.js
+++ b/src/renderer/core/useApp/usePlayer/usePlayer.js
@@ -103,11 +103,13 @@ export default ({ setting }) => {
setAllStatus('Try toggle source...')
},
}).then(url => {
+ if (global.isPlayedStop) return
if (targetSong !== musicInfoItem.value || isPlay.value || type != getPlayType(setting.value.player.highQuality, musicInfoItem.value)) return
setMusicInfo({ url })
setResource(url)
}).catch(err => {
// console.log('err', err.message)
+ if (global.isPlayedStop) return
if (targetSong !== musicInfoItem.value || isPlay.value) return
if (err.message == requestMsg.cancelRequest) return
if (!isRetryed) return setUrl(targetSong, isRefresh, true)
@@ -198,6 +200,7 @@ export default ({ setting }) => {
const setPauseStatus = () => {
setPlay(false)
setTitle()
+ if (global.isPlayedStop) handlePause()
}
const setStopStatus = () => {
setPlay(false)
@@ -288,11 +291,16 @@ export default ({ setting }) => {
const handleEnded = () => {
setAllStatus(t('player__end'))
+
+ if (global.isPlayedStop) return
playNext()
}
- // 播放、暂停播放切换
- const handleTogglePlay = async() => {
+ const handlePause = () => {
+ setPlayerPause()
+ }
+
+ const handlePlay = async() => {
if (playMusicInfo.musicInfo == null) return
if (isPlayerEmpty()) {
if (playMusicInfo.listId == 'download') {
@@ -313,13 +321,24 @@ export default ({ setting }) => {
}
return
}
+ setPlayerPlay()
+ }
+
+ // 播放、暂停播放切换
+ const handleTogglePlay = () => {
+ if (global.isPlayedStop) global.isPlayedStop = false
if (isPlay.value) {
- setPlayerPause()
+ handlePause()
} else {
- setPlayerPlay()
+ handlePlay()
}
}
+ const handlePlayedStop = () => {
+ clearDelayNextTimeout()
+ clearLoadTimeout()
+ }
+
watch(() => setting.value.player.togglePlayMethod, newValue => {
setLoopPlay(newValue === 'singleLoop')
if (playedList.length) clearPlayedList()
@@ -339,13 +358,16 @@ export default ({ setting }) => {
window.eventHub.on(eventPlayerNames.stop, setStopStatus)
window.eventHub.on(eventPlayerNames.playMusic, playMusic)
+ window.eventHub.on(eventPlayerNames.setPlay, handlePlay)
+ window.eventHub.on(eventPlayerNames.setPause, handlePause)
+ window.eventHub.on(eventPlayerNames.setStop, handelStop)
window.eventHub.on(eventPlayerNames.setTogglePlay, handleTogglePlay)
window.eventHub.on(eventPlayerNames.setPlayPrev, playPrev)
window.eventHub.on(eventPlayerNames.setPlayNext, playNext)
window.eventHub.on(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
- window.eventHub.on(eventPlayerNames.setStop, handelStop)
window.eventHub.on(eventPlayerNames.player_ended, handleEnded)
+ window.eventHub.on(eventPlayerNames.playedStop, handlePlayedStop)
onBeforeUnmount(() => {
@@ -360,11 +382,14 @@ export default ({ setting }) => {
window.eventHub.off(eventPlayerNames.playMusic, playMusic)
window.eventHub.off(eventPlayerNames.setTogglePlay, handleTogglePlay)
+ window.eventHub.off(eventPlayerNames.setPlay, handlePlay)
+ window.eventHub.off(eventPlayerNames.setPause, handlePause)
+ window.eventHub.off(eventPlayerNames.setStop, handelStop)
window.eventHub.off(eventPlayerNames.setPlayPrev, playPrev)
window.eventHub.off(eventPlayerNames.setPlayNext, playNext)
window.eventHub.off(eventPlayerNames.setPlayInfo, handleSetPlayInfo)
- window.eventHub.off(eventPlayerNames.setStop, handelStop)
window.eventHub.off(eventPlayerNames.player_ended, handleEnded)
+ window.eventHub.off(eventPlayerNames.playedStop, handlePlayedStop)
})
}
diff --git a/src/renderer/core/useApp/usePlayer/useWatchList.js b/src/renderer/core/useApp/usePlayer/useWatchList.js
index 84ba6708..284fbbd5 100644
--- a/src/renderer/core/useApp/usePlayer/useWatchList.js
+++ b/src/renderer/core/useApp/usePlayer/useWatchList.js
@@ -15,7 +15,7 @@ export default ({ playNext }) => {
const { playIndex } = updatePlayIndex()
if (playIndex < 0 && !playMusicInfo.isTempPlay) { // 歌曲被移除
- if (getList(playMusicInfo.listId).length) {
+ if (getList(playMusicInfo.listId).length && !global.isPlayedStop) {
playNext()
} else {
window.eventHub.emit(eventPlayerNames.setStop)
diff --git a/src/renderer/event/names.js b/src/renderer/event/names.js
index 2216018d..651f4d47 100644
--- a/src/renderer/event/names.js
+++ b/src/renderer/event/names.js
@@ -15,6 +15,7 @@ const names = {
},
player: {
setTogglePlay: 'setTogglePlay', // 播放/暂停切换
+ setPlay: 'setPlay', // 播放
setPause: 'setPause', // 暂停
setStop: 'setStop', // 停止
setPlayPrev: 'setPlayPrev', // 上一曲
@@ -31,6 +32,7 @@ const names = {
updateLyric: 'updateLyric',
activeTransition: 'activeTransition', // 激活进度条动画事件
+ playedStop: 'playedStop', // 定时停止事件
// 播放器事件
play: 'play',
diff --git a/src/renderer/store/modules/player.js b/src/renderer/store/modules/player.js
index a6e42dca..7e54afc3 100644
--- a/src/renderer/store/modules/player.js
+++ b/src/renderer/store/modules/player.js
@@ -32,6 +32,7 @@ const state = {
}
const playMusic = () => {
+ if (global.isPlayedStop) global.isPlayedStop = false
window.eventHub.emit(eventPlayerNames.playMusic)
}
diff --git a/src/renderer/utils/timeoutStop.js b/src/renderer/utils/timeoutStop.js
new file mode 100644
index 00000000..c06d0313
--- /dev/null
+++ b/src/renderer/utils/timeoutStop.js
@@ -0,0 +1,93 @@
+import { ref, computed } from '@renderer/utils/vueTools'
+import { rendererSend, rendererOn, NAMES } from '@common/ipc'
+import { isPlay } from '@renderer/core/share/player'
+import store from '@renderer/store'
+import { player as eventPlayerNames } from '@renderer/event/names'
+
+global.isPlayedStop = false
+
+const time = ref(-1)
+
+
+const timeoutTools = {
+ inited: false,
+ isRunning: false,
+ timeout: null,
+ time: -1,
+ id: 'play__stop__timeout',
+ exit() {
+ const setting = store.getters.setting
+ global.isPlayedStop = true
+ if (!setting.player.waitPlayEndStop && isPlay.value) {
+ window.eventHub.emit(eventPlayerNames.setPause)
+ }
+ },
+ clearTimeout() {
+ rendererSend(NAMES.mainWindow.interval_cancel, this.id)
+ if (!this.isRunning) return
+ this.time = -1
+ time.value = -1
+ this.isRunning = false
+ },
+ start(_time) {
+ this.clearTimeout()
+ this.time = _time
+ time.value = _time
+ this.isRunning = true
+ rendererSend(NAMES.mainWindow.interval, {
+ time: 1000,
+ id: this.id,
+ })
+ },
+ init() {
+ if (this.inited) return
+ this.clearTimeout()
+ rendererOn(NAMES.mainWindow.interval_callback, (event, id) => {
+ if (id !== this.id) return
+
+ if (this.time > 0) {
+ this.time--
+ time.value--
+ } else {
+ this.clearTimeout()
+ this.exit()
+ }
+ })
+ this.inited = true
+ },
+}
+
+export const init = () => {
+ timeoutTools.init()
+}
+
+export const startTimeoutStop = time => {
+ if (global.isPlayedStop) global.isPlayedStop = false
+ timeoutTools.start(time)
+}
+export const stopTimeoutStop = () => {
+ if (global.isPlayedStop) global.isPlayedStop = false
+ timeoutTools.clearTimeout()
+}
+
+const formatTime = time => {
+ // let d = parseInt(time / 86400)
+ // d = d ? d.toString() + ':' : ''
+ // time = time % 86400
+ let h = parseInt(time / 3600)
+ h = h ? h.toString() + ':' : ''
+ time = time % 3600
+ const m = parseInt(time / 60).toString().padStart(2, '0')
+ const s = parseInt(time % 60).toString().padStart(2, '0')
+ return `${h}${m}:${s}`
+}
+export const useTimeout = () => {
+ const timeLabel = computed(() => {
+ return time.value > 0 ? formatTime(time.value) : ''
+ })
+
+ return {
+ time,
+ timeLabel,
+ }
+}
diff --git a/src/renderer/views/setting/components/PlayTimeoutModal.vue b/src/renderer/views/setting/components/PlayTimeoutModal.vue
new file mode 100644
index 00000000..0d0b43b4
--- /dev/null
+++ b/src/renderer/views/setting/components/PlayTimeoutModal.vue
@@ -0,0 +1,175 @@
+
+material-modal(:show="modelValue" bg-close @close="handleCloseModal" @after-enter="$refs.dom_input.focus()" teleport="#view")
+ main(:class="$style.main")
+ h2 {{$t('play_timeout')}}
+ div(:class="$style.content")
+ div(:class="[$style.row, $style.inputGroup]")
+ base-input(:class="$style.input" ref="dom_input" v-model="time" type="number")
+ p(:class="$style.inputLabel") {{$t('play_timeout_unit')}}
+ div(:class="$style.row")
+ base-checkbox(id="play_timeout_end" v-model="currentStting.player.waitPlayEndStop" :label="$t('play_timeout_end')")
+ div(:class="[$style.row, $style.tip, { [$style.show]: !!timeLabel }]")
+ p {{$t('play_timeout_tip', { time: timeLabel })}}
+ div(:class="$style.footer")
+ base-btn(:class="$style.footerBtn" @click="handleCancel") {{$t(timeLabel ? 'play_timeout_stop' : 'play_timeout_close')}}
+ base-btn(:class="$style.footerBtn" @click="handleConfirm") {{$t(timeLabel ? 'play_timeout_update' : 'play_timeout_confirm')}}
+
+
+
+
+
+
diff --git a/src/renderer/views/setting/components/SettingBasic.vue b/src/renderer/views/setting/components/SettingBasic.vue
index d5724a3f..f530b98b 100644
--- a/src/renderer/views/setting/components/SettingBasic.vue
+++ b/src/renderer/views/setting/components/SettingBasic.vue
@@ -9,12 +9,15 @@ dd
label {{$t('theme_' + theme.className)}}
dd
- .gap-top.top
- base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
- .gap-top
- base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
- .gap-top
- base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
+ div
+ .gap-top.top
+ base-checkbox(id="setting_show_animate" v-model="currentStting.isShowAnimation" :label="$t('setting__basic_show_animation')")
+ .gap-top
+ base-checkbox(id="setting_animate" v-model="currentStting.randomAnimate" :label="$t('setting__basic_animation')")
+ .gap-top
+ base-checkbox(id="setting_to_tray" v-model="currentStting.tray.isShow" :label="$t('setting__basic_to_tray')")
+ p.gap-top
+ base-btn.btn(min @click="isShowPlayTimeoutModal = true") {{$t('setting__play_timeout')}} {{ timeLabel ? ` (${timeLabel})` : '' }}
dd(:tips="$t('setting__basic_source_title')")
h3#basic_source {{$t('setting__basic_source')}}
@@ -48,6 +51,7 @@ dd
div
base-checkbox.gap-left(v-for="item in controlBtnPositionList" :key="item.id" :id="`setting_basic_control_btn_position_${item.id}`"
name="setting_basic_control_btn_position" need v-model="currentStting.controlBtnPosition" :value="item.id" :label="item.name")
+play-timeout-modal(v-model="isShowPlayTimeoutModal")
user-api-modal(v-model="isShowUserApiModal")
@@ -58,12 +62,15 @@ import { langList } from '@/lang'
import { currentStting } from '../setting'
import { setWindowSize } from '@renderer/utils'
import apiSourceInfo from '@renderer/utils/music/api-source-info'
+import { useTimeout } from '@renderer/utils/timeoutStop'
+import PlayTimeoutModal from './PlayTimeoutModal'
import UserApiModal from './UserApiModal'
export default {
name: 'SettingBasic',
components: {
+ PlayTimeoutModal,
UserApiModal,
},
setup() {
@@ -81,6 +88,9 @@ export default {
apiSource.value = visible
})
+ const isShowPlayTimeoutModal = ref(false)
+ const { timeLabel } = useTimeout()
+
const isShowUserApiModal = ref(false)
const getApiStatus = () => {
let status
@@ -138,6 +148,8 @@ export default {
return {
currentStting,
themes,
+ isShowPlayTimeoutModal,
+ timeLabel,
apiSources,
isShowUserApiModal,
windowSizeList,
diff --git a/src/renderer/views/setting/setting.js b/src/renderer/views/setting/setting.js
index 8b007922..8b753255 100644
--- a/src/renderer/views/setting/setting.js
+++ b/src/renderer/views/setting/setting.js
@@ -8,6 +8,8 @@ export const currentStting = ref({
volume: 1,
mediaDeviceId: 'default',
isMediaDeviceRemovedStopPlay: false,
+ waitPlayEndStop: true,
+ waitPlayEndStopTime: 0,
},
desktopLyric: {
enable: false,