feat: migration to TypeScript part 3

Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
JellyBrick
2023-09-03 21:02:57 +09:00
parent 03c1ab0e98
commit 278618bc83
25 changed files with 674 additions and 380 deletions

View File

@ -1,36 +1,43 @@
const { join } = require('node:path');
import { join } from 'node:path';
const { ipcMain, net } = require('electron');
const is = require('electron-is');
const { convert } = require('html-to-text');
import { BrowserWindow, ipcMain, net } from 'electron';
import is from 'electron-is';
import { convert } from 'html-to-text';
const { cleanupName } = require('../../providers/song-info');
const { injectCSS } = require('../utils');
import { GetGeniusLyric } from './types';
import { cleanupName, SongInfo } from '../../providers/song-info';
import { injectCSS } from '../utils';
import config from '../../config';
const eastAsianChars = /\p{Script=Katakana}|\p{Script=Hiragana}|\p{Script=Hangul}|\p{Script=Han}/u;
let revRomanized = false;
module.exports = async (win, options) => {
const LyricGeniusTypeObj = config.get('plugins.lyric-genius');
export type LyricGeniusType = typeof LyricGeniusTypeObj;
export default (win: BrowserWindow, options: LyricGeniusType) => {
if (options.romanizedLyrics) {
revRomanized = true;
}
injectCSS(win.webContents, join(__dirname, 'style.css'));
ipcMain.on('search-genius-lyrics', async (event, extractedSongInfo) => {
const metadata = JSON.parse(extractedSongInfo);
event.returnValue = await fetchFromGenius(metadata);
ipcMain.handle('search-genius-lyrics', async (_, extractedSongInfo: string) => {
const metadata = JSON.parse(extractedSongInfo) as SongInfo;
return await fetchFromGenius(metadata);
});
};
const toggleRomanized = () => {
export const toggleRomanized = () => {
revRomanized = !revRomanized;
};
const fetchFromGenius = async (metadata) => {
export const fetchFromGenius = async (metadata: SongInfo) => {
const songTitle = `${cleanupName(metadata.title)}`;
const songArtist = `${cleanupName(metadata.artist)}`;
let lyrics;
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.
@ -46,7 +53,7 @@ const fetchFromGenius = async (metadata) => {
/* 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 && eastAsianChars.test(lyrics)) {
if (revRomanized && !hasAsianChars && lyrics && eastAsianChars.test(lyrics)) {
lyrics = await getLyricsList(`${songArtist} ${songTitle} Romanized`);
}
@ -58,7 +65,7 @@ const fetchFromGenius = async (metadata) => {
* @param {*} queryString
* @returns The lyrics of the first song found using the Genius-Lyrics API
*/
const getLyricsList = async (queryString) => {
const getLyricsList = async (queryString: string): Promise<string | null> => {
const response = await net.fetch(
`https://genius.com/api/search/multi?per_page=5&q=${encodeURIComponent(queryString)}`,
);
@ -69,17 +76,17 @@ const getLyricsList = async (queryString) => {
/* 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();
let url = '';
try {
url = info.response.sections.find((section) => section.type === 'song')
.hits[0].result.url;
} catch {
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;
}
const lyrics = await getLyrics(url);
return lyrics;
};
/**
@ -87,7 +94,7 @@ const getLyricsList = async (queryString) => {
* @param {*} url
* @returns The lyrics of the song URL provided, null if none
*/
const getLyrics = async (url) => {
const getLyrics = async (url: string): Promise<string | null> => {
const response = await net.fetch(url);
if (!response.ok) {
return null;
@ -116,6 +123,3 @@ const getLyrics = async (url) => {
},
});
};
module.exports.toggleRomanized = toggleRomanized;
module.exports.fetchFromGenius = fetchFromGenius;

View File

@ -1,8 +1,8 @@
const { ipcRenderer } = require('electron');
const is = require('electron-is');
import { ipcRenderer } from 'electron';
import is from 'electron-is';
module.exports = () => {
ipcRenderer.on('update-song-info', (_, extractedSongInfo) => setTimeout(() => {
export default () => {
ipcRenderer.on('update-song-info', (_, extractedSongInfo: string) => setTimeout(async () => {
const tabList = document.querySelectorAll('tp-yt-paper-tab');
const tabs = {
upNext: tabList[0],
@ -17,10 +17,10 @@ module.exports = () => {
let hasLyrics = true;
const lyrics = ipcRenderer.sendSync(
const lyrics = await ipcRenderer.invoke(
'search-genius-lyrics',
extractedSongInfo,
);
) as string;
if (!lyrics) {
// Delete previous lyrics if tab is open and couldn't get new lyrics
checkLyricsContainer(() => {
@ -40,17 +40,21 @@ module.exports = () => {
checkLyricsContainer();
tabs.lyrics.addEventListener('click', () => {
const lyricsTabHandler = () => {
const tabContainer = document.querySelector('ytmusic-tab-renderer');
const observer = new MutationObserver((_, observer) => {
checkLyricsContainer(() => observer.disconnect());
});
observer.observe(tabContainer, {
attributes: true,
childList: true,
subtree: true,
});
});
if (tabContainer) {
const observer = new MutationObserver((_, observer) => {
checkLyricsContainer(() => observer.disconnect());
});
observer.observe(tabContainer, {
attributes: true,
childList: true,
subtree: true,
});
}
};
tabs.lyrics.addEventListener('click', lyricsTabHandler);
function checkLyricsContainer(callback = () => {
}) {
@ -63,7 +67,7 @@ module.exports = () => {
}
}
function setLyrics(lyricsContainer) {
function setLyrics(lyricsContainer: Element) {
lyricsContainer.innerHTML = `<div id="contents" class="style-scope ytmusic-section-list-renderer description ytmusic-description-shelf-renderer genius-lyrics">
${
hasLyrics
@ -74,15 +78,20 @@ module.exports = () => {
</div>
<yt-formatted-string class="footer style-scope ytmusic-description-shelf-renderer" style="align-self: baseline"></yt-formatted-string>`;
if (hasLyrics) {
lyricsContainer.querySelector('.footer').textContent = 'Source: Genius';
enableLyricsTab();
const footer = lyricsContainer.querySelector('.footer');
if (footer) {
footer.textContent = 'Source: Genius';
enableLyricsTab();
}
}
}
function setTabsOnclick(callback) {
const defaultHandler = () => {};
function setTabsOnclick(callback: EventListenerOrEventListenerObject | undefined) {
for (const tab of [tabs.upNext, tabs.discover]) {
if (tab) {
tab.addEventListener('click', callback);
tab.addEventListener('click', callback ?? defaultHandler);
}
}
}

View File

@ -1,16 +0,0 @@
const { toggleRomanized } = require('./back');
const { setOptions } = require('../../config/plugins');
module.exports = (win, options) => [
{
label: 'Romanized Lyrics',
type: 'checkbox',
checked: options.romanizedLyrics,
click(item) {
options.romanizedLyrics = item.checked;
setOptions('lyrics-genius', options);
toggleRomanized();
},
},
];

View File

@ -0,0 +1,18 @@
import { BrowserWindow, MenuItem } from 'electron';
import { LyricGeniusType, toggleRomanized } from './back';
import { setOptions } from '../../config/plugins';
module.exports = (win: BrowserWindow, options: LyricGeniusType) => [
{
label: 'Romanized Lyrics',
type: 'checkbox',
checked: options.romanizedLyrics,
click(item: MenuItem) {
options.romanizedLyrics = item.checked;
setOptions('lyrics-genius', options);
toggleRomanized();
},
},
];

View File

@ -0,0 +1,121 @@
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: Index;
type: Index;
result: Result;
}
export interface Highlight {
property: string;
value: string;
snippet: boolean;
ranges: Range[];
}
export interface Range {
start: number;
end: number;
}
export enum Index {
Album = 'album',
Lyric = 'lyric',
Song = 'song',
}
export interface Result {
_type: Index;
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;
}