Update fetchers to support search
This commit is contained in:
parent
01608fa875
commit
f8ecb3fc53
8 changed files with 69 additions and 38 deletions
|
@ -5,7 +5,7 @@ import type {
|
||||||
InternetProviderLyricSearchResponse,
|
InternetProviderLyricSearchResponse,
|
||||||
LyricSearchQuery,
|
LyricSearchQuery,
|
||||||
} from '/@/renderer/api/types';
|
} from '/@/renderer/api/types';
|
||||||
import { LyricSource } from '../../../../renderer/types';
|
import { LyricSource } from '../../../../renderer/api/types';
|
||||||
|
|
||||||
const SEARCH_URL = 'https://genius.com/api/search/song';
|
const SEARCH_URL = 'https://genius.com/api/search/song';
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ async function getSongURL(params: LyricSearchQuery): Promise<GeniusResponse | un
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLyricsFromGenius(url: string): Promise<string | null> {
|
export async function getLyricsByURL(url: string): Promise<string | null> {
|
||||||
let result: AxiosResponse<string, any>;
|
let result: AxiosResponse<string, any>;
|
||||||
try {
|
try {
|
||||||
result = await axios.get<string>(url, { responseType: 'text' });
|
result = await axios.get<string>(url, { responseType: 'text' });
|
||||||
|
@ -147,7 +147,7 @@ export async function query(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lyrics = await getLyricsFromGenius(response.url);
|
const lyrics = await getLyricsByURL(response.url);
|
||||||
if (!lyrics) {
|
if (!lyrics) {
|
||||||
console.error('Could not get lyrics on Genius!');
|
console.error('Could not get lyrics on Genius!');
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -3,10 +3,19 @@ import {
|
||||||
InternetProviderLyricSearchResponse,
|
InternetProviderLyricSearchResponse,
|
||||||
LyricSearchQuery,
|
LyricSearchQuery,
|
||||||
QueueSong,
|
QueueSong,
|
||||||
|
LyricGetQuery,
|
||||||
|
LyricSource,
|
||||||
} from '/@/renderer/api/types';
|
} from '/@/renderer/api/types';
|
||||||
import { query as queryGenius, getSearchResults as searchGenius } from './genius';
|
import {
|
||||||
import { query as queryNetease, getSearchResults as searchNetease } from './netease';
|
query as queryGenius,
|
||||||
import { LyricSource } from '../../../../renderer/types';
|
getSearchResults as searchGenius,
|
||||||
|
getLyricsByURL as getGenius,
|
||||||
|
} from './genius';
|
||||||
|
import {
|
||||||
|
query as queryNetease,
|
||||||
|
getSearchResults as searchNetease,
|
||||||
|
getLyricsBySongId as getNetease,
|
||||||
|
} from './netease';
|
||||||
import { ipcMain } from 'electron';
|
import { ipcMain } from 'electron';
|
||||||
import { store } from '../settings/index';
|
import { store } from '../settings/index';
|
||||||
|
|
||||||
|
@ -14,6 +23,7 @@ type SongFetcher = (params: LyricSearchQuery) => Promise<InternetProviderLyricRe
|
||||||
type SearchFetcher = (
|
type SearchFetcher = (
|
||||||
params: LyricSearchQuery,
|
params: LyricSearchQuery,
|
||||||
) => Promise<InternetProviderLyricSearchResponse[] | null>;
|
) => Promise<InternetProviderLyricSearchResponse[] | null>;
|
||||||
|
type GetFetcher = (id: string) => Promise<string | null>;
|
||||||
|
|
||||||
type CachedLyrics = Record<LyricSource, InternetProviderLyricResponse>;
|
type CachedLyrics = Record<LyricSource, InternetProviderLyricResponse>;
|
||||||
|
|
||||||
|
@ -27,6 +37,11 @@ const SEARCH_FETCHERS: Record<LyricSource, SearchFetcher> = {
|
||||||
[LyricSource.NETEASE]: searchNetease,
|
[LyricSource.NETEASE]: searchNetease,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const GET_FETCHERS: Record<LyricSource, GetFetcher> = {
|
||||||
|
[LyricSource.GENIUS]: getGenius,
|
||||||
|
[LyricSource.NETEASE]: getNetease,
|
||||||
|
};
|
||||||
|
|
||||||
const MAX_CACHED_ITEMS = 10;
|
const MAX_CACHED_ITEMS = 10;
|
||||||
|
|
||||||
const lyricCache = new Map<string, CachedLyrics>();
|
const lyricCache = new Map<string, CachedLyrics>();
|
||||||
|
@ -93,7 +108,18 @@ const searchRemoteLyrics = async (params: LyricSearchQuery) => {
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
ipcMain.handle('lyric-fetch-manual', async (_event, song: QueueSong) => {
|
const getRemoteLyricsById = async (params: LyricGetQuery): Promise<string | null> => {
|
||||||
|
const { remoteSongId, remoteSource } = params;
|
||||||
|
const response = await GET_FETCHERS[remoteSource](remoteSongId);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
ipcMain.handle('lyric-by-song', async (_event, song: QueueSong) => {
|
||||||
const lyric = await getRemoteLyrics(song);
|
const lyric = await getRemoteLyrics(song);
|
||||||
return lyric;
|
return lyric;
|
||||||
});
|
});
|
||||||
|
@ -102,3 +128,8 @@ ipcMain.handle('lyric-search', async (_event, params: LyricSearchQuery) => {
|
||||||
const lyricResults = await searchRemoteLyrics(params);
|
const lyricResults = await searchRemoteLyrics(params);
|
||||||
return lyricResults;
|
return lyricResults;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.handle('lyric-by-remote-id', async (_event, params: LyricGetQuery) => {
|
||||||
|
const lyricResults = await getRemoteLyricsById(params);
|
||||||
|
return lyricResults;
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import axios, { AxiosResponse } from 'axios';
|
import axios, { AxiosResponse } from 'axios';
|
||||||
import { LyricSource } from '../../../../renderer/types';
|
import { LyricSource } from '../../../../renderer/api/types';
|
||||||
import type {
|
import type {
|
||||||
InternetProviderLyricResponse,
|
InternetProviderLyricResponse,
|
||||||
InternetProviderLyricSearchResponse,
|
InternetProviderLyricSearchResponse,
|
||||||
|
@ -64,7 +64,7 @@ async function getSongId(params: LyricSearchQuery): Promise<NetEaseResponse | un
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getLyricsFromSongId(songId: string): Promise<string | undefined> {
|
export async function getLyricsBySongId(songId: string): Promise<string | null> {
|
||||||
let result: AxiosResponse<any, any>;
|
let result: AxiosResponse<any, any>;
|
||||||
try {
|
try {
|
||||||
result = await axios.get(LYRICS_URL, {
|
result = await axios.get(LYRICS_URL, {
|
||||||
|
@ -76,7 +76,7 @@ async function getLyricsFromSongId(songId: string): Promise<string | undefined>
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('NetEase lyrics request got an error!', e);
|
console.error('NetEase lyrics request got an error!', e);
|
||||||
return undefined;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.data.klyric?.lyric || result.data.lrc?.lyric;
|
return result.data.klyric?.lyric || result.data.lrc?.lyric;
|
||||||
|
@ -91,7 +91,7 @@ export async function query(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lyrics = await getLyricsFromSongId(response.id);
|
const lyrics = await getLyricsBySongId(response.id);
|
||||||
if (!lyrics) {
|
if (!lyrics) {
|
||||||
console.error('Could not get lyrics on NetEase!');
|
console.error('Could not get lyrics on NetEase!');
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { IpcRendererEvent, ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
import { InternetProviderLyricResponse, LyricSearchQuery, QueueSong } from '/@/renderer/api/types';
|
import { LyricSearchQuery, QueueSong } from '/@/renderer/api/types';
|
||||||
|
|
||||||
const fetchRemoteLyrics = (song: QueueSong) => {
|
const getRemoteLyricsBySong = (song: QueueSong) => {
|
||||||
const result = ipcRenderer.invoke('lyric-fetch-manual', song);
|
const result = ipcRenderer.invoke('lyric-by-song', song);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,19 +11,13 @@ const searchRemoteLyrics = (params: LyricSearchQuery) => {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const remoteLyricsListener = (
|
const getRemoteLyricsByRemoteId = (id: string) => {
|
||||||
cb: (
|
const result = ipcRenderer.invoke('lyric-by-remote-id', id);
|
||||||
event: IpcRendererEvent,
|
return result;
|
||||||
songName: string,
|
|
||||||
source: string,
|
|
||||||
lyric: InternetProviderLyricResponse,
|
|
||||||
) => void,
|
|
||||||
) => {
|
|
||||||
ipcRenderer.on('lyric-get', cb);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const lyrics = {
|
export const lyrics = {
|
||||||
fetchRemoteLyrics,
|
getRemoteLyricsByRemoteId,
|
||||||
remoteLyricsListener,
|
getRemoteLyricsBySong,
|
||||||
searchRemoteLyrics,
|
searchRemoteLyrics,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1033,14 +1033,14 @@ export type InternetProviderLyricResponse = {
|
||||||
artist: string;
|
artist: string;
|
||||||
lyrics: string;
|
lyrics: string;
|
||||||
name: string;
|
name: string;
|
||||||
source: string;
|
source: LyricSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InternetProviderLyricSearchResponse = {
|
export type InternetProviderLyricSearchResponse = {
|
||||||
artist: string;
|
artist: string;
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
source: string;
|
source: LyricSource;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SynchronizedLyricMetadata = {
|
export type SynchronizedLyricMetadata = {
|
||||||
|
@ -1048,12 +1048,12 @@ export type SynchronizedLyricMetadata = {
|
||||||
remote: boolean;
|
remote: boolean;
|
||||||
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
export type UnsyhchronizedLyricMetadata = {
|
export type UnsynchronizedLyricMetadata = {
|
||||||
lyrics: string;
|
lyrics: string;
|
||||||
remote: boolean;
|
remote: boolean;
|
||||||
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
} & Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
export type FullLyricsMetadata = SynchronizedLyricMetadata | UnsyhchronizedLyricMetadata;
|
export type FullLyricsMetadata = SynchronizedLyricMetadata | UnsynchronizedLyricMetadata;
|
||||||
|
|
||||||
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
export type LyricOverride = Omit<InternetProviderLyricResponse, 'lyrics'>;
|
||||||
|
|
||||||
|
@ -1065,3 +1065,15 @@ export type LyricSearchQuery = {
|
||||||
artist: string;
|
artist: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type LyricGetQuery = {
|
||||||
|
remoteSongId: string;
|
||||||
|
remoteSource: LyricSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum LyricSource {
|
||||||
|
GENIUS = 'Genius',
|
||||||
|
NETEASE = 'NetEase',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LyricsOverride = Omit<FullLyricsMetadata, 'lyrics'> & { id: string };
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useLyricsSettings, useSettingsStoreActions } from '/@/renderer/store';
|
||||||
import { MultiSelect, MultiSelectProps, NumberInput, Switch } from '/@/renderer/components';
|
import { MultiSelect, MultiSelectProps, NumberInput, Switch } from '/@/renderer/components';
|
||||||
import isElectron from 'is-electron';
|
import isElectron from 'is-electron';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { LyricSource } from '/@/renderer/types';
|
import { LyricSource } from '/@/renderer/api/types';
|
||||||
|
|
||||||
const localSettings = isElectron() ? window.electron.localSettings : null;
|
const localSettings = isElectron() ? window.electron.localSettings : null;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { create } from 'zustand';
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { LibraryItem } from '/@/renderer/api/types';
|
import { LibraryItem, LyricSource } from '/@/renderer/api/types';
|
||||||
import { AppRoute } from '/@/renderer/router/routes';
|
import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { AppTheme } from '/@/renderer/themes/types';
|
import { AppTheme } from '/@/renderer/themes/types';
|
||||||
import {
|
import {
|
||||||
|
@ -19,7 +19,6 @@ import {
|
||||||
PlaybackType,
|
PlaybackType,
|
||||||
TableType,
|
TableType,
|
||||||
Platform,
|
Platform,
|
||||||
LyricSource,
|
|
||||||
} from '/@/renderer/types';
|
} from '/@/renderer/types';
|
||||||
|
|
||||||
export type SidebarItemType = {
|
export type SidebarItemType = {
|
||||||
|
|
|
@ -176,8 +176,3 @@ export type GridCardData = {
|
||||||
playButtonBehavior: Play;
|
playButtonBehavior: Play;
|
||||||
route: CardRoute;
|
route: CardRoute;
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum LyricSource {
|
|
||||||
GENIUS = 'Genius',
|
|
||||||
NETEASE = 'NetEase',
|
|
||||||
}
|
|
||||||
|
|
Reference in a new issue