新增播放详情页歌词右键菜单,新增歌词偏移设置
parent
82ea1935de
commit
bc301c27c2
|
@ -1,4 +1,18 @@
|
|||
### 新增
|
||||
|
||||
- 新增播放详情页歌词右键菜单,原来设置-播放详情页设置的字体重置已迁移到此菜单内
|
||||
- 新增歌词偏移设置,可以在播放详情页歌词右键菜单中使用
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复Linux无法全屏的问题
|
||||
- 修复播放下载列表的歌曲时,使用Windows任务栏缩略图工具栏控制按钮的收藏按钮收藏歌曲时的异常问题
|
||||
|
||||
### 变更
|
||||
|
||||
- 播放详情页的任意地方右键双击隐藏详情页的行为,“任意区域”改为在“非歌词区域”
|
||||
|
||||
### 移除
|
||||
|
||||
- 移除设置-播放详情页设置-歌词字体重置,此设置项已迁移到播放详情页的歌词菜单中
|
||||
- 移除播放详情页使用+-快捷键调整字体大小的功能,改用歌词右键菜单的字体大小调整功能
|
||||
|
|
|
@ -77,6 +77,12 @@ const names = {
|
|||
get_lyric: 'get_lyric',
|
||||
save_lyric: 'save_lyric',
|
||||
clear_lyric: 'clear_lyric',
|
||||
get_lyric_raw: 'get_lyric_raw',
|
||||
save_lyric_raw: 'save_lyric_raw',
|
||||
clear_lyric_raw: 'clear_lyric_raw',
|
||||
get_lyric_edited: 'get_lyric_edited',
|
||||
save_lyric_edited: 'save_lyric_edited',
|
||||
remove_lyric_edited: 'remove_lyric_edited',
|
||||
get_music_url: 'get_music_url',
|
||||
save_music_url: 'save_music_url',
|
||||
clear_music_url: 'clear_music_url',
|
||||
|
|
|
@ -127,6 +127,19 @@
|
|||
"love_list": "Favorites",
|
||||
"lyric__load_error": "Failed to get lyrics",
|
||||
"lyric__select": "Lyric text selection",
|
||||
"lyric_menu__align": "Lyric Alignment",
|
||||
"lyric_menu__align_center": "Centered",
|
||||
"lyric_menu__align_left": "Left",
|
||||
"lyric_menu__lrc_size": "Font size [{size}]",
|
||||
"lyric_menu__offset": "Offset [ {offset}ms ]",
|
||||
"lyric_menu__offset_add_10": "10ms faster (right click speeds up 5ms)",
|
||||
"lyric_menu__offset_add_100": "100ms faster (right click speeds up 50ms)",
|
||||
"lyric_menu__offset_dec_10": "10ms slow down (right click slows down 5ms)",
|
||||
"lyric_menu__offset_dec_100": "Slow down by 100ms (right click slows down by 50ms)",
|
||||
"lyric_menu__offset_reset": "Reset",
|
||||
"lyric_menu__size_add": "Increase font size (right click to fine-tune)",
|
||||
"lyric_menu__size_dec": "Decrease font (right click to fine tune)",
|
||||
"lyric_menu__size_reset": "Reset",
|
||||
"min": "Minimize",
|
||||
"music_album": "Album",
|
||||
"music_duplicate": "Duplicate song",
|
||||
|
@ -330,11 +343,11 @@
|
|||
"setting__play_detail_align_center": "Centered",
|
||||
"setting__play_detail_align_left": "Left",
|
||||
"setting__play_detail_align_right": "Right",
|
||||
"setting__play_detail_detail_lyric_progress": "Allows to adjust playback progress by lyrics",
|
||||
"setting__play_detail_font_size": "Lyrics font size (you can use the keyboard + - adjust the font size on the playback details page)",
|
||||
"setting__play_detail_font_size_current": "Current font size: {size}",
|
||||
"setting__play_detail_font_size_reset": "Reset",
|
||||
"setting__play_detail_font_zoom": "Zoom the currently playing lyrics",
|
||||
"setting__play_detail_lyric_progress": "Allows to adjust playback progress by lyrics",
|
||||
"setting__play_lyric_lxlrc": "Use Karaoke-style lyrics playback (if supported)",
|
||||
"setting__play_lyric_s2t": "Convert the playing and downloading lyrics to Traditional Chinese",
|
||||
"setting__play_lyric_transition": "Show lyrics translation",
|
||||
|
|
|
@ -127,6 +127,19 @@
|
|||
"love_list": "收藏",
|
||||
"lyric__load_error": "歌词获取失败",
|
||||
"lyric__select": "歌词文本选择",
|
||||
"lyric_menu__align": "歌词对齐方式",
|
||||
"lyric_menu__align_center": "居中",
|
||||
"lyric_menu__align_left": "居左",
|
||||
"lyric_menu__lrc_size": "字体大小 [ {size} ]",
|
||||
"lyric_menu__offset": "歌词偏移 [ {offset}ms ]",
|
||||
"lyric_menu__offset_add_10": "加快10毫秒(右击加快5毫秒)",
|
||||
"lyric_menu__offset_add_100": "加快100毫秒(右击加快50毫秒)",
|
||||
"lyric_menu__offset_dec_10": "减慢10毫秒(右击减慢5毫秒)",
|
||||
"lyric_menu__offset_dec_100": "减慢100毫秒(右击减慢50毫秒)",
|
||||
"lyric_menu__offset_reset": "重置",
|
||||
"lyric_menu__size_add": "加大字体(右击可微调)",
|
||||
"lyric_menu__size_dec": "减小字体(右击可微调)",
|
||||
"lyric_menu__size_reset": "重置",
|
||||
"min": "最小化",
|
||||
"music_album": "专辑名",
|
||||
"music_duplicate": "重复歌曲",
|
||||
|
@ -330,7 +343,7 @@
|
|||
"setting__play_detail_align_center": "居中",
|
||||
"setting__play_detail_align_left": "居左",
|
||||
"setting__play_detail_align_right": "居右",
|
||||
"setting__play_detail_detail_lyric_progress": "允许通过歌词调整播放进度",
|
||||
"setting__play_detail_lyric_progress": "允许通过歌词调整播放进度",
|
||||
"setting__play_detail_font_size": "歌词字体大小(可以在播放详情页使用键盘的 + - 调整字体大小)",
|
||||
"setting__play_detail_font_size_current": "当前字体大小:{size}",
|
||||
"setting__play_detail_font_size_reset": "重置",
|
||||
|
|
|
@ -127,6 +127,19 @@
|
|||
"love_list": "收藏列表",
|
||||
"lyric__load_error": "歌詞獲取失敗",
|
||||
"lyric__select": "歌詞文本選擇",
|
||||
"lyric_menu__align": "歌詞對齊方式",
|
||||
"lyric_menu__align_center": "居中",
|
||||
"lyric_menu__align_left": "居左",
|
||||
"lyric_menu__lrc_size": "字體大小 [ {size} ]",
|
||||
"lyric_menu__offset": "歌詞偏移 [ {offset}ms ]",
|
||||
"lyric_menu__offset_add_10": "加快10毫秒(右擊加快5毫秒)",
|
||||
"lyric_menu__offset_add_100": "加快100毫秒(右擊加快50毫秒)",
|
||||
"lyric_menu__offset_dec_10": "減慢10毫秒(右擊減慢5毫秒)",
|
||||
"lyric_menu__offset_dec_100": "減慢100毫秒(右擊減慢50毫秒)",
|
||||
"lyric_menu__offset_reset": "重置偏移",
|
||||
"lyric_menu__size_add": "加大字體(右擊可微調)",
|
||||
"lyric_menu__size_dec": "減小字體(右擊可微調)",
|
||||
"lyric_menu__size_reset": "重置",
|
||||
"min": "最小化",
|
||||
"music_album": "專輯名",
|
||||
"music_duplicate": "重複歌曲",
|
||||
|
@ -330,7 +343,7 @@
|
|||
"setting__play_detail_align_center": "居中",
|
||||
"setting__play_detail_align_left": "居左",
|
||||
"setting__play_detail_align_right": "居右",
|
||||
"setting__play_detail_detail_lyric_progress": "允許通過歌詞調整播放進度",
|
||||
"setting__play_detail_lyric_progress": "允許通過歌詞調整播放進度",
|
||||
"setting__play_detail_font_size": "歌詞字體大小(可以在播放詳情頁使用鍵盤的 + - 調整字體大小)",
|
||||
"setting__play_detail_font_size_current": "當前字體大小:{size}",
|
||||
"setting__play_detail_font_size_reset": "重置",
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
const { mainOn, NAMES: { mainWindow: ipcMainWindowNames }, mainHandle } = require('../../common/ipc')
|
||||
const getStore = require('@common/store')
|
||||
|
||||
const LRC_RAW = 'lyrics'
|
||||
const LRC_EDITED = 'lyrics_edited'
|
||||
|
||||
mainHandle(ipcMainWindowNames.get_lyric, async(event, id) => getStore('lyrics', true, false).get(id) || {})
|
||||
mainHandle(ipcMainWindowNames.get_lyric, async(event, id) => {
|
||||
return getStore(LRC_EDITED, true, false).get(id) || getStore(LRC_RAW, true, false).get(id) || {}
|
||||
})
|
||||
|
||||
|
||||
mainOn(ipcMainWindowNames.save_lyric, (event, { id, lyrics }) => getStore('lyrics', true, false).set(id, lyrics))
|
||||
// 原始歌词
|
||||
mainHandle(ipcMainWindowNames.get_lyric_raw, async(event, id) => getStore(LRC_RAW, true, false).get(id) || {})
|
||||
mainOn(ipcMainWindowNames.save_lyric_raw, (event, { id, lyrics }) => getStore(LRC_RAW, true, false).set(id, lyrics))
|
||||
mainOn(ipcMainWindowNames.clear_lyric_raw, () => getStore(LRC_RAW, true, false).clear())
|
||||
|
||||
mainOn(ipcMainWindowNames.clear_lyric, () => getStore('lyrics', true, false).clear())
|
||||
// 已编辑的歌词
|
||||
mainHandle(ipcMainWindowNames.get_lyric_edited, async(event, id) => getStore(LRC_EDITED, true, false).get(id) || {})
|
||||
mainOn(ipcMainWindowNames.save_lyric_edited, (event, { id, lyrics }) => getStore(LRC_EDITED, true, false).set(id, lyrics))
|
||||
mainOn(ipcMainWindowNames.remove_lyric_edited, async(event, id) => getStore(LRC_EDITED, true, false).delete(id))
|
||||
|
|
|
@ -234,5 +234,13 @@ svg(version='1.1' xmlns='http://www.w3.org/2000/svg' xlink='http://www.w3.org/19
|
|||
// 0 0 24 24
|
||||
path(fill='currentColor', d='M22 12L20 13L19 14L18 13L17 16L16 13L15 21L14 13L13 15L12 13L11 17L10 13L9 22L8 13L7 19L6 13L5 14L4 13L2 12L4 11L5 10L6 11L7 5L8 11L9 2L10 11L11 7L12 11L13 9L14 11L15 3L16 11L17 8L18 11L19 10L20 11L22 12Z')
|
||||
|
||||
g#icon-font-decrease(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,17L23,11.93L21.59,10.5L19,13.1V7H17V13.1L14.41,10.5L13,11.93L18,17Z')
|
||||
|
||||
g#icon-font-increase(fill='currentColor')
|
||||
// 0 0 24 24
|
||||
path(d='M5.12,14L7.5,7.67L9.87,14M6.5,5L1,19H3.25L4.37,16H10.62L11.75,19H14L8.5,5H6.5M18,7L13,12.07L14.41,13.5L17,10.9V17H19V10.9L21.59,13.5L23,12.07L18,7Z')
|
||||
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div :class="['right', $style.right]" :style="lrcFontSize">
|
||||
<div :class="['right', $style.right]" :style="lrcFontSize" @contextmenu.stop="handleShowLyricMenu">
|
||||
<div :class="['lyric', $style.lyric, { [$style.draging]: isMsDown }, { [$style.lrcActiveZoom]: isZoomActiveLrc }]" :style="lrcStyles" @wheel="handleWheel" @mousedown="handleLyricMouseDown" ref="dom_lyric">
|
||||
<div :class="['pre', $style.lyricSpace]"></div>
|
||||
<div ref="dom_lyric_text"></div>
|
||||
|
@ -25,6 +25,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
<LyricMenu v-model="lyricMenuVisible" :xy="lyricMenuXY" :lyricInfo="lyricInfo" @updateLyric="handleUpdateLyric" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -32,14 +33,18 @@
|
|||
import { clipboardWriteText } from '@renderer/utils'
|
||||
import { lyric } from '@renderer/core/share/lyric'
|
||||
import { isFullscreen } from '@renderer/core/share'
|
||||
import { isPlay, isShowLrcSelectContent, isShowPlayComment } from '@renderer/core/share/player'
|
||||
import { onMounted, onBeforeUnmount, useCommit, useRefGetter, computed } from '@renderer/utils/vueTools'
|
||||
import { isPlay, isShowLrcSelectContent, isShowPlayComment, musicInfo as playerMusicInfo, musicInfoItem, setMusicInfo } from '@renderer/core/share/player'
|
||||
import { onMounted, onBeforeUnmount, useRefGetter, computed, reactive, ref } from '@renderer/utils/vueTools'
|
||||
import useLyric from '@renderer/utils/compositions/useLyric'
|
||||
import LyricMenu from './components/LyricMenu'
|
||||
import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LyricMenu,
|
||||
},
|
||||
setup() {
|
||||
const setting = useRefGetter('setting')
|
||||
const setPlayDetailLyricFont = useCommit('setPlayDetailLyricFont')
|
||||
const {
|
||||
dom_lyric,
|
||||
dom_lyric_text,
|
||||
|
@ -54,13 +59,37 @@ export default {
|
|||
handleSkipMouseLeave,
|
||||
} = useLyric({ isPlay, lyric })
|
||||
|
||||
const fontSizeUp = () => {
|
||||
if (setting.value.playDetail.style.fontSize >= 200) return
|
||||
setPlayDetailLyricFont(setting.value.playDetail.style.fontSize + 1)
|
||||
const lyricMenuVisible = ref(false)
|
||||
const lyricMenuXY = reactive({
|
||||
x: 0,
|
||||
y: 0,
|
||||
})
|
||||
const lyricInfo = reactive({
|
||||
lyric: '',
|
||||
tlyric: '',
|
||||
lxlyric: '',
|
||||
musicInfo: null,
|
||||
})
|
||||
const updateMusicInfo = () => {
|
||||
lyricInfo.lyric = playerMusicInfo.lrc
|
||||
lyricInfo.tlyric = playerMusicInfo.tlrc
|
||||
lyricInfo.lxlyric = playerMusicInfo.lxlrc
|
||||
lyricInfo.musicInfo = musicInfoItem.value
|
||||
}
|
||||
const fontSizeDown = () => {
|
||||
if (setting.value.playDetail.style.fontSize <= 70) return
|
||||
setPlayDetailLyricFont(setting.value.playDetail.style.fontSize - 1)
|
||||
const handleShowLyricMenu = event => {
|
||||
updateMusicInfo()
|
||||
lyricMenuXY.x = event.pageX
|
||||
lyricMenuXY.y = event.pageY
|
||||
lyricMenuVisible.value = true
|
||||
}
|
||||
const handleUpdateLyric = ({ lyric, tlyric, lxlyric, offset }) => {
|
||||
setMusicInfo({
|
||||
lrc: lyric,
|
||||
tlrc: tlyric,
|
||||
lxlrc: lxlyric,
|
||||
})
|
||||
console.log(offset)
|
||||
window.eventHub.emit(eventPlayerNames.updateLyricOffset, offset)
|
||||
}
|
||||
|
||||
const lrcStyles = computed(() => {
|
||||
|
@ -79,16 +108,10 @@ export default {
|
|||
const isShowLyricProgressSetting = computed(() => setting.value.playDetail.isShowLyricProgressSetting)
|
||||
|
||||
onMounted(() => {
|
||||
window.eventHub.on('key_shift++_down', fontSizeUp)
|
||||
window.eventHub.on('key_numadd_down', fontSizeUp)
|
||||
window.eventHub.on('key_-_down', fontSizeDown)
|
||||
window.eventHub.on('key_numsub_down', fontSizeDown)
|
||||
window.eventHub.on(eventPlayerNames.updateLyric, updateMusicInfo)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.eventHub.off('key_shift++_down', fontSizeUp)
|
||||
window.eventHub.off('key_numadd_down', fontSizeUp)
|
||||
window.eventHub.off('key_-_down', fontSizeDown)
|
||||
window.eventHub.off('key_numsub_down', fontSizeDown)
|
||||
window.eventHub.off(eventPlayerNames.updateLyric, updateMusicInfo)
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -109,6 +132,11 @@ export default {
|
|||
isShowLyricProgressSetting,
|
||||
isZoomActiveLrc,
|
||||
isStopScroll,
|
||||
lyricMenuVisible,
|
||||
lyricMenuXY,
|
||||
handleShowLyricMenu,
|
||||
handleUpdateLyric,
|
||||
lyricInfo,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -0,0 +1,300 @@
|
|||
<template>
|
||||
<teleport to="#root">
|
||||
<div :class="$style.container" :style="menuStyles" ref="dom_menu" :aria-hidden="!modelValue">
|
||||
<!-- <div :class="$style.group">
|
||||
<div :class="$style.title">{{$t('lyric_menu__align')}}</div>
|
||||
<div :class="$style.subGroup">
|
||||
<div :class="[$style.btn, { [$style.active]: playDetailSetting.style.align == 'left' }]" role="button" @click="setFontAlign('left')" ignore-tip :aria-label="$t('lyric_menu__align_left')">{{$t('lyric_menu__align_left')}}</div>
|
||||
<div :class="[$style.btn, { [$style.active]: playDetailSetting.style.align == 'center' }]" role="button" @click="setFontAlign('center')" ignore-tip :aria-label="$t('lyric_menu__align_center')">{{$t('lyric_menu__align_center')}}</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div :class="$style.group">
|
||||
<div :class="$style.subGroup">
|
||||
<div :class="$style.title">{{$t('lyric_menu__lrc_size', { size: playDetailSetting.style.fontSize })}}</div>
|
||||
<button :class="[$style.btn, $style.titleBtn]" :disabled="playDetailSetting.style.fontSize == 100" @click="fontSizeReset" ignore-tip :aria-label="$t('lyric_menu__size_reset')">{{$t('lyric_menu__size_reset')}}</button>
|
||||
</div>
|
||||
<div :class="$style.subGroup">
|
||||
<button :class="$style.btn" @click="fontSizeUp(5)" @contextmenu="fontSizeUp(1)" :aria-label="$t('lyric_menu__size_add')">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="18px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-font-increase"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button :class="$style.btn" @click="fontSizeDown(5)" @contextmenu="fontSizeDown(1)" :aria-label="$t('lyric_menu__size_dec')">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" height="18px" viewBox="0 0 24 24" space="preserve">
|
||||
<use xlink:href="#icon-font-decrease"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="$style.group">
|
||||
<div :class="$style.subGroup">
|
||||
<div :class="$style.title">{{$t('lyric_menu__offset', { offset })}}</div>
|
||||
<button :class="[$style.btn, $style.titleBtn]" :disabled="offsetDisabled || !offset" @click="offsetReset">{{$t('lyric_menu__offset_reset')}}</button>
|
||||
</div>
|
||||
<div :class="$style.subGroup">
|
||||
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(10)" @contextmenu="setOffset(5)" :aria-label="$t('lyric_menu__offset_add_10')">+ 10ms</button>
|
||||
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(-10)" @contextmenu="setOffset(-5)" :aria-label="$t('lyric_menu__offset_dec_10')">- 10ms</button>
|
||||
</div>
|
||||
<div :class="$style.subGroup">
|
||||
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(100)" @contextmenu="setOffset(50)" :aria-label="$t('lyric_menu__offset_add_100')">+ 100ms</button>
|
||||
<button :class="$style.btn" :disabled="offsetDisabled" @click="setOffset(-100)" @contextmenu="setOffset(-50)" :aria-label="$t('lyric_menu__offset_dec_100')">- 100ms</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, useRefGetter, ref, useCommit, watch } from '@renderer/utils/vueTools'
|
||||
import useMenuLocation from '@renderer/utils/compositions/useMenuLocation'
|
||||
import { setLyricEdited, removeLyricEdited, debounce } from '@renderer/utils'
|
||||
|
||||
const offsetTagRxp = /^\s*\[offset:\s*(\S+(?:\d+)*)\s*\]/
|
||||
|
||||
const saveLyric = debounce((musicInfo, lyricInfo) => {
|
||||
setLyricEdited(musicInfo, lyricInfo)
|
||||
})
|
||||
const removeLyric = debounce(musicInfo => {
|
||||
removeLyricEdited(musicInfo)
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'LyricMenu',
|
||||
props: {
|
||||
modelValue: Boolean,
|
||||
xy: Object,
|
||||
lyricInfo: Object,
|
||||
},
|
||||
emits: ['updateLyric', 'update:modelValue'],
|
||||
setup(props, { emit }) {
|
||||
const setting = useRefGetter('setting')
|
||||
const playDetailSetting = useRefGetter('playDetailSetting')
|
||||
const setPlayDetailLyricAlign = useCommit('setPlayDetailLyricAlign')
|
||||
const setPlayDetailLyricFont = useCommit('setPlayDetailLyricFont')
|
||||
|
||||
const offset = ref(0)
|
||||
const offsetDisabled = ref(true)
|
||||
|
||||
const visible = computed(() => props.modelValue)
|
||||
const musicInfo = computed(() => props.lyricInfo.musicInfo)
|
||||
const location = computed(() => props.xy)
|
||||
|
||||
const onHide = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const setFontAlign = val => {
|
||||
if (playDetailSetting.value.style.align == val) return
|
||||
setPlayDetailLyricAlign(val)
|
||||
}
|
||||
|
||||
const fontSizeUp = step => {
|
||||
if (setting.value.playDetail.style.fontSize >= 200) return
|
||||
setPlayDetailLyricFont(Math.min(setting.value.playDetail.style.fontSize + step, 200))
|
||||
}
|
||||
const fontSizeDown = step => {
|
||||
if (setting.value.playDetail.style.fontSize <= 70) return
|
||||
setPlayDetailLyricFont(Math.max(setting.value.playDetail.style.fontSize - step, 70))
|
||||
}
|
||||
const fontSizeReset = () => {
|
||||
setPlayDetailLyricFont(100)
|
||||
}
|
||||
|
||||
const updateLyric = offset => {
|
||||
let lyric = props.lyricInfo.lyric
|
||||
let tlyric = props.lyricInfo.tlyric
|
||||
let lxlyric = props.lyricInfo.lxlyric
|
||||
if (offsetTagRxp.test(lyric)) {
|
||||
lyric = lyric.replace(offsetTagRxp, `[offset:${offset}]`)
|
||||
if (tlyric) tlyric = tlyric.replace(offsetTagRxp, `[offset:${offset}]`)
|
||||
if (lxlyric) lxlyric = lxlyric.replace(offsetTagRxp, `[offset:${offset}]`)
|
||||
} else {
|
||||
lyric = `[offset:${offset}]\n` + lyric
|
||||
if (tlyric) tlyric = `[offset:${offset}]\n` + tlyric
|
||||
if (lxlyric) lxlyric = `[offset:${offset}]\n` + lxlyric
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
saveLyric(props.lyricInfo.musicInfo, {
|
||||
lyric,
|
||||
tlyric,
|
||||
lxlyric,
|
||||
})
|
||||
} else removeLyric(props.lyricInfo.musicInfo)
|
||||
|
||||
emit('updateLyric', {
|
||||
lyric,
|
||||
tlyric,
|
||||
lxlyric,
|
||||
offset,
|
||||
})
|
||||
}
|
||||
const setOffset = step => {
|
||||
offset.value += step
|
||||
updateLyric(offset.value)
|
||||
}
|
||||
const offsetReset = () => {
|
||||
if (!offset.value) return
|
||||
offset.value = 0
|
||||
updateLyric(0)
|
||||
}
|
||||
|
||||
const parseLrcOffset = () => {
|
||||
let lrcOffset
|
||||
if (props.lyricInfo.lyric) {
|
||||
lrcOffset = offsetTagRxp.exec(props.lyricInfo.lyric)
|
||||
if (lrcOffset) {
|
||||
lrcOffset = parseInt(lrcOffset[1])
|
||||
if (Number.isNaN(lrcOffset)) lrcOffset = 0
|
||||
} else lrcOffset = 0
|
||||
offsetDisabled.value = false
|
||||
} else {
|
||||
offsetDisabled.value = true
|
||||
lrcOffset = 0
|
||||
}
|
||||
offset.value = lrcOffset
|
||||
}
|
||||
|
||||
|
||||
const { dom_menu, menuStyles } = useMenuLocation({
|
||||
visible,
|
||||
location,
|
||||
onHide,
|
||||
})
|
||||
|
||||
watch(musicInfo, () => {
|
||||
if (!props.modelValue) return
|
||||
parseLrcOffset()
|
||||
})
|
||||
watch(visible, val => {
|
||||
if (!val) return
|
||||
parseLrcOffset()
|
||||
})
|
||||
|
||||
return {
|
||||
dom_menu,
|
||||
menuStyles,
|
||||
playDetailSetting,
|
||||
offset,
|
||||
fontSizeUp,
|
||||
fontSizeDown,
|
||||
fontSizeReset,
|
||||
setOffset,
|
||||
offsetReset,
|
||||
setFontAlign,
|
||||
offsetDisabled,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" module>
|
||||
@import '@renderer/assets/styles/layout.less';
|
||||
|
||||
.container {
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
transform-origin: 0 0 0;
|
||||
transition: .25s ease;
|
||||
transition-property: transform, opacity;
|
||||
border-radius: @radius-border;
|
||||
background-color: @color-theme_2-background_2;
|
||||
box-shadow: 0 1px 8px 0 rgba(0,0,0,.2);
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.title {
|
||||
flex: auto;
|
||||
padding: 10px 0 10px 10px;
|
||||
color: @color-theme_2-font-label;
|
||||
white-space: nowrap;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.subGroup {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
flex: auto;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
min-width: 60px;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// color: @color-btn;
|
||||
padding: 0 10px;
|
||||
outline: none;
|
||||
transition: @transition-theme;
|
||||
transition-property: background-color, opacity;
|
||||
box-sizing: border-box;
|
||||
.mixin-ellipsis-1;
|
||||
background-color: @color-theme_2-background_2;
|
||||
border: none;
|
||||
|
||||
&:hover {
|
||||
opacity: .7;
|
||||
background-color: @color-theme_2-hover;
|
||||
}
|
||||
&:active {
|
||||
background-color: @color-theme_2-active;
|
||||
}
|
||||
&.active {
|
||||
background-color: @color-theme_2-background_2;
|
||||
color: @color-btn-active;
|
||||
cursor: default;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
cursor: default;
|
||||
opacity: .4;
|
||||
&:hover {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.titleBtn {
|
||||
flex: none;
|
||||
padding: 0 10;
|
||||
min-width: 40px;
|
||||
opacity: .7;
|
||||
|
||||
&[disabled] {
|
||||
opacity: .3;
|
||||
}
|
||||
}
|
||||
|
||||
each(@themes, {
|
||||
:global(#root.@{value}) {
|
||||
.container {
|
||||
background-color: ~'@{color-@{value}-theme_2-background_2}';
|
||||
}
|
||||
.btn {
|
||||
background-color: ~'@{color-@{value}-theme_2-background_2}';
|
||||
&:hover {
|
||||
background-color: ~'@{color-@{value}-theme_2-hover}';
|
||||
}
|
||||
&:active {
|
||||
background-color: ~'@{color-@{value}-theme_2-active}';
|
||||
}
|
||||
&.active {
|
||||
background-color: ~'@{color-@{value}-theme_2-background_2}';
|
||||
color: ~'@{color-@{value}-btn}';
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</style>
|
||||
|
|
@ -4,6 +4,7 @@ export const lyric = reactive({
|
|||
lines: [],
|
||||
text: '',
|
||||
line: 0,
|
||||
offset: 0, // 临时延迟
|
||||
})
|
||||
|
||||
export const setLines = lines => {
|
||||
|
@ -13,3 +14,6 @@ export const setText = (text, line) => {
|
|||
lyric.text = text
|
||||
lyric.line = line
|
||||
}
|
||||
export const setOffset = offset => {
|
||||
lyric.offset = offset
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { onBeforeUnmount, watch, markRawList } from '@renderer/utils/vueTools'
|
||||
import Lyric from '@renderer/utils/lyric-font-player'
|
||||
import { getCurrentTime } from '@renderer/plugins/player'
|
||||
import { getCurrentTime as getPlayerCurrentTime } from '@renderer/plugins/player'
|
||||
import { setDesktopLyricInfo, onGetDesktopLyricInfo } from '@renderer/utils/tools'
|
||||
import { player } from '@renderer/event/names'
|
||||
import { lyric, setText, setLines } from '@renderer/core/share/lyric'
|
||||
import { player as eventPlayerNames } from '@renderer/event/names'
|
||||
import { lyric, setText, setLines, setOffset } from '@renderer/core/share/lyric'
|
||||
import { musicInfo, setStatusText, isPlay, playMusicInfo } from '@renderer/core/share/player'
|
||||
|
||||
export default ({ setting }) => {
|
||||
|
@ -12,19 +12,24 @@ export default ({ setting }) => {
|
|||
fontClassName: 'font',
|
||||
shadowContent: false,
|
||||
activeLineClassName: 'active',
|
||||
onPlay: (line, text) => {
|
||||
onPlay(line, text) {
|
||||
setText(text, line)
|
||||
setStatusText(text)
|
||||
// console.log(line, text)
|
||||
},
|
||||
onSetLyric: lines => { // listening lyrics seting event
|
||||
onSetLyric(lines) { // listening lyrics seting event
|
||||
// console.log(lines) // lines is array of all lyric text
|
||||
setLines(markRawList([...lines]))
|
||||
setText(lines[0] ?? '', 0)
|
||||
setOffset(0) // 重置临时延迟
|
||||
},
|
||||
// offset: 80,
|
||||
})
|
||||
|
||||
const getCurrentTime = () => {
|
||||
return getPlayerCurrentTime() * 1000 + lyric.offset
|
||||
}
|
||||
|
||||
|
||||
const setPlayInfo = ({ musicInfo }) => {
|
||||
setDesktopLyricInfo('music_info', {
|
||||
|
@ -45,7 +50,18 @@ export default ({ setting }) => {
|
|||
|
||||
if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) {
|
||||
setTimeout(() => {
|
||||
const time = getCurrentTime() * 1000
|
||||
const time = getCurrentTime()
|
||||
setDesktopLyricInfo('play', time)
|
||||
lrc.play(time)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const setLyricOffset = offset => {
|
||||
setOffset(offset)
|
||||
if (isPlay.value && (musicInfo.url || playMusicInfo.listId == 'download')) {
|
||||
setTimeout(() => {
|
||||
const time = getCurrentTime()
|
||||
setDesktopLyricInfo('play', time)
|
||||
lrc.play(time)
|
||||
})
|
||||
|
@ -54,7 +70,7 @@ export default ({ setting }) => {
|
|||
|
||||
const handlePlay = () => {
|
||||
if (!musicInfo.lrc) return
|
||||
const currentTime = getCurrentTime() * 1000
|
||||
const currentTime = getCurrentTime()
|
||||
lrc.play(currentTime)
|
||||
setDesktopLyricInfo('play', currentTime)
|
||||
}
|
||||
|
@ -85,14 +101,14 @@ export default ({ setting }) => {
|
|||
lxlrc: musicInfo.lxlrc,
|
||||
isPlay: isPlay.value,
|
||||
line: lyric.line,
|
||||
played_time: getCurrentTime() * 1000,
|
||||
played_time: getCurrentTime(),
|
||||
}, info)
|
||||
break
|
||||
case 'status':
|
||||
setDesktopLyricInfo('status', {
|
||||
isPlay: isPlay.value,
|
||||
line: lyric.line,
|
||||
played_time: getCurrentTime() * 1000,
|
||||
played_time: getCurrentTime(),
|
||||
}, info)
|
||||
break
|
||||
|
||||
|
@ -102,20 +118,22 @@ export default ({ setting }) => {
|
|||
})
|
||||
|
||||
|
||||
window.eventHub.on(player.play, handlePlay)
|
||||
window.eventHub.on(player.pause, handlePause)
|
||||
window.eventHub.on(player.stop, handleStop)
|
||||
window.eventHub.on(player.error, handlePause)
|
||||
window.eventHub.on(player.setPlayInfo, setPlayInfo)
|
||||
window.eventHub.on(player.updateLyric, setLyric)
|
||||
window.eventHub.on(eventPlayerNames.play, handlePlay)
|
||||
window.eventHub.on(eventPlayerNames.pause, handlePause)
|
||||
window.eventHub.on(eventPlayerNames.stop, handleStop)
|
||||
window.eventHub.on(eventPlayerNames.error, handlePause)
|
||||
window.eventHub.on(eventPlayerNames.setPlayInfo, setPlayInfo)
|
||||
window.eventHub.on(eventPlayerNames.updateLyric, setLyric)
|
||||
window.eventHub.on(eventPlayerNames.updateLyricOffset, setLyricOffset)
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
rGetDesktopLyricInfo()
|
||||
window.eventHub.off(player.play, handlePlay)
|
||||
window.eventHub.off(player.pause, handlePause)
|
||||
window.eventHub.off(player.stop, handleStop)
|
||||
window.eventHub.off(player.error, handlePause)
|
||||
window.eventHub.off(player.setPlayInfo, setPlayInfo)
|
||||
window.eventHub.off(player.updateLyric, setLyric)
|
||||
window.eventHub.off(eventPlayerNames.play, handlePlay)
|
||||
window.eventHub.off(eventPlayerNames.pause, handlePause)
|
||||
window.eventHub.off(eventPlayerNames.stop, handleStop)
|
||||
window.eventHub.off(eventPlayerNames.error, handlePause)
|
||||
window.eventHub.off(eventPlayerNames.setPlayInfo, setPlayInfo)
|
||||
window.eventHub.off(eventPlayerNames.updateLyric, setLyric)
|
||||
window.eventHub.off(eventPlayerNames.updateLyricOffset, setLyricOffset)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ const names = {
|
|||
setPlayInfo: 'setPlayInfo', // 设置播放信息
|
||||
updatePic: 'updatePic', // 更新图片事件
|
||||
updateLyric: 'updateLyric', // 更新歌词事件
|
||||
updateLyricOffset: 'updateLyricOffset', // 更新歌词偏移
|
||||
|
||||
activeTransition: 'activeTransition', // 激活进度条动画事件
|
||||
playedStop: 'playedStop', // 定时停止事件
|
||||
|
|
|
@ -69,4 +69,7 @@ export default {
|
|||
isShowAnimation(state) {
|
||||
return state.setting.isShowAnimation
|
||||
},
|
||||
playDetailSetting(state) {
|
||||
return state.setting.playDetail
|
||||
},
|
||||
}
|
||||
|
|
|
@ -35,6 +35,9 @@ export default {
|
|||
state.setting.player.volume = val
|
||||
}
|
||||
},
|
||||
setPlayDetailLyricAlign(state, val) {
|
||||
state.setting.playDetail.style.align = val
|
||||
},
|
||||
setPlayDetailLyricFont(state, val) {
|
||||
state.setting.playDetail.style.fontSize = val
|
||||
},
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { onMounted, onBeforeUnmount, watch, reactive, ref, nextTick } from '@renderer/utils/vueTools'
|
||||
|
||||
export default ({ visible, location, onHide }) => {
|
||||
const transition1 = 'transform, opacity'
|
||||
const transition2 = 'transform, opacity, top, left'
|
||||
let show = false
|
||||
const dom_menu = ref(null)
|
||||
const menuStyles = reactive({
|
||||
left: 0,
|
||||
top: 0,
|
||||
opacity: 0,
|
||||
transitionProperty: 'transform, opacity',
|
||||
transform: 'scale(0) translate(0,0)',
|
||||
})
|
||||
|
||||
const handleShow = () => {
|
||||
show = true
|
||||
menuStyles.opacity = 1
|
||||
menuStyles.transform = `scaleY(1) translate(${handleGetOffsetXY(location.x, location.y)})`
|
||||
}
|
||||
const handleHide = () => {
|
||||
menuStyles.opacity = 0
|
||||
menuStyles.transform = 'scale(0) translate(0, 0)'
|
||||
show = false
|
||||
}
|
||||
const handleGetOffsetXY = (left, top) => {
|
||||
const listWidth = dom_menu.value.clientWidth
|
||||
const listHeight = dom_menu.value.clientHeight
|
||||
const dom_container_parant = dom_menu.value.offsetParent
|
||||
const containerWidth = dom_container_parant.clientWidth
|
||||
const containerHeight = dom_container_parant.clientHeight
|
||||
const offsetWidth = containerWidth - left - listWidth
|
||||
const offsetHeight = containerHeight - top - listHeight
|
||||
let x = 0
|
||||
let y = 0
|
||||
if (containerWidth > listWidth && offsetWidth < 17) {
|
||||
x = offsetWidth - 17
|
||||
}
|
||||
if (containerHeight > listHeight && offsetHeight < 5) {
|
||||
y = offsetHeight - 5
|
||||
}
|
||||
return `${x}px, ${y}px`
|
||||
}
|
||||
const handleDocumentClick = (event) => {
|
||||
if (!show) return
|
||||
|
||||
if (event.target == dom_menu.value || dom_menu.value.contains(event.target)) return
|
||||
|
||||
if (show && menuStyles.transitionProperty != transition1) menuStyles.transitionProperty = transition1
|
||||
|
||||
onHide()
|
||||
}
|
||||
|
||||
watch(visible, visible => {
|
||||
visible ? handleShow() : handleHide()
|
||||
}, { immediate: true })
|
||||
|
||||
watch(location, location => {
|
||||
menuStyles.left = location.x + 2 + 'px'
|
||||
menuStyles.top = location.y + 'px'
|
||||
nextTick(() => {
|
||||
if (show) {
|
||||
if (menuStyles.transitionProperty != transition2) menuStyles.transitionProperty = transition2
|
||||
menuStyles.transform = `scaleY(1) translate(${handleGetOffsetXY(location.x, location.y)})`
|
||||
}
|
||||
})
|
||||
}, { deep: true })
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleDocumentClick)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.removeEventListener('click', handleDocumentClick)
|
||||
})
|
||||
|
||||
return {
|
||||
dom_menu,
|
||||
menuStyles,
|
||||
}
|
||||
}
|
|
@ -491,11 +491,17 @@ export const parseUrlParams = str => {
|
|||
}
|
||||
|
||||
export const getLyric = musicInfo => rendererInvoke(NAMES.mainWindow.get_lyric, `${musicInfo.source}_${musicInfo.songmid}`)
|
||||
export const setLyric = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric, {
|
||||
export const setLyric = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_raw, {
|
||||
id: `${musicInfo.source}_${musicInfo.songmid}`,
|
||||
lyrics: { lyric, tlyric, lxlyric },
|
||||
})
|
||||
export const clearLyric = () => rendererSend(NAMES.mainWindow.clear_lyric)
|
||||
export const setLyricEdited = (musicInfo, { lyric, tlyric, lxlyric }) => rendererSend(NAMES.mainWindow.save_lyric_edited, {
|
||||
id: `${musicInfo.source}_${musicInfo.songmid}`,
|
||||
lyrics: { lyric, tlyric, lxlyric },
|
||||
})
|
||||
export const removeLyricEdited = musicInfo => rendererSend(NAMES.mainWindow.remove_lyric_edited, `${musicInfo.source}_${musicInfo.songmid}`)
|
||||
|
||||
export const clearLyric = () => rendererSend(NAMES.mainWindow.clear_lyric_raw)
|
||||
|
||||
export const getMusicUrl = (musicInfo, type) => rendererInvoke(NAMES.mainWindow.get_music_url, `${musicInfo.source}_${musicInfo.songmid}_${type}`)
|
||||
export const setMusicUrl = (musicInfo, type, url) => rendererSend(NAMES.mainWindow.save_music_url, {
|
||||
|
|
|
@ -97,9 +97,9 @@ export default {
|
|||
watch(() => setting.value.player.mediaDeviceId, val => {
|
||||
currentStting.value.player.mediaDeviceId = val
|
||||
})
|
||||
watch(() => setting.value.playDetail.style.fontSize, val => {
|
||||
currentStting.value.playDetail.style.fontSize = val
|
||||
})
|
||||
watch(() => setting.value.playDetail, val => {
|
||||
currentStting.value.playDetail = val
|
||||
}, { deep: true })
|
||||
watch(() => setting.value.player.isMute, val => {
|
||||
currentStting.value.player.isMute = val
|
||||
})
|
||||
|
|
|
@ -4,7 +4,7 @@ dd
|
|||
.gap-top
|
||||
base-checkbox(id="setting_play_detail_font_zoom_enable" v-model="currentStting.playDetail.isZoomActiveLrc" :label="$t('setting__play_detail_font_zoom')")
|
||||
.gap-top
|
||||
base-checkbox(id="setting_play_detail_lyric_progress_enable" v-model="currentStting.playDetail.isShowLyricProgressSetting" :label="$t('setting__play_detail_detail_lyric_progress')")
|
||||
base-checkbox(id="setting_play_detail_lyric_progress_enable" v-model="currentStting.playDetail.isShowLyricProgressSetting" :label="$t('setting__play_detail_lyric_progress')")
|
||||
|
||||
dd
|
||||
h3#play_detail_align {{$t('setting__play_detail_align')}}
|
||||
|
@ -13,12 +13,6 @@ dd
|
|||
base-checkbox.gap-left(id="setting_play_detail_align_center" v-model="currentStting.playDetail.style.align" value="center" :label="$t('setting__play_detail_align_center')")
|
||||
//- base-checkbox.gap-left(id="setting_play_detail_align_right" v-model="currentStting.playDetail.style.align" value="right" :label="$t('setting__play_detail_align_right')")
|
||||
|
||||
dd
|
||||
h3#play_detail_align {{$t('setting__play_detail_font_size')}}
|
||||
div
|
||||
p.gap-top {{$t('setting__play_detail_font_size_current', { size: currentStting.playDetail.style.fontSize })}}
|
||||
base-btn.gap-top.btn(min @click="handleResetFont") {{$t('setting__play_detail_font_size_reset')}}
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -28,13 +22,8 @@ import { currentStting } from '../setting'
|
|||
export default {
|
||||
name: 'SettingPlayDetail',
|
||||
setup() {
|
||||
const handleResetFont = () => {
|
||||
currentStting.value.playDetail.style.fontSize = 100
|
||||
}
|
||||
|
||||
return {
|
||||
currentStting,
|
||||
handleResetFont,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue