fix(yrc): 移动位置并改为使用watch实现

又是手欠点了格式化的一天
This commit is contained in:
Pizero 2025-01-13 21:18:19 +08:00
parent 33b21b3be7
commit 2d5a4fdfe3
No known key found for this signature in database
GPG Key ID: 577F9430CCAA0812
2 changed files with 289 additions and 248 deletions

View File

@ -51,21 +51,27 @@
<paw />
</Icon> -->
<span class="yrc-box">
<span class="yrc-2 lrc-text text-hidden" id="yrc-2-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-over-char-${i[2]}-${i[3]}`" v-html="i[4]">
</span>
<span class="yrc-2 lrc-text text-hidden" id="yrc-2-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-over-char-${i[2]}-${i[3]}`" v-html="i[4]">
</span>
<span class="yrc-1 lrc-text text-hidden" id="yrc-1-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-char-${i[2]}-${i[3]}`" :class="[
'yrc-char',
i[0] && Number(i[6]) > 0 ? 'fade-in' : 'fade-in-start',
i[0] && Number(i[5]) > 1019 && Number(i[6]) > 0 ? 'long-tone' : 'fade-in-start',
i[0] && Number(i[6]) <= 0 ? 'fade-out' : '',
i[1] ? 'yrc-style-s2' : 'yrc-style-s1'
]" :id="`lrc-char-${i[2]}-${i[3]}`" v-html="i[4]">
</span>
</span>
<span class="yrc-1 lrc-text text-hidden" id="yrc-1-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-char-${i[2]}-${i[3]}`" :class="[
'yrc-char',
i[0] && Number(i[6]) > 0 ? 'fade-in' : 'fade-in-start',
i[0] && Number(i[5]) > 1019 && Number(i[6]) > 0 ? 'long-tone' : 'fade-in-start',
i[0] && Number(i[6]) <= 0 ? 'fade-out' : '',
i[1] ? 'yrc-style-s2' : 'yrc-style-s1'
]" :id="`lrc-char-${i[2]}-${i[3]}`" v-html="i[4]">
</span>
</span>
<!-- <span class="yrc-2 lrc-text text-hidden" id="yrc-2-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-over-char-${i[2]}-${i[3]}`" v-html="i[4]" />
</span>
<span class="yrc-1 lrc-text text-hidden" id="yrc-1-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-char-${i[2]}-${i[3]}`" v-html="i[4]" />
</span> -->
</span>
<!-- <Icon size="20" style="transform: rotate(18deg);" class="paws-2">
<paw />
</Icon> -->
@ -96,17 +102,18 @@
<script setup>
import { MusicOne } from "@icon-park/vue-next";
import { Icon } from "@vicons/utils";
import { Paw } from "@vicons/ionicons5";
// import { Icon } from "@vicons/utils";
// import { Paw } from "@vicons/ionicons5";
import { mainStore } from "@/store";
import config from "@/../package.json";
import { ref, watch, computed, onMounted, nextTick } from "vue";
import { ref, watch, computed, onMounted, nextTick, onUpdated } from "vue";
const store = mainStore();
const fullYear = new Date().getFullYear();
const lrcContainer = ref(null);
const scrollPosition = ref(0);
const currentLine = ref(0);
// const lrcContainer = ref(null);
// const scrollPosition = ref(0);
// const currentLine = ref(0);
const showProgressIcon = ref(false);
const handleMouseEnter = () => {
@ -191,6 +198,75 @@ const progressBarWidth = computed(() => {
if (!store.playerState) return 0;
return (store.playerCurrentTime / store.playerDuration) * 100;
});
// yrc part
watch(() => store.getPlayerLrc, (_new,_old) => {
const isLineByLine = !store.yrcEnable || store.yrcTemp.length == 0 || store.yrcLoading
if (!store.playerYrcShowPro || isLineByLine) {
return
};
// const now = player.value.audioStatus.playedTime * 1000;
const audio = document.querySelector('audio')
if (audio == undefined) {
return
}
const now = audio.currentTime * 1000;
const yrc2 = document.getElementsByClassName("yrc-box")[0];
if (yrc2 == undefined) {
return;
};
const outputDom = yrc2.querySelectorAll("#yrc-2-wrap span");
const inputDom = yrc2.querySelectorAll("#yrc-1-wrap span");
if (inputDom.length == 0 || outputDom.length == 0) {
return;
};
// console.log(store.yrcTemp[0][0])
const yrcFiltered = store.yrcTemp.filter((i) => i[0] < now && now < i[0] + i[1]);
if (yrcFiltered.length == 0) {
// console.log('h')
return;
};
const nowLine = yrcFiltered[yrcFiltered.length - 1][2]
// console.log(nowLine)
for (let i = 0; i < nowLine.length; i++) {
// console.log(JSON.stringify(nowLine[i]))
const [[start, duration], _a, _b, _c] = nowLine[i];
const inputItem = inputDom[i];
if (!inputItem || inputItem.hasAttribute('data-start')) {
return;
};
const computedStyle = window.getComputedStyle(inputItem);
const width = parseFloat(computedStyle.width);
if (isNaN(width)) {
inputItem.removeAttribute('data-start');
return;
};
const outputItem = outputDom[i];
const animateOptions = {
delay: Math.max(0, start - now),
duration: duration,
fill: "forwards",
easing: "linear",
};
outputItem.style.transform = "translateY(-1px)"
const outputAnimate = outputItem.animate(
[
{ width: 0 },
{ width: `${width}px` },
],
animateOptions,
);
// animationTmp.push(outputAnimate);
outputAnimate.onfinish = () => {
outputItem.style.transform = "translateY(1px)";
// animationTmp = animationTmp.filter(a => a !== outputAnimate);
};
inputItem.setAttribute("data-start",true)
};
})
// setInterval(yrcPro, 50);
</script>
<style lang="scss" scoped>

View File

@ -1,8 +1,24 @@
<template>
<APlayer v-if="playList[0]" ref="player" :audio="playList" :autoplay="store.playerAutoplay" :theme="theme"
:autoSwitch="false" :loop="store.playerLoop" :order="store.playerOrder" :volume="volume" :showLrc="true"
:listFolded="listFolded" :listMaxHeight="listMaxHeight" :noticeSwitch="false" @play="onPlay" @pause="onPause"
@timeupdate="onTimeUp" @error="loadMusicError" />
<APlayer
v-if="playList[0]"
ref="player"
:audio="playList"
:autoplay="store.playerAutoplay"
:theme="theme"
:autoSwitch="false"
:loop="store.playerLoop"
:order="store.playerOrder"
:volume="volume"
:showLrc="true"
:listFolded="listFolded"
:listMaxHeight="listMaxHeight"
:noticeSwitch="false"
@play="onPlay"
@pause="onPause"
@timeupdate="onTimeUp"
@error="loadMusicError"
@loadstart="onLoadStart"
/>
</template>
<script setup>
@ -96,7 +112,7 @@ onMounted(() => {
navigator.mediaSession.setActionHandler("previoustrack", () => {
changeSong(0); // 0
});
};
}
console.log("音乐加载完成");
});
} catch (err) {
@ -115,11 +131,82 @@ onMounted(() => {
const voice = import.meta.env.VITE_TTS_Voice;
const vstyle = import.meta.env.VITE_TTS_Style;
SpeechLocal("播放器加载失败.mp3");
};
}
}
});
});
const fetchYrc = async (yrcUrl) => {
const yrcSource = await fetch(yrcUrl);
const yrcText = await yrcSource.text();
store.yrcIndex = playIndex.value;
if (yrcText.startsWith("[ch:0]")) {
store.yrcEnable = true;
store.yrcTemp = decodeYrc(yrcText);
store.yrcLoading = false;
return;
} else if (!store.playerYrcATDB) {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
return;
}
// AMLL TTML Database
const songUrlInf = new URLSearchParams(new URL(yrcUrl).search);
const songId = songUrlInf.get("id");
const songServer = songUrlInf.get("server");
if (!songId) {
return;
}
const songUrlInfUrl = store.playerYrcATDBF
? {
netease: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
tencent: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`,
}
: {
netease: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
tencent: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`,
};
if (!["netease", "tencent"].includes(songServer)) {
return;
}
try {
const amllUrl = songUrlInfUrl[songServer].replace("${songIdlrc}", songId);
const amllSource = await fetch(amllUrl);
const amllText = await amllSource.text();
} catch (e) {
console.log(e);
store.yrcEnable = true;
store.yrcTemp = decodeYrc(amllText);
store.yrcLoading = false;
}
};
function onLoadStart() {
try {
const aplayer = player.value.aplayer;
const lyrics = aplayer.lyrics[playIndex.value];
if (store.playerYrcShow != true) {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
return;
}
if (store.yrcIndex == playIndex.value) {
return;
}
const yrcUrl = aplayer.audio[aplayer.index]["lrc"] + "&yrc=true";
store.yrcIndex = playIndex.value;
store.yrcLoading = true;
fetchYrc(yrcUrl);
} catch (error) {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
console.error(error);
}
}
//
const onPlay = () => {
console.log("播放");
@ -150,7 +237,7 @@ const onPlay = () => {
},
],
});
};
}
if (store.webSpeech) {
if (store.playerSpeechName) {
@ -159,15 +246,15 @@ const onPlay = () => {
const vstyle = import.meta.env.VITE_TTS_Style;
Speech(
"正在播放,“" +
store.getPlayerData.artist +
"”的歌曲,《" +
store.getPlayerData.name +
"》。",
store.getPlayerData.artist +
"”的歌曲,《" +
store.getPlayerData.name +
"》。",
voice,
vstyle,
);
};
};
}
}
};
//
@ -175,227 +262,102 @@ const onPause = () => {
store.setPlayerState(player.value.audioRef.paused);
};
//
function showYrc() {
// YRC ...bushi... qrc yrc
showYrcRunning = 1;
try {
// try 西[20720,-4200]西 try
if (player.value == null) {
return requestAnimationFrame(showYrc);
function doLrcSync(lrc) {
const playerLyricIndex = player.value.aplayer.lyricIndex;
const output = [[true, 1, playerLyricIndex, 0, lrc]];
if (store.playerLrc.toString() != output.toString()) {
store.setPlayerLrc(output);
return;
}
}
function syncLrc() {
//
const aplayer = player.value.aplayer;
// console.log(playIndex.value)
const lyrics = aplayer.lyrics[playIndex.value];
const playerLyricIndex = player.value.aplayer.lyricIndex;
if (lyrics === undefined || lyrics[playerLyricIndex] === undefined) {
return doLrcSync("歌词加载中...");
}
let lrc = lyrics[playerLyricIndex][1];
if (lrc === "Loading") {
return doLrcSync("歌词加载中...");
} else if (lrc === "Not available" && !store.playerYrcATDB) {
return doLrcSync("歌词加载失败");
} else if (lrc === "Not availible") {
//
const songUrlInfw = new URLSearchParams(new URL(yrcUrl).search);
const songIdlrc = songUrlInfw.get("id");
const songServerlrc = songUrlInfw.get("server");
if (!songIdlrc) {
return doLrcSync("歌词加载失败");
}
const aplayer = player.value.aplayer;
const lyrics = aplayer.lyrics[playIndex.value];
if (store.playerYrcShow != true) {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
}
else {
if (store.yrcIndex != playIndex.value) {
const yrcUrl = aplayer.audio[aplayer.index]["lrc"] + "&yrc=true";
store.yrcIndex = playIndex.value;
store.yrcLoading = true;
fetch(yrcUrl)
.then((i) => {
if (i.status < 200 || i.status >= 400) {
throw i.text();
};
return i.text();
})
.then((i) => {
store.yrcIndex = playIndex.value;
if (i.startsWith("[ch:0]")) {
store.yrcEnable = true;
store.yrcTemp = decodeYrc(i);
store.yrcLoading = false;
return;
} else if (!store.playerYrcATDB) {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
return;
};
// AMLL TTML Database
const songUrlInf = new URLSearchParams(new URL(yrcUrl).search)
const songId = songUrlInf.get('id')
const songServer = songUrlInf.get("server");
if (!songId) {
return;
};
const songUrlInfUrl = store.playerYrcATDBF
? {
'netease': `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
'tencent': `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`
}
: {
'netease': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
'tencent': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`
};
if (!['netease', 'tencent'].includes(songServer)) {
return;
};
const amllUrl = songUrlInfUrl[songServer]
return fetch(amllUrl)
.then((response) => {
if (!response.ok) {
throw response.text()
};
return response.text();
}).then((amllyrcfile) => {
store.yrcEnable = true;
store.yrcTemp = decodeYrc(amllyrcfile);
store.yrcLoading = false;
});
}).catch(() => {
store.yrcEnable = false;
store.yrcTemp = [];
store.yrcLoading = false;
});
};
};
if (!store.yrcEnable || store.yrcTemp.length == 0 || store.yrcLoading) {
//
let lyricIndex = player.value.aplayer.lyricIndex;
if (lyrics === undefined || lyrics[lyricIndex] === undefined) {
return requestAnimationFrame(showYrc);
}
let lrc = lyrics[lyricIndex][1];
if (lrc === "Loading") {
lrc = "歌词加载中...";
} else if (lrc === "Not available") {
if (store.playerYrcATDB) {
//
const songUrlInfw = new URLSearchParams(new URL(yrcUrl).search)
const songIdlrc = songUrlInfw.get('id')
const songServerlrc = songUrlInfw.get("server");
if (songIdlrc) {
const songUrlInfwurl = store.playerYrcATDBF
? {
'netease': `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.lrc`,
'tencent': `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.lrc`
}
: {
'netease': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.lrc`,
'tencent': `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.lrc`
};
if (!['netease', 'tencent'].includes(songServerlrc)) {
return;
};
const amllUrllrc = songUrlInfwurl[songServerlrc].replace('${songIdlrc}', songIdlrc);
fetch(amllUrllrc)
.then((response) => {
if (response.status === 404 || !response.ok) {
lrc = "歌词加载失败";
return;
} else {
return response.text();
}
})
.catch(() => {
lrc = "歌词加载失败";
});
}
} else {
lrc = "歌词加载失败";
const songUrlInfwurl = store.playerYrcATDBF
? {
netease: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.lrc`,
tencent: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.lrc`,
}
: {
netease: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.lrc`,
tencent: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.lrc`,
};
}
const output = [[true, 1, lyricIndex, 0, lrc]];
if (store.playerLrc.toString() != output.toString()) {
store.setPlayerLrc(output);
};
return requestAnimationFrame(showYrc);
};
//
const now = player.value.audioStatus.playedTime * 1000;
const yrcFiltered = store.yrcTemp.filter((i) => i[0] < now);
let animationTmp = [];
const yrcLyric =
yrcFiltered.length > 0
? yrcFiltered.slice(-1)[0][2].map((it) => {
if (!["netease", "tencent"].includes(songServerlrc)) {
return doLrcSync("歌词加载失败");
}
const amllUrllrc = songUrlInfwurl[songServerlrc].replace("${songIdlrc}", songIdlrc);
fetch(amllUrllrc)
.then((response) => response.text())
.catch(() => {
doLrcSync("歌词加载失败");
});
return doLrcSync("歌词加载中...");
}
doLrcSync(lrc);
}
function syncYrc() {
const now = player.value.audioStatus.playedTime * 1000;
const yrcFiltered = store.yrcTemp.filter((i) => i[0] < now);
const yrcLyric =
yrcFiltered.length > 0
? yrcFiltered.slice(-1)[0][2].map((it) => {
const [[start, duration], word, line, row] = it;
const isCurrent = now >= start && now <= start + duration;
const isSungLyrics = start + duration < now;
const lessdur = start + duration - now;
return [isCurrent, isSungLyrics, line, row, word, duration, lessdur, "auto"];
})
: [[true, 1, 0, 0, `${store.playerTitle} - ${store.playerArtist}`]];
if (store.playerLrc.toString() != yrcLyric.toString()) {
store.setPlayerLrc(yrcLyric);
};
if (store.playerYrcShowPro) {
// N
if (yrcFiltered.length === 0) {
return requestAnimationFrame(showYrc);
};
const lineStart = yrcFiltered.slice(-1)[0][0];
if (nowLineStart == lineStart) {
return requestAnimationFrame(showYrc);
};
const yrc2 = document.getElementsByClassName("yrc-box")[0];
if (yrc2 == undefined) {
return requestAnimationFrame(showYrc);
};
const outputDom = yrc2.querySelectorAll("#yrc-2-wrap span");
const inputDom = yrc2.querySelectorAll("#yrc-1-wrap span");
if (inputDom.length == 0 || outputDom.length == 0) {
return requestAnimationFrame(showYrc);
};
const nowLineWord = yrcFiltered.slice(-1)[0][2];
for (let i = 0; i < nowLineWord.length; i++) {
const [[start, duration], _a, _b, _c] = nowLineWord[i];
const intputItem = inputDom[i];
if (!intputItem || intputItem.hasAttribute('data-start')) {
return requestAnimationFrame(showYrc);
};
const computedStyle = window.getComputedStyle(intputItem);
const width = parseFloat(computedStyle.width);
if (isNaN(width)) {
intputItem.removeAttribute('data-start');
return requestAnimationFrame(showYrc);
};
const outputItem = outputDom[i];
const animateOptions = {
delay: Math.max(0, start - now),
duration: duration,
fill: "forwards",
easing: "linear",
};
outputItem.style.transform = "translateY(-1px)"
const outputAnimate = outputItem.animate(
[
{ width: 0, },
{ width: `${width}px` },
],
animateOptions,
);
animationTmp.push(outputAnimate);
outputAnimate.onfinish = () => {
outputItem.style.transform = "translateY(1px)";
animationTmp = animationTmp.filter(a => a !== outputAnimate);
};
};
nowLineStart = yrcFiltered.slice(-1)[0][0];
};
return requestAnimationFrame(showYrc);
} catch (error) {
console.error(error);
return requestAnimationFrame(showYrc);
};
};
: [[true, 1, 0, 0, `${store.playerTitle} - ${store.playerArtist}`]];
if (store.playerLrc.toString() != yrcLyric.toString()) {
store.setPlayerLrc(yrcLyric);
}
}
//
const onTimeUp = () => {
store.playerCurrentTime = player.value.audioStatus.playedTime;
store.playerDuration = player.value.audioStatus.duration;
if (showYrcRunning == 0) {
requestAnimationFrame(showYrc);
// YRC ...bushi... qrc yrc
showYrcRunning = 1;
try {
// try 西[20720,-4200]西 try
if (player.value == null) {
return;
}
const aplayer = player.value.aplayer;
const isLineByLine = false;
// const isLineByLine = !store.yrcEnable || store.yrcTemp.length == 0 || store.yrcLoading
if (isLineByLine) {
return syncLrc();
} else {
return syncYrc();
}
} catch (error) {
console.error(error);
}
};
const onLoadStart = () => {
};
//
const playToggle = () => {
player.value.toggle();
@ -419,7 +381,6 @@ const toggleList = () => {
player.value.toggleList();
};
//
const loadMusicError = () => {
let notice = "";
@ -430,7 +391,7 @@ const loadMusicError = () => {
const voice = import.meta.env.VITE_TTS_Voice;
const vstyle = import.meta.env.VITE_TTS_Style;
SpeechLocal("歌曲加载失败.mp3");
};
}
} else {
notice = "播放歌曲出现错误";
if (store.webSpeech) {
@ -438,7 +399,7 @@ const loadMusicError = () => {
const voice = import.meta.env.VITE_TTS_Voice;
const vstyle = import.meta.env.VITE_TTS_Style;
SpeechLocal("播放器未知异常.mp3");
};
}
}
ElMessage({
message: notice,
@ -495,14 +456,18 @@ defineExpose({ playToggle, changeVolume, changeSong, toggleList });
text-align: left;
margin: 7px 0 6px 6px;
height: 44px;
-webkit-mask: linear-gradient(#fff 15%,
#fff 85%,
hsla(0deg, 0%, 100%, 0.6) 90%,
hsla(0deg, 0%, 100%, 0));
mask: linear-gradient(#fff 15%,
#fff 85%,
hsla(0deg, 0%, 100%, 0.6) 90%,
hsla(0deg, 0%, 100%, 0));
-webkit-mask: linear-gradient(
#fff 15%,
#fff 85%,
hsla(0deg, 0%, 100%, 0.6) 90%,
hsla(0deg, 0%, 100%, 0)
);
mask: linear-gradient(
#fff 15%,
#fff 85%,
hsla(0deg, 0%, 100%, 0.6) 90%,
hsla(0deg, 0%, 100%, 0)
);
&::before,
&::after {