update: use new lyric parser
This commit is contained in:
parent
b3ba38c91c
commit
57de9f426f
2
bunfig.toml
Normal file
2
bunfig.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[install.scopes]
|
||||
"@jsr" = "https://npm.jsr.io"
|
@ -35,5 +35,8 @@
|
||||
"concurrently": "^9.0.1",
|
||||
"cross-env": "^7.0.3"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {},
|
||||
"trustedDependencies": [
|
||||
"svelte-preprocess"
|
||||
]
|
||||
}
|
||||
|
@ -46,7 +46,6 @@
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
position: relative;
|
||||
@ -55,6 +54,6 @@
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
transition: .45s;
|
||||
filter: brightness(0.8);
|
||||
filter: saturate(1.2);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import createSpring from '@core/graphics/spring';
|
||||
import type { ScriptItem } from '@core/lyrics/type';
|
||||
import type { LyricWord, ScriptItem } from '@core/lyrics/type';
|
||||
import type { LyricPos } from './type';
|
||||
import type { Spring } from '@core/graphics/spring/spring';
|
||||
|
||||
@ -134,6 +134,53 @@
|
||||
};
|
||||
|
||||
export const getRef = () => ref;
|
||||
|
||||
// Calculate if the current character should be highlighted based on progress
|
||||
const isCharacterHighlighted = (line: ScriptItem, word: LyricWord, charIndex: number, progress: number) => {
|
||||
const charProgress = getCharacterProgress(word, charIndex);
|
||||
return line.start <= progress &&
|
||||
progress <= line.end &&
|
||||
progress > charProgress;
|
||||
};
|
||||
|
||||
// Get the progress timing for a specific character in a word
|
||||
const getCharacterProgress = (word: LyricWord, charIndex: number) => {
|
||||
const { startTime, endTime } = word;
|
||||
const wordDuration = endTime - startTime;
|
||||
return wordDuration * (charIndex / word.word.length) + startTime;
|
||||
};
|
||||
|
||||
// Calculate the transition duration for a character
|
||||
const getTransitionDuration = (word: LyricWord, charIndex: number) => {
|
||||
const { startTime, endTime } = word;
|
||||
const wordDuration = endTime - startTime;
|
||||
const charDuration = wordDuration * ((charIndex + 1) / word.word.length);
|
||||
|
||||
// If duration is less than 0.6s, we'll use CSS class with fixed duration
|
||||
if (charDuration < 0.6) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, calculate custom duration
|
||||
return charDuration / 1.6;
|
||||
};
|
||||
|
||||
// Generate the CSS classes for a character
|
||||
const getCharacterClasses = (line: ScriptItem, word: LyricWord, charIndex: number, progress: number) => {
|
||||
const baseClasses = 'inline-block';
|
||||
const opacityClass = isCharacterHighlighted(line, word, charIndex, progress)
|
||||
? 'opacity-100 text-glow'
|
||||
: 'opacity-35';
|
||||
|
||||
return `${baseClasses} ${opacityClass}`.trim();
|
||||
};
|
||||
|
||||
// Generate the style string for a character
|
||||
const getCharacterStyle = (line: ScriptItem, word: LyricWord, charIndex: number, progress: number) => {
|
||||
const duration = getTransitionDuration(word, charIndex);
|
||||
const progressAtCurrentLine = progress <= line.end;
|
||||
return (duration && progressAtCurrentLine) ? `transition-duration: ${duration}s;` : 'transition-duration: 200ms;';
|
||||
};
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
@ -150,8 +197,7 @@
|
||||
on:touchstart={() => {
|
||||
clickMask.style.backgroundColor = 'rgba(255,255,255,.3)';
|
||||
}}
|
||||
style="transform: translate3d({positionX}px, {positionY}px, 0); transition-property: text-shadow;
|
||||
transition-duration: 0.36s; transition-timing-function: ease-out;
|
||||
style="transform: translate3d({positionX}px, {positionY}px, 0);
|
||||
transform-origin: center left; font-family: LyricFont, sans-serif;"
|
||||
>
|
||||
<span
|
||||
@ -161,7 +207,7 @@
|
||||
>
|
||||
</span>
|
||||
{#if debugMode}
|
||||
<span class="text-lg absolute -translate-y-7">
|
||||
<span class="text-white text-lg absolute -translate-y-7">
|
||||
{index}: duration: {(line.end - line.start).toFixed(3)}, {line.start.toFixed(3)}~{line.end.toFixed(3)}
|
||||
</span>
|
||||
{/if}
|
||||
@ -171,14 +217,8 @@
|
||||
{#if word.word}
|
||||
{#each word.word.split('') as chr, i}
|
||||
<span
|
||||
class={(line.start <= progress &&
|
||||
progress <= line.end &&
|
||||
progress > (word.endTime - word.startTime) * (i / word.word.length) + word.startTime
|
||||
? 'opacity-100 text-glow'
|
||||
: 'opacity-35') + ' inline-block ' +
|
||||
(((word.endTime - word.startTime) > 0.6) ? '' : 'duration-200')}
|
||||
style={((word.endTime - word.startTime) < 0.3) ? '' :
|
||||
`transition-duration: ${(word.endTime - word.startTime) / 1.3}s;`}
|
||||
class={getCharacterClasses(line, word, i, progress)}
|
||||
style={getCharacterStyle(line, word, i, progress)}
|
||||
>
|
||||
{chr}
|
||||
</span>
|
||||
@ -188,7 +228,7 @@
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 duration-200
|
||||
class={`text-white text-[2rem] leading-9 lg:text-5xl lg:leading-[4rem] font-semibold mr-4 duration-200
|
||||
${line.start <= progress && progress <= line.end ? 'opacity-100 text-glow' : 'opacity-35'}`}
|
||||
>
|
||||
{line.text}
|
||||
@ -205,10 +245,9 @@
|
||||
|
||||
<style>
|
||||
.text-glow {
|
||||
text-shadow:
|
||||
0 0 3px #ffffff2c,
|
||||
0 0 6px #ffffff2c,
|
||||
0 15px 30px rgba(0, 0, 0, 0.11),
|
||||
0 5px 15px rgba(0, 0, 0, 0.08);
|
||||
text-shadow: 0 0 3px #ffffff2c,
|
||||
0 0 6px #ffffff2c,
|
||||
0 15px 30px rgba(0, 0, 0, 0.11),
|
||||
0 5px 15px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { LrcJsonData, ScriptItem } from '@core/lyrics/type';
|
||||
import { type ScriptItem, type LyricData } from '@alikia/aqualyrics';
|
||||
import { onMount } from 'svelte';
|
||||
import LyricLine from './lyricLine.svelte';
|
||||
import createLyricsSearcher from '@core/lyrics/lyricSearcher';
|
||||
@ -15,7 +15,7 @@
|
||||
document.body.style.overflow = 'hidden';
|
||||
|
||||
// Props
|
||||
export let originalLyrics: LrcJsonData;
|
||||
export let originalLyrics: LyricData;
|
||||
export let progress: number;
|
||||
export let player: HTMLAudioElement | null;
|
||||
|
||||
@ -243,13 +243,32 @@
|
||||
player.currentTime = originalLyrics.scripts[lyricIndex].start;
|
||||
player.play();
|
||||
}
|
||||
|
||||
let lastFPSTime = performance.now();
|
||||
let frameCount = 0;
|
||||
let fps = 0;
|
||||
|
||||
function calculateFPS(t: number) {
|
||||
// 计算时间差
|
||||
const deltaTime = t - lastFPSTime;
|
||||
frameCount ++;
|
||||
if (frameCount % 5 == 0) {
|
||||
fps = 1000 / deltaTime;
|
||||
}
|
||||
lastFPSTime = t;
|
||||
// 请求下一帧
|
||||
requestAnimationFrame(calculateFPS);
|
||||
}
|
||||
|
||||
// 开始检测帧率
|
||||
requestAnimationFrame(calculateFPS);
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
|
||||
{#if debugMode}
|
||||
<span class="text-lg absolute">
|
||||
progress: {progress.toFixed(2)}, nextUpdate: {nextUpdate}, scrolling: {scrolling}, current: {currentLyricIndex}
|
||||
<span class="text-white text-lg absolute z-50 px-2 py-0.5 m-2 rounded-3xl bg-white bg-opacity-20 backdrop-blur-lg right-0 font-mono">
|
||||
{fps.toFixed(1)}fps, progress: {progress.toFixed(2)}, nextUpdate: {nextUpdate}, scrolling: {scrolling}, current: {currentLyricIndex}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
@ -47,7 +47,6 @@ export class Spring {
|
||||
return (
|
||||
Math.abs(this.targetPosition - this.currentPosition) < 0.01 &&
|
||||
this.getV(this.currentTime) < 0.01 &&
|
||||
this.getV2(this.currentTime) < 0.01 &&
|
||||
this.queueParams === undefined &&
|
||||
this.queuePosition === undefined
|
||||
);
|
||||
|
@ -26,6 +26,7 @@
|
||||
"vite-plugin-wasm": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alikia/aqualyrics": "npm:@jsr/alikia__aqualyrics",
|
||||
"@applemusic-like-lyrics/core": "^0.1.3",
|
||||
"@applemusic-like-lyrics/lyric": "^0.2.2",
|
||||
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||
|
@ -12,43 +12,44 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify/svelte": "^4.0.2",
|
||||
"@sveltejs/adapter-auto": "^3.2.0",
|
||||
"@sveltejs/adapter-node": "^5.0.1",
|
||||
"@sveltejs/kit": "^2.5.9",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.0",
|
||||
"@types/eslint": "^8.56.10",
|
||||
"@types/node": "^20.14.10",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/adapter-node": "^5.2.9",
|
||||
"@sveltejs/kit": "^2.8.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||
"@types/eslint": "^8.56.12",
|
||||
"@types/node": "^20.17.6",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-svelte": "^2.39.0",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-svelte": "^3.2.3",
|
||||
"eslint-plugin-svelte": "^2.46.0",
|
||||
"postcss": "^8.4.49",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-svelte": "^3.2.8",
|
||||
"svelte": "^4.2.19",
|
||||
"svelte-check": "^3.7.1",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.5",
|
||||
"svelte-check": "^3.8.6",
|
||||
"tailwindcss": "^3.4.15",
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "5.4.6",
|
||||
"vite-plugin-wasm": "^3.3.0",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@alikia/aqualyrics": "npm:@jsr/alikia__aqualyrics",
|
||||
"@applemusic-like-lyrics/core": "^0.1.3",
|
||||
"@applemusic-like-lyrics/lyric": "^0.2.2",
|
||||
"@applemusic-like-lyrics/lyric": "^0.2.4",
|
||||
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@types/bun": "^1.1.6",
|
||||
"@types/bun": "^1.1.13",
|
||||
"bezier-easing": "^2.1.0",
|
||||
"jotai": "^2.8.0",
|
||||
"jotai": "^2.10.2",
|
||||
"jotai-svelte": "^0.0.2",
|
||||
"jss": "^10.10.0",
|
||||
"jss-preset-default": "^10.10.0",
|
||||
"localforage": "^1.10.0",
|
||||
"lrc-parser-ts": "^1.0.3",
|
||||
"music-metadata-browser": "^2.5.10",
|
||||
"music-metadata-browser": "^2.5.11",
|
||||
"node-cache": "^5.1.2",
|
||||
"rollup-plugin-node-polyfills": "^0.2.1",
|
||||
"uuid": "^9.0.1"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,13 +7,12 @@
|
||||
import extractFileName from '@core/utils/extractFileName';
|
||||
import localforage from 'localforage';
|
||||
import { writable } from 'svelte/store';
|
||||
import lrcParser from '@core/lyrics/lrc/parser';
|
||||
import type { LrcJsonData } from '@core/lyrics/type';
|
||||
import { type LyricData } from "@alikia/aqualyrics";
|
||||
import userAdjustingProgress from '@core/state/userAdjustingProgress';
|
||||
import type { IAudioMetadata } from 'music-metadata-browser';
|
||||
import { onMount } from 'svelte';
|
||||
import progressBarRaw from '@core/state/progressBarRaw';
|
||||
import { parseTTML } from '@core/lyrics/ttml';
|
||||
import { parseTTML, parseLRC } from '@alikia/aqualyrics';
|
||||
import NewLyrics from '@core/components/lyrics/newLyrics.svelte';
|
||||
|
||||
const audioId = $page.params.id;
|
||||
@ -27,7 +26,7 @@
|
||||
let paused: boolean = true;
|
||||
let launched = false;
|
||||
let prepared: string[] = [];
|
||||
let originalLyrics: LrcJsonData;
|
||||
let originalLyrics: LyricData;
|
||||
let lyricsText: string[] = [];
|
||||
let hasLyrics: boolean;
|
||||
const coverPath = writable('');
|
||||
@ -139,12 +138,13 @@
|
||||
f.text().then((lr) => {
|
||||
if (f.name.endsWith('.ttml')) {
|
||||
originalLyrics = parseTTML(lr);
|
||||
console.log(originalLyrics);
|
||||
for (const line of originalLyrics.scripts!) {
|
||||
lyricsText.push(line.text);
|
||||
}
|
||||
hasLyrics = true;
|
||||
} else if (f.name.endsWith('.lrc')) {
|
||||
originalLyrics = lrcParser(lr);
|
||||
originalLyrics = parseLRC(lr);
|
||||
if (!originalLyrics.scripts) return;
|
||||
for (const line of originalLyrics.scripts) {
|
||||
lyricsText.push(line.text);
|
||||
|
Loading…
Reference in New Issue
Block a user