diff --git a/src/config/store.ts b/src/config/store.ts index 3f6513ab..cc594083 100644 --- a/src/config/store.ts +++ b/src/config/store.ts @@ -4,10 +4,33 @@ import defaults from './defaults'; import { DefaultPresetList, type Preset } from '@/plugins/downloader/types'; -// prettier-ignore -export type IStore = InstanceType>>; +import type { SyncedLyricsPluginConfig } from '@/plugins/synced-lyrics/types'; + +export type IStore = InstanceType< + typeof import('conf').default> +>; const migrations = { + '>=3.4.0'(store: IStore) { + const lyricGeniusConfig = store.get('plugins.lyrics-genius') as + | { + enabled?: boolean; + romanizedLyrics?: boolean; + } + | undefined; + + if (lyricGeniusConfig) { + const syncedLyricsConfig = store.get('plugins.synced-lyrics') as + | SyncedLyricsPluginConfig + | undefined; + store.set('plugins.synced-lyrics', { + ...syncedLyricsConfig, + enabled: lyricGeniusConfig.enabled, + }); + + store.delete('plugins.lyrics-genius'); + } + }, '>=3.3.0'(store: IStore) { const lastfmConfig = store.get('plugins.lastfm') as { enabled?: boolean; diff --git a/src/plugins/downloader/main/index.ts b/src/plugins/downloader/main/index.ts index d651daf4..db5f92df 100644 --- a/src/plugins/downloader/main/index.ts +++ b/src/plugins/downloader/main/index.ts @@ -19,8 +19,6 @@ import { sendFeedback as sendFeedback_, setBadge, } from './utils'; -import { fetchFromGenius } from '@/plugins/lyrics-genius/main'; -import { isEnabled } from '@/config/plugins'; import registerCallback, { cleanupName, getImage, @@ -598,16 +596,6 @@ async function writeID3( }; } - if (isEnabled('lyrics-genius')) { - const lyrics = await fetchFromGenius(metadata); - if (lyrics) { - tags.unsynchronisedLyrics = { - language: '', - text: lyrics, - }; - } - } - if (metadata.trackId) { tags.trackNumber = metadata.trackId; } diff --git a/src/plugins/lyrics-genius/index.ts b/src/plugins/lyrics-genius/index.ts deleted file mode 100644 index af9190c4..00000000 --- a/src/plugins/lyrics-genius/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -import style from './style.css?inline'; -import { createPlugin } from '@/utils'; -import { onConfigChange, onMainLoad } from './main'; -import { onRendererLoad } from './renderer'; -import { t } from '@/i18n'; - -export type LyricsGeniusPluginConfig = { - enabled: boolean; - romanizedLyrics: boolean; -}; - -export default createPlugin({ - name: () => t('plugins.lyrics-genius.name'), - description: () => t('plugins.lyrics-genius.description'), - restartNeeded: true, - config: { - enabled: false, - romanizedLyrics: false, - } as LyricsGeniusPluginConfig, - stylesheets: [style], - async menu({ getConfig, setConfig }) { - const config = await getConfig(); - - return [ - { - label: t('plugins.lyrics-genius.menu.romanized-lyrics'), - type: 'checkbox', - checked: config.romanizedLyrics, - click(item) { - setConfig({ - romanizedLyrics: item.checked, - }); - }, - }, - ]; - }, - - backend: { - start: onMainLoad, - onConfigChange, - }, - renderer: onRendererLoad, -}); diff --git a/src/plugins/lyrics-genius/main.ts b/src/plugins/lyrics-genius/main.ts deleted file mode 100644 index fd40c765..00000000 --- a/src/plugins/lyrics-genius/main.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { net } from 'electron'; -import is from 'electron-is'; -import { convert } from 'html-to-text'; - -import { GetGeniusLyric } from './types'; -import { cleanupName, type SongInfo } from '@/providers/song-info'; - -import type { LyricsGeniusPluginConfig } from './index'; - -import type { BackendContext } from '@/types/contexts'; - -const eastAsianChars = - /\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u; -let revRomanized = false; - -export const onMainLoad = async ({ - ipc, - getConfig, -}: BackendContext) => { - const config = await getConfig(); - - if (config.romanizedLyrics) { - revRomanized = true; - } - - ipc.handle('search-genius-lyrics', async (extractedSongInfo: SongInfo) => { - const metadata = extractedSongInfo; - return await fetchFromGenius(metadata); - }); -}; - -export const onConfigChange = (newConfig: LyricsGeniusPluginConfig) => { - revRomanized = newConfig.romanizedLyrics; -}; - -export const fetchFromGenius = async (metadata: SongInfo) => { - const songTitle = `${cleanupName(metadata.title)}`; - const songArtist = `${cleanupName(metadata.artist)}`; - let lyrics: string | null; - - /* Uses Regex to test the title and artist first for said characters if romanization is enabled. Otherwise, normal - Genius Lyrics behavior is observed. - */ - let hasAsianChars = false; - if ( - revRomanized && - (eastAsianChars.test(songTitle) || eastAsianChars.test(songArtist)) - ) { - lyrics = await getLyricsList(`${songArtist} ${songTitle} Romanized`); - hasAsianChars = true; - } else { - lyrics = await getLyricsList(`${songArtist} ${songTitle}`); - } - - /* If the romanization toggle is on, and we did not detect any characters in the title or artist, we do a check - for characters in the lyrics themselves. If this check proves true, we search for Romanized lyrics. - */ - if (revRomanized && !hasAsianChars && lyrics && eastAsianChars.test(lyrics)) { - lyrics = await getLyricsList(`${songArtist} ${songTitle} Romanized`); - } - - return lyrics; -}; - -/** - * Fetches a JSON of songs which is then parsed and passed into getLyrics to get the lyrical content of the first song - * @param {*} queryString - * @returns The lyrics of the first song found using the Genius-Lyrics API - */ -const getLyricsList = async (queryString: string): Promise => { - const response = await net.fetch( - `https://genius.com/api/search/multi?per_page=5&q=${encodeURIComponent( - queryString, - )}`, - ); - if (!response.ok) { - return null; - } - - /* Fetch the first URL with the api, giving a collection of song results. - Pick the first song, parsing the json given by the API. - */ - const info = (await response.json()) as GetGeniusLyric; - const url = info?.response?.sections?.find( - (section) => section.type === 'song', - )?.hits[0]?.result?.url; - - if (url) { - return await getLyrics(url); - } else { - return null; - } -}; - -/** - * - * @param {*} url - * @returns The lyrics of the song URL provided, null if none - */ -const getLyrics = async (url: string): Promise => { - const response = await net.fetch(url); - if (!response.ok) { - return null; - } - - if (is.dev()) { - console.log('Fetching lyrics from Genius:', url); - } - - const html = await response.text(); - return convert(html, { - baseElements: { - selectors: ['[class^="Lyrics__Container"]', '.lyrics'], - }, - selectors: [ - { - selector: 'a', - format: 'linkFormatter', - }, - ], - formatters: { - // Remove links by keeping only the content - linkFormatter(element, walk, builder) { - walk(element.children, builder); - }, - }, - }); -}; diff --git a/src/plugins/lyrics-genius/renderer.ts b/src/plugins/lyrics-genius/renderer.ts deleted file mode 100644 index bb710702..00000000 --- a/src/plugins/lyrics-genius/renderer.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { LoggerPrefix } from '@/utils'; - -import { t } from '@/i18n'; - -import { defaultTrustedTypePolicy } from '@/utils/trusted-types'; - -import type { SongInfo } from '@/providers/song-info'; -import type { RendererContext } from '@/types/contexts'; -import type { LyricsGeniusPluginConfig } from '@/plugins/lyrics-genius/index'; - -export const onRendererLoad = ({ - ipc: { invoke, on }, -}: RendererContext) => { - const setLyrics = (lyricsContainer: Element, lyrics: string | null) => { - const targetHtml = ` -
- ${ - lyrics?.replaceAll(/\r\n|\r|\n/g, '
') ?? - 'Could not retrieve lyrics from genius' - } -
- - - `; - (lyricsContainer.innerHTML as string | TrustedHTML) = - defaultTrustedTypePolicy - ? defaultTrustedTypePolicy.createHTML(targetHtml) - : targetHtml; - - if (lyrics) { - const footer = lyricsContainer.querySelector('.footer'); - - if (footer) { - footer.textContent = 'Source: Genius'; - } - } - }; - - let unregister: (() => void) | null = null; - - on('ytmd:update-song-info', (extractedSongInfo: SongInfo) => { - unregister?.(); - - setTimeout(async () => { - const tabList = document.querySelectorAll('tp-yt-paper-tab'); - const tabs = { - upNext: tabList[0], - lyrics: tabList[1], - discover: tabList[2], - }; - - // Check if disabled - if (!tabs.lyrics?.hasAttribute('disabled')) return; - - const lyrics = (await invoke( - 'search-genius-lyrics', - extractedSongInfo, - )) as string | null; - - if (!lyrics) { - // Delete previous lyrics if tab is open and couldn't get new lyrics - tabs.upNext.click(); - - return; - } - - if (window.electronIs.dev()) { - console.log( - LoggerPrefix, - t('plugins.lyric-genius.renderer.fetched-lyrics'), - ); - } - - const tryToInjectLyric = (callback?: () => void) => { - const lyricsContainer = document.querySelector( - '[page-type="MUSIC_PAGE_TYPE_TRACK_LYRICS"] > ytmusic-message-renderer', - ); - - if (lyricsContainer) { - callback?.(); - - setLyrics(lyricsContainer, lyrics); - applyLyricsTabState(); - } - }; - - const applyLyricsTabState = () => { - if (lyrics) { - tabs.lyrics.removeAttribute('disabled'); - tabs.lyrics.removeAttribute('aria-disabled'); - } else { - tabs.lyrics.setAttribute('disabled', ''); - tabs.lyrics.setAttribute('aria-disabled', ''); - } - }; - - const lyricsTabHandler = () => { - const tabContainer = document.querySelector('ytmusic-tab-renderer'); - if (!tabContainer) return; - - const observer = new MutationObserver((_, observer) => { - tryToInjectLyric(() => observer.disconnect()); - }); - - observer.observe(tabContainer, { - attributes: true, - childList: true, - subtree: true, - }); - }; - - applyLyricsTabState(); - - tabs.discover.addEventListener('click', applyLyricsTabState); - tabs.lyrics.addEventListener('click', lyricsTabHandler); - tabs.upNext.addEventListener('click', applyLyricsTabState); - - tryToInjectLyric(); - - unregister = () => { - tabs.discover.removeEventListener('click', applyLyricsTabState); - tabs.lyrics.removeEventListener('click', lyricsTabHandler); - tabs.upNext.removeEventListener('click', applyLyricsTabState); - }; - }, 500); - }); -}; diff --git a/src/plugins/lyrics-genius/style.css b/src/plugins/lyrics-genius/style.css deleted file mode 100644 index 65412b96..00000000 --- a/src/plugins/lyrics-genius/style.css +++ /dev/null @@ -1,12 +0,0 @@ -/* Disable links in Genius lyrics */ -.genius-lyrics a { - color: var(--ytmusic-text-primary); - display: inline-block; - pointer-events: none; - text-decoration: none; -} - -.description { - font-size: clamp(1.4rem, 1.1vmax, 3rem) !important; - text-align: center !important; -} diff --git a/src/plugins/lyrics-genius/types.ts b/src/plugins/lyrics-genius/types.ts deleted file mode 100644 index 01cfd47b..00000000 --- a/src/plugins/lyrics-genius/types.ts +++ /dev/null @@ -1,117 +0,0 @@ -export interface GetGeniusLyric { - meta: Meta; - response: Response; -} - -export interface Meta { - status: number; -} - -export interface Response { - sections: Section[]; -} - -export interface Section { - type: string; - hits: Hit[]; -} - -export interface Hit { - highlights: Highlight[]; - index: ResultType; - type: ResultType; - result: Result; -} - -export interface Highlight { - property: string; - value: string; - snippet: boolean; - ranges: Range[]; -} - -export interface Range { - start: number; - end: number; -} - -export type ResultType = 'song' | 'album' | 'lyric'; - -export interface Result { - _type: ResultType; - annotation_count?: number; - api_path: string; - artist_names?: string; - full_title: string; - header_image_thumbnail_url?: string; - header_image_url?: string; - id: number; - instrumental?: boolean; - lyrics_owner_id?: number; - lyrics_state?: LyricsState; - lyrics_updated_at?: number; - path?: string; - pyongs_count?: number | null; - relationships_index_url?: string; - release_date_components: ReleaseDateComponents; - release_date_for_display: string; - release_date_with_abbreviated_month_for_display?: string; - song_art_image_thumbnail_url?: string; - song_art_image_url?: string; - stats?: Stats; - title?: string; - title_with_featured?: string; - updated_by_human_at?: number; - url: string; - featured_artists?: string[]; - primary_artist?: Artist; - cover_art_thumbnail_url?: string; - cover_art_url?: string; - name?: string; - name_with_artist?: string; - artist?: Artist; -} - -export interface Artist { - _type: Type; - api_path: string; - header_image_url: string; - id: number; - image_url: string; - index_character: IndexCharacter; - is_meme_verified: boolean; - is_verified: boolean; - name: string; - slug: string; - url: string; - iq?: number; -} - -// TODO: Add more types -export enum Type { - Artist = 'artist', -} - -// TODO: Add more index characters -export enum IndexCharacter { - G = 'g', - Y = 'y', -} - -// TODO: Add more states -export enum LyricsState { - Complete = 'complete', -} - -export interface ReleaseDateComponents { - year: number; - month: number; - day: number | null; -} - -export interface Stats { - unreviewed_annotations: number; - concurrents?: number; - hot: boolean; - pageviews?: number; -}