新增播放速率调整功能
parent
9522e975a0
commit
f809782559
|
@ -7,6 +7,7 @@
|
||||||
- 新增是否自动下载更新设置,默认开启,可以去设置-软件更新更改
|
- 新增是否自动下载更新设置,默认开启,可以去设置-软件更新更改
|
||||||
- 新增当前版本更新日志显示弹窗(建议大家阅读更新日志以了解当前版本的变化),在更新版本后将自动弹出
|
- 新增当前版本更新日志显示弹窗(建议大家阅读更新日志以了解当前版本的变化),在更新版本后将自动弹出
|
||||||
- 新增是否在更新版本的首次启动时显示更新日志弹窗设置,默认开启,可以去设置-软件更新更改
|
- 新增是否在更新版本的首次启动时显示更新日志弹窗设置,默认开启,可以去设置-软件更新更改
|
||||||
|
- 新增播放速率调整功能,可以去播放详情页的控制按钮调整,范围限制为x0.5至x2之间(#13)
|
||||||
- 添加wy、tx源逐字歌词的支持
|
- 添加wy、tx源逐字歌词的支持
|
||||||
- 添加启动时的数据库表及表结构完整性校验,若未通过校验,则会显示弹窗提示后将该数据库重命名添加`.bak`后缀后重建数据库启动。对于某些人遇到更新到v2.0.0后出现之前收藏的歌曲全部丢失或者歌曲无法添加到列表的问题,可以通过此特性自动重建数据库并重新迁移数据,不再需要手动去数据目录删除数据库
|
- 添加启动时的数据库表及表结构完整性校验,若未通过校验,则会显示弹窗提示后将该数据库重命名添加`.bak`后缀后重建数据库启动。对于某些人遇到更新到v2.0.0后出现之前收藏的歌曲全部丢失或者歌曲无法添加到列表的问题,可以通过此特性自动重建数据库并重新迁移数据,不再需要手动去数据目录删除数据库
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ const defaultSetting: LX.AppSetting = {
|
||||||
'player.isShowTaskProgess': true,
|
'player.isShowTaskProgess': true,
|
||||||
'player.volume': 1,
|
'player.volume': 1,
|
||||||
'player.isMute': false,
|
'player.isMute': false,
|
||||||
|
'player.playbackRate': 1,
|
||||||
'player.mediaDeviceId': 'default',
|
'player.mediaDeviceId': 'default',
|
||||||
'player.isMediaDeviceRemovedStopPlay': false,
|
'player.isMediaDeviceRemovedStopPlay': false,
|
||||||
'player.isShowLyricTranslation': false,
|
'player.isShowLyricTranslation': false,
|
||||||
|
|
|
@ -108,6 +108,11 @@ declare global {
|
||||||
*/
|
*/
|
||||||
'player.isMute': boolean
|
'player.isMute': boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放速率
|
||||||
|
*/
|
||||||
|
'player.playbackRate': number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 音频输出设备id
|
* 音频输出设备id
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -62,6 +62,7 @@ declare namespace LX {
|
||||||
| LyricAction<'set_status', {
|
| LyricAction<'set_status', {
|
||||||
isPlay: boolean
|
isPlay: boolean
|
||||||
line: number
|
line: number
|
||||||
|
rate: number
|
||||||
played_time: number
|
played_time: number
|
||||||
}>
|
}>
|
||||||
| LyricAction<'set_lyric', {
|
| LyricAction<'set_lyric', {
|
||||||
|
@ -71,6 +72,7 @@ declare namespace LX {
|
||||||
lxlrc: string | null
|
lxlrc: string | null
|
||||||
}>
|
}>
|
||||||
| LyricAction<'set_offset', number>
|
| LyricAction<'set_offset', number>
|
||||||
|
| LyricAction<'set_playbackRate', number>
|
||||||
| LyricAction<'set_play', number>
|
| LyricAction<'set_play', number>
|
||||||
| LyricAction<'set_pause'>
|
| LyricAction<'set_pause'>
|
||||||
| LyricAction<'set_stop'>
|
| LyricAction<'set_stop'>
|
||||||
|
|
|
@ -28,6 +28,7 @@ const createAnimation = (dom, duration, isVertical) => new window.Animation(new
|
||||||
module.exports = class FontPlayer {
|
module.exports = class FontPlayer {
|
||||||
constructor({
|
constructor({
|
||||||
time = 0,
|
time = 0,
|
||||||
|
rate = 1,
|
||||||
lyric = '',
|
lyric = '',
|
||||||
lineContentClassName = 'line-content',
|
lineContentClassName = 'line-content',
|
||||||
lineClassName = 'line',
|
lineClassName = 'line',
|
||||||
|
@ -43,6 +44,8 @@ module.exports = class FontPlayer {
|
||||||
this.time = time
|
this.time = time
|
||||||
this.lyric = lyric
|
this.lyric = lyric
|
||||||
|
|
||||||
|
this._rate = rate
|
||||||
|
|
||||||
this.isVertical = isVertical
|
this.isVertical = isVertical
|
||||||
|
|
||||||
this.lineContentClassName = lineContentClassName
|
this.lineContentClassName = lineContentClassName
|
||||||
|
@ -136,7 +139,7 @@ module.exports = class FontPlayer {
|
||||||
|
|
||||||
const dom = document.createElement('span')
|
const dom = document.createElement('span')
|
||||||
dom.textContent = text
|
dom.textContent = text
|
||||||
const animation = createAnimation(dom, time, this.isVertical)
|
const animation = createAnimation(dom, time / this._rate, this.isVertical)
|
||||||
this.lrcContent.appendChild(dom)
|
this.lrcContent.appendChild(dom)
|
||||||
// lineText += text
|
// lineText += text
|
||||||
|
|
||||||
|
@ -186,7 +189,7 @@ module.exports = class FontPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentTime() {
|
_currentTime() {
|
||||||
return getNow() - this._performanceTime + this._startTime
|
return (getNow() - this._performanceTime) * this._rate + this._startTime
|
||||||
}
|
}
|
||||||
|
|
||||||
_findcurFontNum(curTime, startIndex = 0) {
|
_findcurFontNum(curTime, startIndex = 0) {
|
||||||
|
@ -201,7 +204,7 @@ module.exports = class FontPlayer {
|
||||||
const currentTime = this._currentTime()
|
const currentTime = this._currentTime()
|
||||||
const driftTime = currentTime - curFont.startTime
|
const driftTime = currentTime - curFont.startTime
|
||||||
if (currentTime > curFont.startTime + curFont.time) {
|
if (currentTime > curFont.startTime + curFont.time) {
|
||||||
this._handlePlayFont(curFont, driftTime, true)
|
this._handlePlayFont(curFont, driftTime / this._rate, true)
|
||||||
this.lineContent.classList.add('played')
|
this.lineContent.classList.add('played')
|
||||||
this.isPlay = false
|
this.isPlay = false
|
||||||
this.pause()
|
this.pause()
|
||||||
|
@ -257,13 +260,13 @@ module.exports = class FontPlayer {
|
||||||
|
|
||||||
if (driftTime >= 0 || this.curFontNum == 0) {
|
if (driftTime >= 0 || this.curFontNum == 0) {
|
||||||
let nextFont = this.fonts[this.curFontNum + 1]
|
let nextFont = this.fonts[this.curFontNum + 1]
|
||||||
this.delay = nextFont.startTime - curFont.startTime - driftTime
|
const delay = (nextFont.startTime - curFont.startTime - driftTime) / this._rate
|
||||||
if (this.delay > 0) {
|
if (delay > 0) {
|
||||||
if (this.isPlay) {
|
if (this.isPlay) {
|
||||||
this.timeoutTools.start(() => {
|
this.timeoutTools.start(() => {
|
||||||
if (!this.isPlay) return
|
if (!this.isPlay) return
|
||||||
this._refresh()
|
this._refresh()
|
||||||
}, this.delay)
|
}, delay)
|
||||||
}
|
}
|
||||||
this._handlePlayFont(curFont, driftTime)
|
this._handlePlayFont(curFont, driftTime)
|
||||||
return
|
return
|
||||||
|
@ -342,6 +345,13 @@ module.exports = class FontPlayer {
|
||||||
this.curFontNum = this.maxFontNum
|
this.curFontNum = this.maxFontNum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPlaybackRate(rate) {
|
||||||
|
this._rate = rate
|
||||||
|
if (!this.lines.length) return
|
||||||
|
if (!this.isPlay) return
|
||||||
|
this.play(this._currentTime())
|
||||||
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.pause()
|
this.pause()
|
||||||
if (this.isLineMode) return this._handlePlayLine(false)
|
if (this.isLineMode) return this._handlePlayLine(false)
|
||||||
|
|
|
@ -8,6 +8,7 @@ module.exports = class Lyric {
|
||||||
lyric = '',
|
lyric = '',
|
||||||
extendedLyrics = [],
|
extendedLyrics = [],
|
||||||
offset = 0,
|
offset = 0,
|
||||||
|
rate = 1,
|
||||||
lineContentClassName = 'line-content',
|
lineContentClassName = 'line-content',
|
||||||
lineClassName = 'line',
|
lineClassName = 'line',
|
||||||
shadowClassName = 'shadow',
|
shadowClassName = 'shadow',
|
||||||
|
@ -25,6 +26,7 @@ module.exports = class Lyric {
|
||||||
this.lyric = lyric
|
this.lyric = lyric
|
||||||
this.extendedLyrics = extendedLyrics
|
this.extendedLyrics = extendedLyrics
|
||||||
this.offset = offset
|
this.offset = offset
|
||||||
|
this.rate = rate
|
||||||
this.onPlay = onPlay
|
this.onPlay = onPlay
|
||||||
this.onSetLyric = onSetLyric
|
this.onSetLyric = onSetLyric
|
||||||
this.onUpdateLyric = onUpdateLyric
|
this.onUpdateLyric = onUpdateLyric
|
||||||
|
@ -50,6 +52,7 @@ module.exports = class Lyric {
|
||||||
|
|
||||||
this.linePlayer = new LinePlayer({
|
this.linePlayer = new LinePlayer({
|
||||||
offset: this.offset,
|
offset: this.offset,
|
||||||
|
rate: this.rate,
|
||||||
onPlay: this._handleLinePlayerOnPlay,
|
onPlay: this._handleLinePlayerOnPlay,
|
||||||
onSetLyric: this._handleLinePlayerOnSetLyric,
|
onSetLyric: this._handleLinePlayerOnSetLyric,
|
||||||
})
|
})
|
||||||
|
@ -116,6 +119,7 @@ module.exports = class Lyric {
|
||||||
this._lines = lyricLines.map(line => {
|
this._lines = lyricLines.map(line => {
|
||||||
const fontPlayer = new FontPlayer({
|
const fontPlayer = new FontPlayer({
|
||||||
time: line.time,
|
time: line.time,
|
||||||
|
rate: this.rate,
|
||||||
lyric: line.text,
|
lyric: line.text,
|
||||||
extendedLyrics: line.extendedLyrics,
|
extendedLyrics: line.extendedLyrics,
|
||||||
lineContentClassName: this.lineContentClassName,
|
lineContentClassName: this.lineContentClassName,
|
||||||
|
@ -141,6 +145,7 @@ module.exports = class Lyric {
|
||||||
this._lines = lyricLines.map(line => {
|
this._lines = lyricLines.map(line => {
|
||||||
const fontPlayer = new FontPlayer({
|
const fontPlayer = new FontPlayer({
|
||||||
time: line.time,
|
time: line.time,
|
||||||
|
rate: this.rate,
|
||||||
lyric: line.text,
|
lyric: line.text,
|
||||||
extendedLyrics: line.extendedLyrics,
|
extendedLyrics: line.extendedLyrics,
|
||||||
lineContentClassName: this.lineContentClassName,
|
lineContentClassName: this.lineContentClassName,
|
||||||
|
@ -200,6 +205,17 @@ module.exports = class Lyric {
|
||||||
this._init()
|
this._init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPlaybackRate(rate) {
|
||||||
|
this.rate = rate
|
||||||
|
this.linePlayer.setPlaybackRate(rate)
|
||||||
|
this._initLines(this.initInfo.lines, this.initInfo.offset, true)
|
||||||
|
if (this.linePlayer.isPlay) {
|
||||||
|
const num = this.playingLineNum
|
||||||
|
this.playingLineNum = 0
|
||||||
|
this._handleLinePlayerOnPlay(num, '', this.linePlayer._currentTime())
|
||||||
|
} else this.playingLineNum = 0
|
||||||
|
}
|
||||||
|
|
||||||
setVertical(isVertical) {
|
setVertical(isVertical) {
|
||||||
this.isVertical = isVertical
|
this.isVertical = isVertical
|
||||||
this._initLines(this.initInfo.lines, this.initInfo.offset, true)
|
this._initLines(this.initInfo.lines, this.initInfo.offset, true)
|
||||||
|
|
|
@ -38,7 +38,7 @@ const parseExtendedLyric = (lrcLinesMap, extendedLyric) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = class LinePlayer {
|
module.exports = class LinePlayer {
|
||||||
constructor({ offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) {
|
constructor({ offset = 0, rate = 1, onPlay = function() { }, onSetLyric = function() { } } = {}) {
|
||||||
this.tags = {}
|
this.tags = {}
|
||||||
this.lines = null
|
this.lines = null
|
||||||
this.onPlay = onPlay
|
this.onPlay = onPlay
|
||||||
|
@ -49,6 +49,7 @@ module.exports = class LinePlayer {
|
||||||
this.offset = offset
|
this.offset = offset
|
||||||
this._performanceTime = 0
|
this._performanceTime = 0
|
||||||
this._startTime = 0
|
this._startTime = 0
|
||||||
|
this._rate = rate
|
||||||
}
|
}
|
||||||
|
|
||||||
_init() {
|
_init() {
|
||||||
|
@ -119,7 +120,7 @@ module.exports = class LinePlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentTime() {
|
_currentTime() {
|
||||||
return getNow() - this._performanceTime + this._startTime
|
return (getNow() - this._performanceTime) * this._rate + this._startTime
|
||||||
}
|
}
|
||||||
|
|
||||||
_findCurLineNum(curTime, startIndex = 0) {
|
_findCurLineNum(curTime, startIndex = 0) {
|
||||||
|
@ -146,14 +147,14 @@ module.exports = class LinePlayer {
|
||||||
|
|
||||||
if (driftTime >= 0 || this.curLineNum === 0) {
|
if (driftTime >= 0 || this.curLineNum === 0) {
|
||||||
let nextLine = this.lines[this.curLineNum + 1]
|
let nextLine = this.lines[this.curLineNum + 1]
|
||||||
this.delay = nextLine.time - curLine.time - driftTime
|
const delay = (nextLine.time - curLine.time - driftTime) / this._rate
|
||||||
|
|
||||||
if (this.delay > 0) {
|
if (delay > 0) {
|
||||||
if (this.isPlay) {
|
if (this.isPlay) {
|
||||||
timeoutTools.start(() => {
|
timeoutTools.start(() => {
|
||||||
if (!this.isPlay) return
|
if (!this.isPlay) return
|
||||||
this._refresh()
|
this._refresh()
|
||||||
}, this.delay)
|
}, delay)
|
||||||
}
|
}
|
||||||
this.onPlay(this.curLineNum, curLine.text, currentTime)
|
this.onPlay(this.curLineNum, curLine.text, currentTime)
|
||||||
return
|
return
|
||||||
|
@ -195,6 +196,13 @@ module.exports = class LinePlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPlaybackRate(rate) {
|
||||||
|
this._rate = rate
|
||||||
|
if (!this.lines.length) return
|
||||||
|
if (!this.isPlay) return
|
||||||
|
this.play(this._currentTime())
|
||||||
|
}
|
||||||
|
|
||||||
setLyric(lyric, extendedLyrics) {
|
setLyric(lyric, extendedLyrics) {
|
||||||
// console.log(extendedLyrics)
|
// console.log(extendedLyrics)
|
||||||
if (this.isPlay) this.pause()
|
if (this.isPlay) this.pause()
|
||||||
|
|
|
@ -218,6 +218,8 @@
|
||||||
"player__play_toggle_mode_off": "Disable",
|
"player__play_toggle_mode_off": "Disable",
|
||||||
"player__play_toggle_mode_random": "List Random",
|
"player__play_toggle_mode_random": "List Random",
|
||||||
"player__play_toggle_mode_single_loop": "Single Loop",
|
"player__play_toggle_mode_single_loop": "Single Loop",
|
||||||
|
"player__playback_rate": "Current playback rate:",
|
||||||
|
"player__playback_rate_reset_btn": "Reset",
|
||||||
"player__playing": "Now playing...",
|
"player__playing": "Now playing...",
|
||||||
"player__prev": "Prev",
|
"player__prev": "Prev",
|
||||||
"player__refresh_url": "Music URL expired, refreshing...",
|
"player__refresh_url": "Music URL expired, refreshing...",
|
||||||
|
|
|
@ -218,6 +218,8 @@
|
||||||
"player__play_toggle_mode_off": "禁用",
|
"player__play_toggle_mode_off": "禁用",
|
||||||
"player__play_toggle_mode_random": "列表随机",
|
"player__play_toggle_mode_random": "列表随机",
|
||||||
"player__play_toggle_mode_single_loop": "单曲循环",
|
"player__play_toggle_mode_single_loop": "单曲循环",
|
||||||
|
"player__playback_rate": "当前播放速率:",
|
||||||
|
"player__playback_rate_reset_btn": "重置",
|
||||||
"player__playing": "播放中...",
|
"player__playing": "播放中...",
|
||||||
"player__prev": "上一首",
|
"player__prev": "上一首",
|
||||||
"player__refresh_url": "URL过期,正在刷新URL...",
|
"player__refresh_url": "URL过期,正在刷新URL...",
|
||||||
|
|
|
@ -218,6 +218,8 @@
|
||||||
"player__play_toggle_mode_off": "禁用",
|
"player__play_toggle_mode_off": "禁用",
|
||||||
"player__play_toggle_mode_random": "列表隨機",
|
"player__play_toggle_mode_random": "列表隨機",
|
||||||
"player__play_toggle_mode_single_loop": "單曲循環",
|
"player__play_toggle_mode_single_loop": "單曲循環",
|
||||||
|
"player__playback_rate": "當前播放速率:",
|
||||||
|
"player__playback_rate_reset_btn": "重置",
|
||||||
"player__playing": "播放中...",
|
"player__playing": "播放中...",
|
||||||
"player__prev": "上一首",
|
"player__prev": "上一首",
|
||||||
"player__refresh_url": "URL過期,正在刷新URL...",
|
"player__refresh_url": "URL過期,正在刷新URL...",
|
||||||
|
|
|
@ -33,6 +33,10 @@ export const setLyricOffset = (offset: number) => {
|
||||||
lrc.setOffset(offset)
|
lrc.setOffset(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setPlaybackRate = (rate: number) => {
|
||||||
|
lrc.setPlaybackRate(rate)
|
||||||
|
}
|
||||||
|
|
||||||
export const setLyric = () => {
|
export const setLyric = () => {
|
||||||
if (!musicInfo.id) return
|
if (!musicInfo.id) return
|
||||||
const extendedLyrics = []
|
const extendedLyrics = []
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { onProvideMainWindowChannel } from '@lyric/utils/ipc'
|
import { onProvideMainWindowChannel } from '@lyric/utils/ipc'
|
||||||
import { onBeforeUnmount } from '@common/utils/vueTools'
|
import { onBeforeUnmount } from '@common/utils/vueTools'
|
||||||
import { setMusicInfo, setIsPlay } from '../store/action'
|
import { setMusicInfo, setIsPlay } from '../store/action'
|
||||||
import { pause, play, setLyric, setLyricOffset, stop } from './lyric'
|
import { pause, play, setLyric, setLyricOffset, setPlaybackRate, stop } from './lyric'
|
||||||
import { lyrics } from '@lyric/store/lyric'
|
import { lyrics } from '@lyric/store/lyric'
|
||||||
|
|
||||||
let mainWindowPort: Electron.IpcRendererEvent['ports'][0] | null = null
|
let mainWindowPort: Electron.IpcRendererEvent['ports'][0] | null = null
|
||||||
|
@ -41,12 +41,16 @@ const handleDesktopLyricMessage = (event: LX.DesktopLyric.LyricActions) => {
|
||||||
break
|
break
|
||||||
case 'set_status':
|
case 'set_status':
|
||||||
setIsPlay(event.data.isPlay)
|
setIsPlay(event.data.isPlay)
|
||||||
|
setPlaybackRate(event.data.rate)
|
||||||
if (event.data.isPlay) play(event.data.played_time)
|
if (event.data.isPlay) play(event.data.played_time)
|
||||||
else pause()
|
else pause()
|
||||||
break
|
break
|
||||||
case 'set_offset':
|
case 'set_offset':
|
||||||
setLyricOffset(event.data)
|
setLyricOffset(event.data)
|
||||||
break
|
break
|
||||||
|
case 'set_playbackRate':
|
||||||
|
setPlaybackRate(event.data)
|
||||||
|
break
|
||||||
case 'set_pause':
|
case 'set_pause':
|
||||||
setIsPlay(false)
|
setIsPlay(false)
|
||||||
pause()
|
pause()
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="144" height="144">
|
||||||
|
<path fill="currentColor" d="M11.5 6C8.467 6 6 8.467 6 11.5L6 36.5C6 39.533 8.467 42 11.5 42L36.5 42C39.533 42 42 39.533 42 36.5L42 11.5C42 8.467 39.533 6 36.5 6L11.5 6 z M 11.5 9L36.5 9C37.878 9 39 10.122 39 11.5L39 36.5C39 37.878 37.878 39 36.5 39L11.5 39C10.122 39 9 37.878 9 36.5L9 11.5C9 10.122 10.122 9 11.5 9 z M 18.167969 13.074219L25.197266 24L18.167969 34.925781L24.644531 34.925781L31.753906 24L24.644531 13.074219L18.167969 13.074219 z" />
|
||||||
|
</svg>
|
|
@ -0,0 +1,140 @@
|
||||||
|
<template>
|
||||||
|
<div :class="[$style.sliderContent, className]">
|
||||||
|
<div :class="[$style.slider ]">
|
||||||
|
<div ref="dom_sliderBar" :class="$style.sliderBar" :style="{ transform: `scaleX(${(value - min) / (max - min) || 0})` }" />
|
||||||
|
</div>
|
||||||
|
<div :class="$style.sliderMask" @mousedown="handleSliderMsDown" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref, onBeforeUnmount } from '@common/utils/vueTools'
|
||||||
|
// import { player as eventPlayerNames } from '@renderer/event/names'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
className: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
min: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ['change'],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const sliderEvent = {
|
||||||
|
isMsDown: false,
|
||||||
|
msDownX: 0,
|
||||||
|
msDownValue: 0,
|
||||||
|
}
|
||||||
|
const dom_sliderBar = ref(null)
|
||||||
|
|
||||||
|
const handleSliderMsDown = event => {
|
||||||
|
sliderEvent.isMsDown = true
|
||||||
|
sliderEvent.msDownX = event.clientX
|
||||||
|
|
||||||
|
sliderEvent.msDownValue = event.offsetX / dom_sliderBar.value.clientWidth
|
||||||
|
let val = sliderEvent.msDownValue * (props.max - props.min) + props.min
|
||||||
|
if (val < props.min) val = props.min
|
||||||
|
if (val > props.max) val = props.max
|
||||||
|
emit('change', val)
|
||||||
|
|
||||||
|
// if (isMute.value) window.app_event.setSliderIsMute(false)
|
||||||
|
}
|
||||||
|
const handleSliderMsUp = () => {
|
||||||
|
sliderEvent.isMsDown = false
|
||||||
|
}
|
||||||
|
const handleSliderMsMove = event => {
|
||||||
|
if (!sliderEvent.isMsDown) return
|
||||||
|
let value = (sliderEvent.msDownValue + (event.clientX - sliderEvent.msDownX) / dom_sliderBar.value.clientWidth) * (props.max - props.min) + props.min
|
||||||
|
if (value > props.max) value = props.max
|
||||||
|
else if (value < props.min) value = props.min
|
||||||
|
emit('change', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', handleSliderMsMove)
|
||||||
|
document.addEventListener('mouseup', handleSliderMsUp)
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('mousemove', handleSliderMsMove)
|
||||||
|
document.removeEventListener('mouseup', handleSliderMsUp)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
handleSliderMsDown,
|
||||||
|
dom_sliderBar,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" module>
|
||||||
|
@import '@renderer/assets/styles/layout.less';
|
||||||
|
|
||||||
|
.slider-content {
|
||||||
|
flex: none;
|
||||||
|
position: relative;
|
||||||
|
width: 100px;
|
||||||
|
padding: 5px 0;
|
||||||
|
// margin-right: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
opacity: .5;
|
||||||
|
transition: opacity @transition-normal;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
// cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
height: 5px;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: @transition-normal;
|
||||||
|
transition-property: background-color, opacity;
|
||||||
|
background-color: var(--color-primary-alpha-700);
|
||||||
|
// background-color: #f5f5f5;
|
||||||
|
position: relative;
|
||||||
|
// border-radius: @radius-progress-border;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .muted {
|
||||||
|
// opacity: .5;
|
||||||
|
// }
|
||||||
|
|
||||||
|
.slider-bar {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: 0;
|
||||||
|
transition-property: transform;
|
||||||
|
transition-timing-function: ease;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
// border-radius: @radius-progress-border;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
background-color: var(--color-button-font);
|
||||||
|
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-mask {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<material-popup-btn :class="$style.btnContent">
|
||||||
|
<button :class="[$style.btn, { [$style.active]: playbackRate != 1 }]" :aria-label="`${$t('player__playback_rate')}x${playbackRate}`">
|
||||||
|
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" width="100%" viewBox="0 0 24 24" space="preserve">
|
||||||
|
<use xlink:href="#icon-plex" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<template #content>
|
||||||
|
<div :class="$style.setting">
|
||||||
|
<div :class="$style.info">
|
||||||
|
<span>x{{ playbackRate }}</span>
|
||||||
|
<base-btn min @click="handleUpdatePlaybackRate(1)">{{ $t('player__playback_rate_reset_btn') }}</base-btn>
|
||||||
|
</div>
|
||||||
|
<base-slider-bar :value="playbackRate" :min="0.5" :max="2" @change="handleUpdatePlaybackRate" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</material-popup-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// import { computed } from '@common/utils/vueTools'
|
||||||
|
import { playbackRate } from '@renderer/store/player/playbackRate'
|
||||||
|
|
||||||
|
const handleUpdatePlaybackRate = (val) => {
|
||||||
|
window.app_event.setPlaybackRate(Math.round(val * 10) / 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
// const icon = computed(() => {
|
||||||
|
// return playbackRate.value == 0
|
||||||
|
// ? '#icon-volume-off-outline'
|
||||||
|
// : playbackRate.value < 0.3
|
||||||
|
// ? '#icon-volume-low-outline'
|
||||||
|
// : playbackRate.value < 0.7
|
||||||
|
// ? '#icon-volume-medium-outline'
|
||||||
|
// : '#icon-volume-high-outline'
|
||||||
|
// })
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" module>
|
||||||
|
@import '@renderer/assets/styles/layout.less';
|
||||||
|
.btnContent {
|
||||||
|
flex: none;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
position: relative;
|
||||||
|
// color: var(--color-button-font);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
transition: color @transition-normal;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
transition: opacity @transition-fast;
|
||||||
|
opacity: .5;
|
||||||
|
// filter: drop-shadow(0 0 1px rgba(0, 0, 0, 0.2));
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
svg {
|
||||||
|
opacity: .9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:active {
|
||||||
|
svg {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
svg {
|
||||||
|
color: var(--color-primary);
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
padding: 2px 3px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
span {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,133 +0,0 @@
|
||||||
<template>
|
|
||||||
<div :class="[$style.volumeContent, className]">
|
|
||||||
<div :class="[$style.volume ]">
|
|
||||||
<div ref="dom_volumeBar" :class="$style.volumeBar" :style="{ transform: `scaleX(${volume || 0})` }" />
|
|
||||||
</div>
|
|
||||||
<div :class="$style.volumeMask" @mousedown="handleVolumeMsDown" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { ref, onBeforeUnmount } from '@common/utils/vueTools'
|
|
||||||
// import { player as eventPlayerNames } from '@renderer/event/names'
|
|
||||||
|
|
||||||
import { volume, isMute } from '@renderer/store/player/volume'
|
|
||||||
import { appSetting } from '@renderer/store/setting'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setup(props) {
|
|
||||||
const volumeEvent = {
|
|
||||||
isMsDown: false,
|
|
||||||
msDownX: 0,
|
|
||||||
msDownVolume: 0,
|
|
||||||
}
|
|
||||||
const dom_volumeBar = ref(null)
|
|
||||||
|
|
||||||
const handleVolumeMsDown = event => {
|
|
||||||
volumeEvent.isMsDown = true
|
|
||||||
volumeEvent.msDownX = event.clientX
|
|
||||||
|
|
||||||
let val = event.offsetX / dom_volumeBar.value.clientWidth
|
|
||||||
if (val < 0) val = 0
|
|
||||||
if (val > 1) val = 1
|
|
||||||
|
|
||||||
window.app_event.setVolume(volumeEvent.msDownVolume = val)
|
|
||||||
|
|
||||||
// if (isMute.value) window.app_event.setVolumeIsMute(false)
|
|
||||||
}
|
|
||||||
const handleVolumeMsUp = () => {
|
|
||||||
volumeEvent.isMsDown = false
|
|
||||||
}
|
|
||||||
const handleVolumeMsMove = event => {
|
|
||||||
if (!volumeEvent.isMsDown) return
|
|
||||||
let volume = volumeEvent.msDownVolume + (event.clientX - volumeEvent.msDownX) / dom_volumeBar.value.clientWidth
|
|
||||||
if (volume > 1) volume = 1
|
|
||||||
else if (volume < 0) volume = 0
|
|
||||||
window.app_event.setVolume(volume)
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('mousemove', handleVolumeMsMove)
|
|
||||||
document.addEventListener('mouseup', handleVolumeMsUp)
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
document.removeEventListener('mousemove', handleVolumeMsMove)
|
|
||||||
document.removeEventListener('mouseup', handleVolumeMsUp)
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
handleVolumeMsDown,
|
|
||||||
dom_volumeBar,
|
|
||||||
volume,
|
|
||||||
isMute,
|
|
||||||
appSetting,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="less" module>
|
|
||||||
@import '@renderer/assets/styles/layout.less';
|
|
||||||
|
|
||||||
.volume-content {
|
|
||||||
flex: none;
|
|
||||||
position: relative;
|
|
||||||
width: 100px;
|
|
||||||
padding: 5px 0;
|
|
||||||
// margin-right: 10px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
opacity: .5;
|
|
||||||
transition: opacity @transition-normal;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.volume {
|
|
||||||
// cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
height: 5px;
|
|
||||||
border-radius: 20px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: @transition-normal;
|
|
||||||
transition-property: background-color, opacity;
|
|
||||||
background-color: var(--color-primary-alpha-700);
|
|
||||||
// background-color: #f5f5f5;
|
|
||||||
position: relative;
|
|
||||||
// border-radius: @radius-progress-border;
|
|
||||||
}
|
|
||||||
|
|
||||||
// .muted {
|
|
||||||
// opacity: .5;
|
|
||||||
// }
|
|
||||||
|
|
||||||
.volume-bar {
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
transform: scaleX(0);
|
|
||||||
transform-origin: 0;
|
|
||||||
transition-property: transform;
|
|
||||||
transition-timing-function: ease;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
// border-radius: @radius-progress-border;
|
|
||||||
transition-duration: 0.2s;
|
|
||||||
background-color: var(--color-button-font);
|
|
||||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.volume-mask {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
|
@ -16,7 +16,7 @@
|
||||||
@update:model-value="saveVolumeIsMute($event)"
|
@update:model-value="saveVolumeIsMute($event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<common-volume-bar />
|
<base-slider-bar :value="volume" :min="0" :max="1" @change="handleUpdateVolume" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</material-popup-btn>
|
</material-popup-btn>
|
||||||
|
@ -30,6 +30,10 @@ import { computed } from '@common/utils/vueTools'
|
||||||
import { saveVolumeIsMute } from '@renderer/store/setting'
|
import { saveVolumeIsMute } from '@renderer/store/setting'
|
||||||
import { volume, isMute } from '@renderer/store/player/volume'
|
import { volume, isMute } from '@renderer/store/player/volume'
|
||||||
|
|
||||||
|
const handleUpdateVolume = (val) => {
|
||||||
|
window.app_event.setVolume(val)
|
||||||
|
}
|
||||||
|
|
||||||
const icon = computed(() => {
|
const icon = computed(() => {
|
||||||
return isMute.value
|
return isMute.value
|
||||||
? '#icon-volume-mute-outline'
|
? '#icon-volume-mute-outline'
|
||||||
|
|
|
@ -14,6 +14,7 @@ div(:class="$style.footerLeftControlBtns")
|
||||||
button(:class="[$style.footerLeftControlBtn, {[$style.active]: isShowPlayComment}]" @click="toggleVisibleComment" :aria-label="$t('comment__show')")
|
button(:class="[$style.footerLeftControlBtn, {[$style.active]: isShowPlayComment}]" @click="toggleVisibleComment" :aria-label="$t('comment__show')")
|
||||||
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve')
|
svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/1999/xlink' width='95%' viewBox='0 0 24 24' space='preserve')
|
||||||
use(xlink:href='#icon-comment')
|
use(xlink:href='#icon-comment')
|
||||||
|
common-playback-rate-btn
|
||||||
common-volume-btn
|
common-volume-btn
|
||||||
common-toggle-play-mode-btn
|
common-toggle-play-mode-btn
|
||||||
button(:class="$style.footerLeftControlBtn" @click="isShowAddMusicTo = true" :aria-label="$t('player__add_music_to')")
|
button(:class="$style.footerLeftControlBtn" @click="isShowAddMusicTo = true" :aria-label="$t('player__add_music_to')")
|
||||||
|
|
|
@ -69,6 +69,7 @@ const handleDesktopLyricMessage = (action: LX.DesktopLyric.WinMainActions) => {
|
||||||
action: 'set_status',
|
action: 'set_status',
|
||||||
data: {
|
data: {
|
||||||
isPlay: isPlay.value,
|
isPlay: isPlay.value,
|
||||||
|
rate: appSetting['player.playbackRate'],
|
||||||
line: lyric.line,
|
line: lyric.line,
|
||||||
played_time: getCurrentTime(),
|
played_time: getCurrentTime(),
|
||||||
},
|
},
|
||||||
|
@ -100,6 +101,7 @@ export const init = () => {
|
||||||
setLines(markRawList([...lines]))
|
setLines(markRawList([...lines]))
|
||||||
setText(lines[0] ?? '', 0)
|
setText(lines[0] ?? '', 0)
|
||||||
},
|
},
|
||||||
|
rate: appSetting['player.playbackRate'],
|
||||||
// offset: 80,
|
// offset: 80,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -143,6 +145,25 @@ export const setLyricOffset = (offset: number) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const setPlaybackRate = (rate: number) => {
|
||||||
|
lrc.setPlaybackRate(rate)
|
||||||
|
sendDesktopLyricInfo({
|
||||||
|
action: 'set_playbackRate',
|
||||||
|
data: rate,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isPlay.value) {
|
||||||
|
setTimeout(() => {
|
||||||
|
const time = getCurrentTime()
|
||||||
|
sendDesktopLyricInfo({
|
||||||
|
action: 'set_play',
|
||||||
|
data: time,
|
||||||
|
})
|
||||||
|
lrc.play(time)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const setLyric = () => {
|
export const setLyric = () => {
|
||||||
if (!musicInfo.id) return
|
if (!musicInfo.id) return
|
||||||
if (musicInfo.lrc) {
|
if (musicInfo.lrc) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
|
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
|
||||||
|
import { debounce } from '@common/utils/common'
|
||||||
// import { setDesktopLyricInfo, onGetDesktopLyricInfo } from '@renderer/utils/ipc'
|
// import { setDesktopLyricInfo, onGetDesktopLyricInfo } from '@renderer/utils/ipc'
|
||||||
// import { musicInfo } from '@renderer/store/player/state'
|
// import { musicInfo } from '@renderer/store/player/state'
|
||||||
import {
|
import {
|
||||||
|
@ -8,9 +9,11 @@ import {
|
||||||
stop,
|
stop,
|
||||||
init,
|
init,
|
||||||
sendInfo,
|
sendInfo,
|
||||||
|
setPlaybackRate,
|
||||||
} from '@renderer/core/lyric'
|
} from '@renderer/core/lyric'
|
||||||
import { appSetting } from '@renderer/store/setting'
|
import { appSetting } from '@renderer/store/setting'
|
||||||
|
|
||||||
|
const handleApplyPlaybackRate = debounce(setPlaybackRate, 300)
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
init()
|
init()
|
||||||
|
@ -30,6 +33,7 @@ export default () => {
|
||||||
window.app_event.on('error', pause)
|
window.app_event.on('error', pause)
|
||||||
window.app_event.on('musicToggled', setPlayInfo)
|
window.app_event.on('musicToggled', setPlayInfo)
|
||||||
window.app_event.on('lyricUpdated', setLyric)
|
window.app_event.on('lyricUpdated', setLyric)
|
||||||
|
window.app_event.on('setPlaybackRate', handleApplyPlaybackRate)
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
window.app_event.off('play', play)
|
window.app_event.off('play', play)
|
||||||
|
@ -38,5 +42,6 @@ export default () => {
|
||||||
window.app_event.off('error', pause)
|
window.app_event.off('error', pause)
|
||||||
window.app_event.off('musicToggled', setPlayInfo)
|
window.app_event.off('musicToggled', setPlayInfo)
|
||||||
window.app_event.off('lyricUpdated', setLyric)
|
window.app_event.off('lyricUpdated', setLyric)
|
||||||
|
window.app_event.off('setPlaybackRate', handleApplyPlaybackRate)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
|
||||||
|
import { setPlaybackRate as setPlayerPlaybackRate } from '@renderer/plugins/player'
|
||||||
|
|
||||||
|
import { debounce } from '@common/utils'
|
||||||
|
// import { HOTKEY_PLAYER } from '@common/hotKey'
|
||||||
|
import { playbackRate, setplaybackRate } from '@renderer/store/player/playbackRate'
|
||||||
|
import { appSetting, savePlaybackRate } from '@renderer/store/setting'
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const handleSavePlaybackRate = debounce(savePlaybackRate, 300)
|
||||||
|
|
||||||
|
setplaybackRate(appSetting['player.playbackRate'])
|
||||||
|
setPlayerPlaybackRate(appSetting['player.playbackRate'])
|
||||||
|
|
||||||
|
|
||||||
|
const handleSetPlaybackRate = (num: number) => {
|
||||||
|
const rate = num < 0.5 ? 0.5 : num > 2 ? 2 : num
|
||||||
|
setplaybackRate(rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// const handleSetPlaybackRateUp = (step = 0.02) => {
|
||||||
|
// handleSetPlaybackRate(volume.value + step)
|
||||||
|
// }
|
||||||
|
// const handleSetPlaybackRateDown = (step = 0.02) => {
|
||||||
|
// handleSetPlaybackRate(volume.value - step)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const hotkeyVolumeUp = () => {
|
||||||
|
// handleSetPlaybackRateUp()
|
||||||
|
// }
|
||||||
|
// const hotkeyVolumeDown = () => {
|
||||||
|
// handleSetPlaybackRateDown()
|
||||||
|
// }
|
||||||
|
|
||||||
|
watch(playbackRate, rate => {
|
||||||
|
handleSavePlaybackRate(rate)
|
||||||
|
setPlayerPlaybackRate(rate)
|
||||||
|
})
|
||||||
|
watch(() => appSetting['player.playbackRate'], rate => {
|
||||||
|
setplaybackRate(rate)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// window.key_event.on(HOTKEY_PLAYER.volume_up.action, hotkeyVolumeUp)
|
||||||
|
// window.key_event.on(HOTKEY_PLAYER.volume_down.action, hotkeyVolumeDown)
|
||||||
|
window.app_event.on('setPlaybackRate', handleSetPlaybackRate)
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// window.key_event.off(HOTKEY_PLAYER.volume_up.action, hotkeyVolumeUp)
|
||||||
|
// window.key_event.off(HOTKEY_PLAYER.volume_down.action, hotkeyVolumeDown)
|
||||||
|
window.app_event.off('setPlaybackRate', handleSetPlaybackRate)
|
||||||
|
})
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ import useVolume from './useVolume'
|
||||||
import useWatchList from './useWatchList'
|
import useWatchList from './useWatchList'
|
||||||
import { HOTKEY_PLAYER } from '@common/hotKey'
|
import { HOTKEY_PLAYER } from '@common/hotKey'
|
||||||
import { playNext, pause, playPrev, togglePlay } from '@renderer/core/player'
|
import { playNext, pause, playPrev, togglePlay } from '@renderer/core/player'
|
||||||
|
import usePlaybackRate from './usePlaybackRate'
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -40,6 +41,7 @@ export default () => {
|
||||||
usePlayEvent()
|
usePlayEvent()
|
||||||
useLyric()
|
useLyric()
|
||||||
useVolume()
|
useVolume()
|
||||||
|
usePlaybackRate()
|
||||||
useWatchList()
|
useWatchList()
|
||||||
|
|
||||||
const handlePlayNext = () => {
|
const handlePlayNext = () => {
|
||||||
|
|
|
@ -50,6 +50,14 @@ export class AppEvent extends Event {
|
||||||
this.emit('setVolume', volume)
|
this.emit('setVolume', volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置播放速率大小
|
||||||
|
* @param rate 播放速率
|
||||||
|
*/
|
||||||
|
setPlaybackRate(rate: number) {
|
||||||
|
this.emit('setPlaybackRate', rate)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置是否静音
|
* 设置是否静音
|
||||||
* @param isMute 是否静音
|
* @param isMute 是否静音
|
||||||
|
|
|
@ -52,6 +52,16 @@ export const setLoopPlay = (isLoop: boolean) => {
|
||||||
if (audio) audio.loop = isLoop
|
if (audio) audio.loop = isLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getPlaybackRate = (): number => {
|
||||||
|
return audio?.defaultPlaybackRate ?? 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setPlaybackRate = (rate: number) => {
|
||||||
|
if (!audio) return
|
||||||
|
audio.defaultPlaybackRate = rate
|
||||||
|
audio.playbackRate = rate
|
||||||
|
}
|
||||||
|
|
||||||
export const getMute = (): boolean => {
|
export const getMute = (): boolean => {
|
||||||
return audio?.muted ?? false
|
return audio?.muted ?? false
|
||||||
}
|
}
|
||||||
|
@ -81,9 +91,9 @@ export const getDuration = () => {
|
||||||
return audio?.duration ?? 0
|
return audio?.duration ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPlaybackRate = () => {
|
// export const getPlaybackRate = () => {
|
||||||
return audio?.playbackRate ?? 1
|
// return audio?.playbackRate ?? 1
|
||||||
}
|
// }
|
||||||
|
|
||||||
type Noop = () => void
|
type Noop = () => void
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import { ref } from '@common/utils/vueTools'
|
||||||
|
|
||||||
|
|
||||||
|
export const playbackRate = ref(1)
|
||||||
|
|
||||||
|
export const setplaybackRate = (num: number) => {
|
||||||
|
playbackRate.value = num
|
||||||
|
}
|
|
@ -57,6 +57,14 @@ export const saveVolumeIsMute = (isMute: boolean) => {
|
||||||
updateSetting({ 'player.isMute': isMute })
|
updateSetting({ 'player.isMute': isMute })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置播放速率
|
||||||
|
* @param rate 播放速率
|
||||||
|
*/
|
||||||
|
export const savePlaybackRate = (rate: number) => {
|
||||||
|
updateSetting({ 'player.playbackRate': rate })
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置是否开启桌面歌词
|
* 设置是否开启桌面歌词
|
||||||
|
|
|
@ -12,6 +12,7 @@ const parseTools = {
|
||||||
rxps: {
|
rxps: {
|
||||||
info: /^{"/,
|
info: /^{"/,
|
||||||
lineTime: /^\[(\d+),\d+\]/,
|
lineTime: /^\[(\d+),\d+\]/,
|
||||||
|
lineTime2: /^\[([\d:.]+)\]/,
|
||||||
wordTime: /\(\d+,\d+\)/,
|
wordTime: /\(\d+,\d+\)/,
|
||||||
wordTimeAll: /(\(\d+,\d+\))/g,
|
wordTimeAll: /(\(\d+,\d+\))/g,
|
||||||
timeLabelFixRxp: /(?:\.0+|0+)$/,
|
timeLabelFixRxp: /(?:\.0+|0+)$/,
|
||||||
|
@ -42,6 +43,10 @@ const parseTools = {
|
||||||
lxlrcLines.push(line)
|
lxlrcLines.push(line)
|
||||||
lrcLines.push(line)
|
lrcLines.push(line)
|
||||||
}
|
}
|
||||||
|
if (this.rxps.lineTime2.test(line)) {
|
||||||
|
// lxlrcLines.push(line)
|
||||||
|
lrcLines.push(line)
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,22 +110,21 @@ const parseTools = {
|
||||||
const rlrcLines = rlrc.split('\n')
|
const rlrcLines = rlrc.split('\n')
|
||||||
let lrcLines = lrc.split('\n')
|
let lrcLines = lrc.split('\n')
|
||||||
// let temp = []
|
// let temp = []
|
||||||
const timeTagRxp = /^\[([\d:.]+)\]/
|
|
||||||
let newLrc = []
|
let newLrc = []
|
||||||
rlrcLines.forEach((line) => {
|
rlrcLines.forEach((line) => {
|
||||||
const result = timeTagRxp.exec(line)
|
const result = this.rxps.lineTime2.exec(line)
|
||||||
if (!result) return
|
if (!result) return
|
||||||
const words = line.replace(timeTagRxp, '')
|
const words = line.replace(this.rxps.lineTime2, '')
|
||||||
if (!words.trim()) return
|
if (!words.trim()) return
|
||||||
const t1 = this.getIntv(result[1])
|
const t1 = this.getIntv(result[1])
|
||||||
|
|
||||||
while (lrcLines.length) {
|
while (lrcLines.length) {
|
||||||
const lrcLine = lrcLines.shift()
|
const lrcLine = lrcLines.shift()
|
||||||
const lrcLineResult = timeTagRxp.exec(lrcLine)
|
const lrcLineResult = this.rxps.lineTime2.exec(lrcLine)
|
||||||
if (!lrcLineResult) continue
|
if (!lrcLineResult) continue
|
||||||
const t2 = this.getIntv(lrcLineResult[1])
|
const t2 = this.getIntv(lrcLineResult[1])
|
||||||
if (Math.abs(t1 - t2) < 10) {
|
if (Math.abs(t1 - t2) < 10) {
|
||||||
newLrc.push(line.replace(timeTagRxp, lrcLineResult[0]))
|
newLrc.push(line.replace(this.rxps.lineTime2, lrcLineResult[0]))
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// temp.push(line)
|
// temp.push(line)
|
||||||
|
|
Loading…
Reference in New Issue