plugin: Synced Lyrics (#2207)

* Added Plugin File

* Added Logic

* Known issue

* Finished Backend part

* Before cleanup

* Added Style
Removed log

* Fixed time and visibility issues

* Changed lyrics style

* Changed way lyrics are selected

* Fix

* Added style lyrics options

* Cleanup

* Fix lyrics styling
Changed how lyrics status are changed

* Moved code to make file more readable

* Change Tab Size

* Fixed issue with overlapping lyrics

* Removed debug console.log

* Added style adaptation for music videos

* Changed file indent

* Revered back to original pnpm file

* Removed unnecessary option

* Fix lyrics status bug
Removed leftover logs

* Started to implement fetching for genius lyrics

* feat(synced-lyrics): add `addedVersion` field

* Made changes according to feedbacks

* fix: add a delay of 300ms to the current time

- Since the transition takes 300ms, we need to add a delay of 300ms to the current time

* Removed test about genius.com scraping

* Removed 300ms delay

* chore: cleaned up the code

* Specified path and variable

* chore: always enable lyrics tab

* chore: use SolidJS to render the lyrics

* chore: remove useless signal

* chore: feature-parity with original PR (+some nice stuff)

* recreate lock file

* show json decode error

* feat(synced-lyrics): improve ui
- Change type assertion code
- Replace span to `yt-formatted-string`
- Add refetch button

* chore: make the lyric styling a solidjs effect

* feat: i18n

* chore: apply suggestion

---------

Co-authored-by: Su-Yong <simssy2205@gmail.com>
Co-authored-by: JellyBrick <shlee1503@naver.com>
Co-authored-by: Angelos Bouklis <53124886+ArjixWasTaken@users.noreply.github.com>
This commit is contained in:
No NOréo
2024-07-31 12:54:21 +02:00
committed by GitHub
parent 116dbad9bc
commit 8ce91b143a
19 changed files with 977 additions and 26 deletions

View File

@ -0,0 +1,90 @@
/* eslint-disable prefer-const, @typescript-eslint/no-unused-vars */
import { createRenderer } from '@/utils';
import { SongInfo } from '@/providers/song-info';
import { YoutubePlayer } from '@/types/youtube-player';
import { makeLyricsRequest } from './lyrics';
import { selectors, tabStates } from './utils';
import { setConfig } from './renderer';
import { setCurrentTime } from './components/LyricsContainer';
import type { SyncedLyricsPluginConfig } from '../types';
export let _ytAPI: YoutubePlayer | null = null;
export const renderer = createRenderer({
onConfigChange(newConfig) {
setConfig(newConfig as SyncedLyricsPluginConfig);
},
observerCallback(mutations: MutationRecord[]) {
for (const mutation of mutations) {
const header = mutation.target as HTMLElement;
switch (mutation.attributeName) {
case 'disabled':
header.removeAttribute('disabled');
break;
case 'aria-selected':
tabStates[header.ariaSelected as 'true' | 'false']?.(
_ytAPI?.getVideoData(),
);
break;
}
}
},
onPlayerApiReady(api) {
_ytAPI = api;
// @ts-expect-error type is 'unknown', so TS complains
api.addEventListener('videodatachange', this.videoDataChange);
// @ts-expect-error type is 'unknown', so TS complains
this.videoDataChange();
},
hasAddedEvents: false,
observer: null as MutationObserver | null,
videoDataChange() {
if (!this.hasAddedEvents) {
const video = document.querySelector('video');
// @ts-expect-error type is 'unknown', so TS complains
video?.addEventListener('timeupdate', this.progressCallback);
if (video) this.hasAddedEvents = true;
}
const header = document.querySelector<HTMLElement>(selectors.head);
if (!header) return;
this.observer ??= new MutationObserver(
this.observerCallback as MutationCallback,
);
// Force the lyrics tab to be enabled at all times.
this.observer.disconnect();
this.observer.observe(header, { attributes: true });
header.removeAttribute('disabled');
},
progressCallback(evt: Event) {
switch (evt.type) {
case 'timeupdate': {
const video = evt.target as HTMLVideoElement;
setCurrentTime(video.currentTime * 1000);
break;
}
}
},
async start({ getConfig, ipc: { on } }) {
setConfig((await getConfig()) as SyncedLyricsPluginConfig);
on('ytmd:update-song-info', async (info: SongInfo) => {
await makeLyricsRequest(info);
});
},
});