mirror of
https://github.com/imsyy/SPlayer.git
synced 2025-11-25 03:14:57 +08:00
Compare commits
2 Commits
cea9f7b025
...
58e3c6e21c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58e3c6e21c | ||
|
|
68756f2502 |
2
components.d.ts
vendored
2
components.d.ts
vendored
@@ -101,7 +101,6 @@ declare module 'vue' {
|
||||
NQrCode: typeof import('naive-ui')['NQrCode']
|
||||
NRadio: typeof import('naive-ui')['NRadio']
|
||||
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
|
||||
NResult: typeof import('naive-ui')['NResult']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSkeleton: typeof import('naive-ui')['NSkeleton']
|
||||
@@ -124,6 +123,7 @@ declare module 'vue' {
|
||||
PlayerData: typeof import('./src/components/Player/PlayerData.vue')['default']
|
||||
PlayerMenu: typeof import('./src/components/Player/PlayerMenu.vue')['default']
|
||||
PlayerRightMenu: typeof import('./src/components/Player/PlayerRightMenu.vue')['default']
|
||||
PlayerSlider: typeof import('./src/components/Player/PlayerSlider.vue')['default']
|
||||
PlayerSpectrum: typeof import('./src/components/Player/PlayerSpectrum.vue')['default']
|
||||
PlaylistAdd: typeof import('./src/components/Modal/PlaylistAdd.vue')['default']
|
||||
PlaySetting: typeof import('./src/components/Setting/PlaySetting.vue')['default']
|
||||
|
||||
@@ -8,17 +8,7 @@
|
||||
]"
|
||||
>
|
||||
<!-- 进度条 -->
|
||||
<n-slider
|
||||
v-model:value="statusStore.progress"
|
||||
:step="0.01"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:tooltip="false"
|
||||
:keyboard="false"
|
||||
class="player-slider"
|
||||
@dragstart="player.pause(false)"
|
||||
@dragend="sliderDragend"
|
||||
/>
|
||||
<PlayerSlider />
|
||||
<!-- 信息 -->
|
||||
<div class="play-data">
|
||||
<!-- 封面 -->
|
||||
@@ -199,7 +189,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { DropdownOption } from "naive-ui";
|
||||
import { useMusicStore, useStatusStore, useDataStore, useSettingStore } from "@/stores";
|
||||
import { secondsToTime, calculateCurrentTime, convertSecondsToTime } from "@/utils/time";
|
||||
import { secondsToTime, convertSecondsToTime } from "@/utils/time";
|
||||
import { renderIcon, coverLoaded } from "@/utils/helper";
|
||||
import { toLikeSong } from "@/utils/auth";
|
||||
import {
|
||||
@@ -267,15 +257,6 @@ const songMoreOptions = computed<DropdownOption[]>(() => {
|
||||
];
|
||||
});
|
||||
|
||||
// 进度条拖拽结束
|
||||
const sliderDragend = () => {
|
||||
const seek = calculateCurrentTime(statusStore.progress, statusStore.duration);
|
||||
statusStore.playStatus = true;
|
||||
// 调整进度
|
||||
player.setSeek(seek);
|
||||
player.play();
|
||||
};
|
||||
|
||||
// 是否展示歌词
|
||||
const isShowLyrics = computed(() => {
|
||||
const isHasLrc = musicStore.isHasLrc;
|
||||
|
||||
@@ -77,17 +77,7 @@
|
||||
<!-- 进度条 -->
|
||||
<div class="slider">
|
||||
<span>{{ secondsToTime(statusStore.currentTime) }}</span>
|
||||
<n-slider
|
||||
v-model:value="statusStore.progress"
|
||||
:step="0.01"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:tooltip="false"
|
||||
:keyboard="false"
|
||||
class="player-slider"
|
||||
@dragstart="player.pause(false)"
|
||||
@dragend="sliderDragend"
|
||||
/>
|
||||
<PlayerSlider :show-tooltip="false" />
|
||||
<span>{{ secondsToTime(statusStore.duration) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,7 +92,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useMusicStore, useStatusStore, useDataStore } from "@/stores";
|
||||
import { secondsToTime, calculateCurrentTime } from "@/utils/time";
|
||||
import { secondsToTime } from "@/utils/time";
|
||||
import { openDownloadSong, openPlaylistAdd } from "@/utils/modal";
|
||||
import { toLikeSong } from "@/utils/auth";
|
||||
import player from "@/utils/player";
|
||||
@@ -110,15 +100,6 @@ import player from "@/utils/player";
|
||||
const dataStore = useDataStore();
|
||||
const musicStore = useMusicStore();
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
// 进度条拖拽结束
|
||||
const sliderDragend = () => {
|
||||
const seek = calculateCurrentTime(statusStore.progress, statusStore.duration);
|
||||
statusStore.playStatus = true;
|
||||
// 调整进度
|
||||
player.setSeek(seek);
|
||||
player.play();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
77
src/components/Player/PlayerSlider.vue
Normal file
77
src/components/Player/PlayerSlider.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<n-slider
|
||||
v-model:value="sliderProgress"
|
||||
:step="0.01"
|
||||
:min="0"
|
||||
:max="statusStore.duration"
|
||||
:keyboard="false"
|
||||
:format-tooltip="formatTooltip"
|
||||
:tooltip="showTooltip"
|
||||
:show-tooltip="showSliderTooltip"
|
||||
class="player-slider"
|
||||
@mouseenter="showSliderTooltip = true"
|
||||
@mouseleave="showSliderTooltip = false"
|
||||
@dragstart="startDrag"
|
||||
@dragend="endDrag"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useStatusStore } from "@/stores";
|
||||
import player from "@/utils/player";
|
||||
import { secondsToTime } from "@/utils/time";
|
||||
|
||||
withDefaults(defineProps<{ showTooltip?: boolean }>(), { showTooltip: true });
|
||||
|
||||
const statusStore = useStatusStore();
|
||||
|
||||
// 拖动时的临时值
|
||||
const dragValue = ref(0);
|
||||
// 是否拖动
|
||||
const isDragging = ref(false);
|
||||
// 是否显示提示
|
||||
const showSliderTooltip = ref(false);
|
||||
|
||||
// 实时进度
|
||||
const sliderProgress = computed({
|
||||
// 获取进度
|
||||
get: () => (isDragging.value ? dragValue.value : statusStore.currentTime),
|
||||
// 设置进度
|
||||
set: (value) => {
|
||||
// 若为拖动中
|
||||
if (isDragging.value) {
|
||||
dragValue.value = value;
|
||||
return;
|
||||
}
|
||||
// 结束或者为点击
|
||||
useThrottleFn((value: number) => {
|
||||
player.setSeek(value);
|
||||
}, 30);
|
||||
},
|
||||
});
|
||||
|
||||
// 开始拖拽
|
||||
const startDrag = () => {
|
||||
isDragging.value = true;
|
||||
// 立即赋值当前时间
|
||||
dragValue.value = statusStore.currentTime;
|
||||
};
|
||||
|
||||
// 结束拖拽
|
||||
const endDrag = () => {
|
||||
isDragging.value = false;
|
||||
// 直接更改进度
|
||||
player.setSeek(dragValue.value);
|
||||
};
|
||||
|
||||
// 格式化提示
|
||||
const formatTooltip = (value: number) => {
|
||||
return `${secondsToTime(value)} / ${secondsToTime(statusStore.duration)}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.player-slider {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,11 @@
|
||||
import { useMusicStore, useSettingStore, useStatusStore } from "@/stores";
|
||||
import { parsedLyricsData, parseTTMLToAMLL, parseTTMLToYrc, resetSongLyric } from "../lyric";
|
||||
import {
|
||||
parsedLyricsData,
|
||||
parseLocalLyric,
|
||||
parseTTMLToAMLL,
|
||||
parseTTMLToYrc,
|
||||
resetSongLyric,
|
||||
} from "../lyric";
|
||||
import { songLyric, songLyricTTML } from "@/api/song";
|
||||
import { parseTTML } from "@applemusic-like-lyrics/lyric";
|
||||
import { LyricLine } from "@applemusic-like-lyrics/core";
|
||||
@@ -26,6 +32,7 @@ export const getLyricData = async (id: number) => {
|
||||
try {
|
||||
// 检测本地歌词覆盖
|
||||
const getLyric = getLyricFun(settingStore.localLyricPath, id);
|
||||
|
||||
// 并发请求:如果 TTML 先到并且有效,则直接采用 TTML,不再等待或覆盖为 LRC
|
||||
const lrcPromise = getLyric("lrc", songLyric);
|
||||
const ttmlPromise = settingStore.enableTTMLLyric ? getLyric("ttml", songLyricTTML) : null;
|
||||
@@ -44,6 +51,19 @@ export const getLyricData = async (id: number) => {
|
||||
statusStore.usingTTMLLyric = false;
|
||||
return;
|
||||
}
|
||||
// 本地 TTML 使用 parseLocalLyric,在线 TTML 使用原有解析方式
|
||||
if (ttmlLocal) {
|
||||
parseLocalLyric(ttmlContent, "ttml");
|
||||
statusStore.usingTTMLLyric = true;
|
||||
ttmlAdopted = true;
|
||||
if (!settled) {
|
||||
statusStore.lyricLoading = false;
|
||||
settled = true;
|
||||
}
|
||||
console.log("✅ TTML lyrics adopted (prefer TTML)");
|
||||
return;
|
||||
}
|
||||
// 在线 TTML 解析
|
||||
const parsedResult = parseTTML(ttmlContent);
|
||||
if (!parsedResult?.lines?.length) {
|
||||
statusStore.usingTTMLLyric = false;
|
||||
@@ -99,7 +119,14 @@ export const getLyricData = async (id: number) => {
|
||||
const { lyric: lyricRes, isLocal: lyricLocal } = await lrcPromise;
|
||||
// 如果 TTML 已采用,则忽略 LRC
|
||||
if (ttmlAdopted) return;
|
||||
parsedLyricsData(lyricRes, lyricLocal && !settingStore.enableExcludeLocalLyrics);
|
||||
// 如果没有歌词内容,直接返回
|
||||
if (!lyricRes) return;
|
||||
// 本地歌词使用 parseLocalLyric,在线歌词使用 parsedLyricsData
|
||||
if (lyricLocal) {
|
||||
parseLocalLyric(lyricRes, "lrc");
|
||||
} else {
|
||||
parsedLyricsData(lyricRes, !settingStore.enableExcludeLocalLyrics);
|
||||
}
|
||||
statusStore.usingTTMLLyric = false;
|
||||
if (!settled) {
|
||||
statusStore.lyricLoading = false;
|
||||
|
||||
@@ -637,6 +637,7 @@ onBeforeUnmount(() => {
|
||||
.lyric-container {
|
||||
height: 100%;
|
||||
padding: 0 8px;
|
||||
cursor: move;
|
||||
.lyric-line {
|
||||
width: 100%;
|
||||
line-height: normal;
|
||||
@@ -756,15 +757,18 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
&.locked {
|
||||
cursor: default;
|
||||
.song-name,
|
||||
.menu-btn,
|
||||
.lyric-container {
|
||||
pointer-events: none;
|
||||
}
|
||||
&:hover {
|
||||
.lock-btn {
|
||||
opacity: 1;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lyric-container {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user