Lint all files

This commit is contained in:
jeffvli 2023-07-01 19:10:05 -07:00
parent 22af76b4d6
commit 30e52ebb54
334 changed files with 76519 additions and 75932 deletions

View file

@ -1,10 +1,10 @@
import axios, { AxiosResponse } from 'axios';
import { load } from 'cheerio';
import {
LyricSource,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '../../../../renderer/api/types';
import { orderSearchResults } from './shared';
@ -13,197 +13,197 @@ const SEARCH_URL = 'https://genius.com/api/search/song';
// Adapted from https://github.com/NyaomiDEV/Sunamu/blob/master/src/main/lyricproviders/genius.ts
export interface GeniusResponse {
meta: Meta;
response: Response;
meta: Meta;
response: Response;
}
export interface Meta {
status: number;
status: number;
}
export interface Response {
next_page: number;
sections: Section[];
next_page: number;
sections: Section[];
}
export interface Section {
hits: Hit[];
type: string;
hits: Hit[];
type: string;
}
export interface Hit {
highlights: any[];
index: string;
result: Result;
type: string;
highlights: any[];
index: string;
result: Result;
type: string;
}
export interface Result {
_type: string;
annotation_count: number;
api_path: string;
artist_names: string;
featured_artists: any[];
full_title: string;
header_image_thumbnail_url: string;
header_image_url: string;
id: number;
instrumental: boolean;
language: string;
lyrics_owner_id: number;
lyrics_state: string;
lyrics_updated_at: number;
path: string;
primary_artist: PrimaryArtist;
pyongs_count: 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;
_type: string;
annotation_count: number;
api_path: string;
artist_names: string;
featured_artists: any[];
full_title: string;
header_image_thumbnail_url: string;
header_image_url: string;
id: number;
instrumental: boolean;
language: string;
lyrics_owner_id: number;
lyrics_state: string;
lyrics_updated_at: number;
path: string;
primary_artist: PrimaryArtist;
pyongs_count: 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;
}
export interface PrimaryArtist {
_type: string;
api_path: string;
header_image_url: string;
id: number;
image_url: string;
index_character: string;
is_meme_verified: boolean;
is_verified: boolean;
name: string;
slug: string;
url: string;
_type: string;
api_path: string;
header_image_url: string;
id: number;
image_url: string;
index_character: string;
is_meme_verified: boolean;
is_verified: boolean;
name: string;
slug: string;
url: string;
}
export interface ReleaseDateComponents {
day: number;
month: number;
year: number;
day: number;
month: number;
year: number;
}
export interface Stats {
hot: boolean;
unreviewed_annotations: number;
hot: boolean;
unreviewed_annotations: number;
}
export async function getSearchResults(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<GeniusResponse>;
let result: AxiosResponse<GeniusResponse>;
const searchQuery = [params.artist, params.name].join(' ');
const searchQuery = [params.artist, params.name].join(' ');
if (!searchQuery) {
return null;
}
if (!searchQuery) {
return null;
}
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '5',
q: searchQuery,
},
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '5',
q: searchQuery,
},
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
const rawSongsResult = result.data.response?.sections?.[0]?.hits?.map((hit) => hit.result);
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
return {
artist: song.artist_names,
id: song.url,
name: song.full_title,
source: LyricSource.GENIUS,
};
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
const rawSongsResult = result.data.response?.sections?.[0]?.hits?.map((hit) => hit.result);
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
return {
artist: song.artist_names,
id: song.url,
name: song.full_title,
source: LyricSource.GENIUS,
};
});
return orderSearchResults({ params, results: songResults });
return orderSearchResults({ params, results: songResults });
}
async function getSongId(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<Omit<InternetProviderLyricResponse, 'lyrics'> | null> {
let result: AxiosResponse<GeniusResponse>;
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '1',
q: `${params.artist} ${params.name}`,
},
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
let result: AxiosResponse<GeniusResponse>;
try {
result = await axios.get(SEARCH_URL, {
params: {
per_page: '1',
q: `${params.artist} ${params.name}`,
},
});
} catch (e) {
console.error('Genius search request got an error!', e);
return null;
}
const hit = result.data.response?.sections?.[0]?.hits?.[0]?.result;
const hit = result.data.response?.sections?.[0]?.hits?.[0]?.result;
if (!hit) {
return null;
}
if (!hit) {
return null;
}
return {
artist: hit.artist_names,
id: hit.url,
name: hit.full_title,
source: LyricSource.GENIUS,
};
return {
artist: hit.artist_names,
id: hit.url,
name: hit.full_title,
source: LyricSource.GENIUS,
};
}
export async function getLyricsBySongId(url: string): Promise<string | null> {
let result: AxiosResponse<string, any>;
try {
result = await axios.get<string>(url, { responseType: 'text' });
} catch (e) {
console.error('Genius lyrics request got an error!', e);
return null;
}
let result: AxiosResponse<string, any>;
try {
result = await axios.get<string>(url, { responseType: 'text' });
} catch (e) {
console.error('Genius lyrics request got an error!', e);
return null;
}
const $ = load(result.data.split('<br/>').join('\n'));
const lyricsDiv = $('div.lyrics');
const $ = load(result.data.split('<br/>').join('\n'));
const lyricsDiv = $('div.lyrics');
if (lyricsDiv.length > 0) return lyricsDiv.text().trim();
if (lyricsDiv.length > 0) return lyricsDiv.text().trim();
const lyricSections = $('div[class^=Lyrics__Container]')
.map((_, e) => $(e).text())
.toArray()
.join('\n');
return lyricSections;
const lyricSections = $('div[class^=Lyrics__Container]')
.map((_, e) => $(e).text())
.toArray()
.join('\n');
return lyricSections;
}
export async function query(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
const response = await getSongId(params);
if (!response) {
console.error('Could not find the song on Genius!');
return null;
}
const response = await getSongId(params);
if (!response) {
console.error('Could not find the song on Genius!');
return null;
}
const lyrics = await getLyricsBySongId(response.id);
if (!lyrics) {
console.error('Could not get lyrics on Genius!');
return null;
}
const lyrics = await getLyricsBySongId(response.id);
if (!lyrics) {
console.error('Could not get lyrics on Genius!');
return null;
}
return {
artist: response.artist,
id: response.id,
lyrics,
name: response.name,
source: LyricSource.GENIUS,
};
return {
artist: response.artist,
id: response.id,
lyrics,
name: response.name,
source: LyricSource.GENIUS,
};
}

View file

@ -1,53 +1,53 @@
import { ipcMain } from 'electron';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
QueueSong,
LyricGetQuery,
LyricSource,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
QueueSong,
LyricGetQuery,
LyricSource,
} from '../../../../renderer/api/types';
import { store } from '../settings/index';
import {
query as queryGenius,
getSearchResults as searchGenius,
getLyricsBySongId as getGenius,
query as queryGenius,
getSearchResults as searchGenius,
getLyricsBySongId as getGenius,
} from './genius';
import {
query as queryLrclib,
getSearchResults as searchLrcLib,
getLyricsBySongId as getLrcLib,
query as queryLrclib,
getSearchResults as searchLrcLib,
getLyricsBySongId as getLrcLib,
} from './lrclib';
import {
query as queryNetease,
getSearchResults as searchNetease,
getLyricsBySongId as getNetease,
query as queryNetease,
getSearchResults as searchNetease,
getLyricsBySongId as getNetease,
} from './netease';
type SongFetcher = (params: LyricSearchQuery) => Promise<InternetProviderLyricResponse | null>;
type SearchFetcher = (
params: LyricSearchQuery,
params: LyricSearchQuery,
) => Promise<InternetProviderLyricSearchResponse[] | null>;
type GetFetcher = (id: string) => Promise<string | null>;
type CachedLyrics = Record<LyricSource, InternetProviderLyricResponse>;
const FETCHERS: Record<LyricSource, SongFetcher> = {
[LyricSource.GENIUS]: queryGenius,
[LyricSource.LRCLIB]: queryLrclib,
[LyricSource.NETEASE]: queryNetease,
[LyricSource.GENIUS]: queryGenius,
[LyricSource.LRCLIB]: queryLrclib,
[LyricSource.NETEASE]: queryNetease,
};
const SEARCH_FETCHERS: Record<LyricSource, SearchFetcher> = {
[LyricSource.GENIUS]: searchGenius,
[LyricSource.LRCLIB]: searchLrcLib,
[LyricSource.NETEASE]: searchNetease,
[LyricSource.GENIUS]: searchGenius,
[LyricSource.LRCLIB]: searchLrcLib,
[LyricSource.NETEASE]: searchNetease,
};
const GET_FETCHERS: Record<LyricSource, GetFetcher> = {
[LyricSource.GENIUS]: getGenius,
[LyricSource.LRCLIB]: getLrcLib,
[LyricSource.NETEASE]: getNetease,
[LyricSource.GENIUS]: getGenius,
[LyricSource.LRCLIB]: getLrcLib,
[LyricSource.NETEASE]: getNetease,
};
const MAX_CACHED_ITEMS = 10;
@ -55,95 +55,95 @@ const MAX_CACHED_ITEMS = 10;
const lyricCache = new Map<string, CachedLyrics>();
const getRemoteLyrics = async (song: QueueSong) => {
const sources = store.get('lyrics', []) as LyricSource[];
const sources = store.get('lyrics', []) as LyricSource[];
const cached = lyricCache.get(song.id);
const cached = lyricCache.get(song.id);
if (cached) {
for (const source of sources) {
const data = cached[source];
if (data) return data;
}
}
let lyricsFromSource = null;
if (cached) {
for (const source of sources) {
const data = cached[source];
if (data) return data;
const params = {
album: song.album || song.name,
artist: song.artistName,
duration: song.duration,
name: song.name,
};
const response = await FETCHERS[source](params);
if (response) {
const newResult = cached
? {
...cached,
[source]: response,
}
: ({ [source]: response } as CachedLyrics);
if (lyricCache.size === MAX_CACHED_ITEMS && cached === undefined) {
const toRemove = lyricCache.keys().next().value;
lyricCache.delete(toRemove);
}
lyricCache.set(song.id, newResult);
lyricsFromSource = response;
break;
}
}
}
let lyricsFromSource = null;
for (const source of sources) {
const params = {
album: song.album || song.name,
artist: song.artistName,
duration: song.duration,
name: song.name,
};
const response = await FETCHERS[source](params);
if (response) {
const newResult = cached
? {
...cached,
[source]: response,
}
: ({ [source]: response } as CachedLyrics);
if (lyricCache.size === MAX_CACHED_ITEMS && cached === undefined) {
const toRemove = lyricCache.keys().next().value;
lyricCache.delete(toRemove);
}
lyricCache.set(song.id, newResult);
lyricsFromSource = response;
break;
}
}
return lyricsFromSource;
return lyricsFromSource;
};
const searchRemoteLyrics = async (params: LyricSearchQuery) => {
const sources = store.get('lyrics', []) as LyricSource[];
const sources = store.get('lyrics', []) as LyricSource[];
const results: Record<LyricSource, InternetProviderLyricSearchResponse[]> = {
[LyricSource.GENIUS]: [],
[LyricSource.LRCLIB]: [],
[LyricSource.NETEASE]: [],
};
const results: Record<LyricSource, InternetProviderLyricSearchResponse[]> = {
[LyricSource.GENIUS]: [],
[LyricSource.LRCLIB]: [],
[LyricSource.NETEASE]: [],
};
for (const source of sources) {
const response = await SEARCH_FETCHERS[source](params);
for (const source of sources) {
const response = await SEARCH_FETCHERS[source](params);
if (response) {
response.forEach((result) => {
results[source].push(result);
});
if (response) {
response.forEach((result) => {
results[source].push(result);
});
}
}
}
return results;
return results;
};
const getRemoteLyricsById = async (params: LyricGetQuery): Promise<string | null> => {
const { remoteSongId, remoteSource } = params;
const response = await GET_FETCHERS[remoteSource](remoteSongId);
const { remoteSongId, remoteSource } = params;
const response = await GET_FETCHERS[remoteSource](remoteSongId);
if (!response) {
return null;
}
if (!response) {
return null;
}
return response;
return response;
};
ipcMain.handle('lyric-by-song', async (_event, song: QueueSong) => {
const lyric = await getRemoteLyrics(song);
return lyric;
const lyric = await getRemoteLyrics(song);
return lyric;
});
ipcMain.handle('lyric-search', async (_event, params: LyricSearchQuery) => {
const lyricResults = await searchRemoteLyrics(params);
return lyricResults;
const lyricResults = await searchRemoteLyrics(params);
return lyricResults;
});
ipcMain.handle('lyric-by-remote-id', async (_event, params: LyricGetQuery) => {
const lyricResults = await getRemoteLyricsById(params);
return lyricResults;
const lyricResults = await getRemoteLyricsById(params);
return lyricResults;
});

View file

@ -1,10 +1,10 @@
// Credits to https://github.com/tranxuanthang/lrcget for API implementation
import axios, { AxiosResponse } from 'axios';
import {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
LyricSource,
} from '../../../../renderer/api/types';
import { orderSearchResults } from './shared';
@ -14,106 +14,106 @@ const SEEARCH_URL = 'https://lrclib.net/api/search';
const TIMEOUT_MS = 5000;
export interface LrcLibSearchResponse {
albumName: string;
artistName: string;
id: number;
name: string;
albumName: string;
artistName: string;
id: number;
name: string;
}
export interface LrcLibTrackResponse {
albumName: string;
artistName: string;
duration: number;
id: number;
instrumental: boolean;
isrc: string;
lang: string;
name: string;
plainLyrics: string | null;
releaseDate: string;
spotifyId: string;
syncedLyrics: string | null;
albumName: string;
artistName: string;
duration: number;
id: number;
instrumental: boolean;
isrc: string;
lang: string;
name: string;
plainLyrics: string | null;
releaseDate: string;
spotifyId: string;
syncedLyrics: string | null;
}
export async function getSearchResults(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<LrcLibSearchResponse[]>;
let result: AxiosResponse<LrcLibSearchResponse[]>;
if (!params.name) {
return null;
}
if (!params.name) {
return null;
}
try {
result = await axios.get<LrcLibSearchResponse[]>(SEEARCH_URL, {
params: {
q: params.name,
},
try {
result = await axios.get<LrcLibSearchResponse[]>(SEEARCH_URL, {
params: {
q: params.name,
},
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
if (!result.data) return null;
const songResults: InternetProviderLyricSearchResponse[] = result.data.map((song) => {
return {
artist: song.artistName,
id: String(song.id),
name: song.name,
source: LyricSource.LRCLIB,
};
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
if (!result.data) return null;
const songResults: InternetProviderLyricSearchResponse[] = result.data.map((song) => {
return {
artist: song.artistName,
id: String(song.id),
name: song.name,
source: LyricSource.LRCLIB,
};
});
return orderSearchResults({ params, results: songResults });
return orderSearchResults({ params, results: songResults });
}
export async function getLyricsBySongId(songId: string): Promise<string | null> {
let result: AxiosResponse<LrcLibTrackResponse, any>;
let result: AxiosResponse<LrcLibTrackResponse, any>;
try {
result = await axios.get<LrcLibTrackResponse>(`${FETCH_URL}/${songId}`);
} catch (e) {
console.error('LrcLib lyrics request got an error!', e);
return null;
}
try {
result = await axios.get<LrcLibTrackResponse>(`${FETCH_URL}/${songId}`);
} catch (e) {
console.error('LrcLib lyrics request got an error!', e);
return null;
}
return result.data.syncedLyrics || result.data.plainLyrics || null;
return result.data.syncedLyrics || result.data.plainLyrics || null;
}
export async function query(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
let result: AxiosResponse<LrcLibTrackResponse, any>;
let result: AxiosResponse<LrcLibTrackResponse, any>;
try {
result = await axios.get<LrcLibTrackResponse>(FETCH_URL, {
params: {
album_name: params.album,
artist_name: params.artist,
duration: params.duration,
track_name: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
try {
result = await axios.get<LrcLibTrackResponse>(FETCH_URL, {
params: {
album_name: params.album,
artist_name: params.artist,
duration: params.duration,
track_name: params.name,
},
timeout: TIMEOUT_MS,
});
} catch (e) {
console.error('LrcLib search request got an error!', e);
return null;
}
const lyrics = result.data.syncedLyrics || result.data.plainLyrics || null;
const lyrics = result.data.syncedLyrics || result.data.plainLyrics || null;
if (!lyrics) {
console.error(`Could not get lyrics on LrcLib!`);
return null;
}
if (!lyrics) {
console.error(`Could not get lyrics on LrcLib!`);
return null;
}
return {
artist: result.data.artistName,
id: String(result.data.id),
lyrics,
name: result.data.name,
source: LyricSource.LRCLIB,
};
return {
artist: result.data.artistName,
id: String(result.data.id),
lyrics,
name: result.data.name,
source: LyricSource.LRCLIB,
};
}

View file

@ -2,9 +2,9 @@ import axios, { AxiosResponse } from 'axios';
import { LyricSource } from '../../../../renderer/api/types';
import { orderSearchResults } from './shared';
import type {
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
InternetProviderLyricResponse,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '/@/renderer/api/types';
const SEARCH_URL = 'https://music.163.com/api/search/get';
@ -13,155 +13,155 @@ const LYRICS_URL = 'https://music.163.com/api/song/lyric';
// Adapted from https://github.com/NyaomiDEV/Sunamu/blob/master/src/main/lyricproviders/netease.ts
export interface NetEaseResponse {
code: number;
result: Result;
code: number;
result: Result;
}
export interface Result {
hasMore: boolean;
songCount: number;
songs: Song[];
hasMore: boolean;
songCount: number;
songs: Song[];
}
export interface Song {
album: Album;
alias: string[];
artists: Artist[];
copyrightId: number;
duration: number;
fee: number;
ftype: number;
id: number;
mark: number;
mvid: number;
name: string;
rUrl: null;
rtype: number;
status: number;
transNames?: string[];
album: Album;
alias: string[];
artists: Artist[];
copyrightId: number;
duration: number;
fee: number;
ftype: number;
id: number;
mark: number;
mvid: number;
name: string;
rUrl: null;
rtype: number;
status: number;
transNames?: string[];
}
export interface Album {
artist: Artist;
copyrightId: number;
id: number;
mark: number;
name: string;
picId: number;
publishTime: number;
size: number;
status: number;
transNames?: string[];
artist: Artist;
copyrightId: number;
id: number;
mark: number;
name: string;
picId: number;
publishTime: number;
size: number;
status: number;
transNames?: string[];
}
export interface Artist {
albumSize: number;
alias: any[];
fansGroup: null;
id: number;
img1v1: number;
img1v1Url: string;
name: string;
picId: number;
picUrl: null;
trans: null;
albumSize: number;
alias: any[];
fansGroup: null;
id: number;
img1v1: number;
img1v1Url: string;
name: string;
picId: number;
picUrl: null;
trans: null;
}
export async function getSearchResults(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricSearchResponse[] | null> {
let result: AxiosResponse<NetEaseResponse>;
let result: AxiosResponse<NetEaseResponse>;
const searchQuery = [params.artist, params.name].join(' ');
const searchQuery = [params.artist, params.name].join(' ');
if (!searchQuery) {
return null;
}
if (!searchQuery) {
return null;
}
try {
result = await axios.get(SEARCH_URL, {
params: {
limit: 5,
offset: 0,
s: searchQuery,
type: '1',
},
try {
result = await axios.get(SEARCH_URL, {
params: {
limit: 5,
offset: 0,
s: searchQuery,
type: '1',
},
});
} catch (e) {
console.error('NetEase search request got an error!', e);
return null;
}
const rawSongsResult = result?.data.result?.songs;
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
const artist = song.artists ? song.artists.map((artist) => artist.name).join(', ') : '';
return {
artist,
id: String(song.id),
name: song.name,
source: LyricSource.NETEASE,
};
});
} catch (e) {
console.error('NetEase search request got an error!', e);
return null;
}
const rawSongsResult = result?.data.result?.songs;
if (!rawSongsResult) return null;
const songResults: InternetProviderLyricSearchResponse[] = rawSongsResult.map((song) => {
const artist = song.artists ? song.artists.map((artist) => artist.name).join(', ') : '';
return {
artist,
id: String(song.id),
name: song.name,
source: LyricSource.NETEASE,
};
});
return orderSearchResults({ params, results: songResults });
return orderSearchResults({ params, results: songResults });
}
async function getMatchedLyrics(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<Omit<InternetProviderLyricResponse, 'lyrics'> | null> {
const results = await getSearchResults(params);
const results = await getSearchResults(params);
const firstMatch = results?.[0];
const firstMatch = results?.[0];
if (!firstMatch || (firstMatch?.score && firstMatch.score > 0.5)) {
return null;
}
if (!firstMatch || (firstMatch?.score && firstMatch.score > 0.5)) {
return null;
}
return firstMatch;
return firstMatch;
}
export async function getLyricsBySongId(songId: string): Promise<string | null> {
let result: AxiosResponse<any, any>;
try {
result = await axios.get(LYRICS_URL, {
params: {
id: songId,
kv: '-1',
lv: '-1',
},
});
} catch (e) {
console.error('NetEase lyrics request got an error!', e);
return null;
}
let result: AxiosResponse<any, any>;
try {
result = await axios.get(LYRICS_URL, {
params: {
id: songId,
kv: '-1',
lv: '-1',
},
});
} catch (e) {
console.error('NetEase lyrics request got an error!', e);
return null;
}
return result.data.klyric?.lyric || result.data.lrc?.lyric;
return result.data.klyric?.lyric || result.data.lrc?.lyric;
}
export async function query(
params: LyricSearchQuery,
params: LyricSearchQuery,
): Promise<InternetProviderLyricResponse | null> {
const lyricsMatch = await getMatchedLyrics(params);
if (!lyricsMatch) {
console.error('Could not find the song on NetEase!');
return null;
}
const lyricsMatch = await getMatchedLyrics(params);
if (!lyricsMatch) {
console.error('Could not find the song on NetEase!');
return null;
}
const lyrics = await getLyricsBySongId(lyricsMatch.id);
if (!lyrics) {
console.error('Could not get lyrics on NetEase!');
return null;
}
const lyrics = await getLyricsBySongId(lyricsMatch.id);
if (!lyrics) {
console.error('Could not get lyrics on NetEase!');
return null;
}
return {
artist: lyricsMatch.artist,
id: lyricsMatch.id,
lyrics,
name: lyricsMatch.name,
source: LyricSource.NETEASE,
};
return {
artist: lyricsMatch.artist,
id: lyricsMatch.id,
lyrics,
name: lyricsMatch.name,
source: LyricSource.NETEASE,
};
}

View file

@ -1,34 +1,34 @@
import Fuse from 'fuse.js';
import {
InternetProviderLyricSearchResponse,
LyricSearchQuery,
InternetProviderLyricSearchResponse,
LyricSearchQuery,
} from '../../../../renderer/api/types';
export const orderSearchResults = (args: {
params: LyricSearchQuery;
results: InternetProviderLyricSearchResponse[];
params: LyricSearchQuery;
results: InternetProviderLyricSearchResponse[];
}) => {
const { params, results } = args;
const { params, results } = args;
const options: Fuse.IFuseOptions<InternetProviderLyricSearchResponse> = {
fieldNormWeight: 1,
includeScore: true,
keys: [
{ getFn: (song) => song.name, name: 'name', weight: 3 },
{ getFn: (song) => song.artist, name: 'artist' },
],
threshold: 1.0,
};
const options: Fuse.IFuseOptions<InternetProviderLyricSearchResponse> = {
fieldNormWeight: 1,
includeScore: true,
keys: [
{ getFn: (song) => song.name, name: 'name', weight: 3 },
{ getFn: (song) => song.artist, name: 'artist' },
],
threshold: 1.0,
};
const fuse = new Fuse(results, options);
const fuse = new Fuse(results, options);
const searchResults = fuse.search<InternetProviderLyricSearchResponse>({
...(params.artist && { artist: params.artist }),
...(params.name && { name: params.name }),
});
const searchResults = fuse.search<InternetProviderLyricSearchResponse>({
...(params.artist && { artist: params.artist }),
...(params.name && { name: params.name }),
});
return searchResults.map((result) => ({
...result.item,
score: result.score,
}));
return searchResults.map((result) => ({
...result.item,
score: result.score,
}));
};

View file

@ -6,215 +6,215 @@ import { PlayerData } from '/@/renderer/store';
declare module 'node-mpv';
function wait(timeout: number) {
return new Promise((resolve) => {
setTimeout(() => {
resolve('resolved');
}, timeout);
});
return new Promise((resolve) => {
setTimeout(() => {
resolve('resolved');
}, timeout);
});
}
ipcMain.handle('player-is-running', async () => {
return getMpvInstance()?.isRunning();
return getMpvInstance()?.isRunning();
});
ipcMain.handle('player-clean-up', async () => {
getMpvInstance()?.stop();
getMpvInstance()?.clearPlaylist();
getMpvInstance()?.stop();
getMpvInstance()?.clearPlaylist();
});
ipcMain.on('player-start', async () => {
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
});
// Starts the player
ipcMain.on('player-play', async () => {
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
await getMpvInstance()
?.play()
.catch((err) => {
console.log('MPV failed to play', err);
});
});
// Pauses the player
ipcMain.on('player-pause', async () => {
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
});
// Stops the player
ipcMain.on('player-stop', async () => {
await getMpvInstance()
?.stop()
.catch((err) => {
console.log('MPV failed to stop', err);
});
await getMpvInstance()
?.stop()
.catch((err) => {
console.log('MPV failed to stop', err);
});
});
// Goes to the next track in the playlist
ipcMain.on('player-next', async () => {
await getMpvInstance()
?.next()
.catch((err) => {
console.log('MPV failed to go to next', err);
});
await getMpvInstance()
?.next()
.catch((err) => {
console.log('MPV failed to go to next', err);
});
});
// Goes to the previous track in the playlist
ipcMain.on('player-previous', async () => {
await getMpvInstance()
?.prev()
.catch((err) => {
console.log('MPV failed to go to previous', err);
});
await getMpvInstance()
?.prev()
.catch((err) => {
console.log('MPV failed to go to previous', err);
});
});
// Seeks forward or backward by the given amount of seconds
ipcMain.on('player-seek', async (_event, time: number) => {
await getMpvInstance()
?.seek(time)
.catch((err) => {
console.log('MPV failed to seek', err);
});
await getMpvInstance()
?.seek(time)
.catch((err) => {
console.log('MPV failed to seek', err);
});
});
// Seeks to the given time in seconds
ipcMain.on('player-seek-to', async (_event, time: number) => {
await getMpvInstance()
?.goToPosition(time)
.catch((err) => {
console.log(`MPV failed to seek to ${time}`, err);
});
await getMpvInstance()
?.goToPosition(time)
.catch((err) => {
console.log(`MPV failed to seek to ${time}`, err);
});
});
// Sets the queue in position 0 and 1 to the given data. Used when manually starting a song or using the next/prev buttons
ipcMain.on('player-set-queue', async (_event, data: PlayerData, pause?: boolean) => {
if (!data.queue.current && !data.queue.next) {
await getMpvInstance()
?.clearPlaylist()
.catch((err) => {
console.log('MPV failed to clear playlist', err);
});
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to pause', err);
});
return;
}
let complete = false;
let tryAttempts = 0;
while (!complete) {
if (tryAttempts > 3) {
getMainWindow()?.webContents.send('renderer-player-error', 'Failed to load song');
complete = true;
} else {
try {
if (data.queue.current) {
await getMpvInstance()
?.load(data.queue.current.streamUrl, 'replace')
if (!data.queue.current && !data.queue.next) {
await getMpvInstance()
?.clearPlaylist()
.catch((err) => {
console.log('MPV failed to load song', err);
console.log('MPV failed to clear playlist', err);
});
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
await getMpvInstance()
?.pause()
.catch((err) => {
console.log('MPV failed to load next song', err);
console.log('MPV failed to pause', err);
});
}
complete = true;
} catch (err) {
console.error(err);
tryAttempts += 1;
await wait(500);
}
return;
}
}
if (pause) {
await getMpvInstance()?.pause();
}
let complete = false;
let tryAttempts = 0;
while (!complete) {
if (tryAttempts > 3) {
getMainWindow()?.webContents.send('renderer-player-error', 'Failed to load song');
complete = true;
} else {
try {
if (data.queue.current) {
await getMpvInstance()
?.load(data.queue.current.streamUrl, 'replace')
.catch((err) => {
console.log('MPV failed to load song', err);
});
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
complete = true;
} catch (err) {
console.error(err);
tryAttempts += 1;
await wait(500);
}
}
}
if (pause) {
await getMpvInstance()?.pause();
}
});
// Replaces the queue in position 1 to the given data
ipcMain.on('player-set-queue-next', async (_event, data: PlayerData) => {
const size = await getMpvInstance()
?.getPlaylistSize()
.catch((err) => {
console.log('MPV failed to get playlist size', err);
});
const size = await getMpvInstance()
?.getPlaylistSize()
.catch((err) => {
console.log('MPV failed to get playlist size', err);
});
if (!size) {
return;
}
if (!size) {
return;
}
if (size > 1) {
await getMpvInstance()
?.playlistRemove(1)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
}
if (size > 1) {
await getMpvInstance()
?.playlistRemove(1)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
});
// Sets the next song in the queue when reaching the end of the queue
ipcMain.on('player-auto-next', async (_event, data: PlayerData) => {
// Always keep the current song as position 0 in the mpv queue
// This allows us to easily set update the next song in the queue without
// disturbing the currently playing song
await getMpvInstance()
?.playlistRemove(0)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
if (data.queue.next) {
// Always keep the current song as position 0 in the mpv queue
// This allows us to easily set update the next song in the queue without
// disturbing the currently playing song
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
?.playlistRemove(0)
.catch((err) => {
console.log('MPV failed to remove song from playlist', err);
});
if (data.queue.next) {
await getMpvInstance()
?.load(data.queue.next.streamUrl, 'append')
.catch((err) => {
console.log('MPV failed to load next song', err);
});
}
});
// Sets the volume to the given value (0-100)
ipcMain.on('player-volume', async (_event, value: number) => {
await getMpvInstance()
?.volume(value)
.catch((err) => {
console.log('MPV failed to set volume', err);
});
await getMpvInstance()
?.volume(value)
.catch((err) => {
console.log('MPV failed to set volume', err);
});
});
// Toggles the mute status
ipcMain.on('player-mute', async () => {
await getMpvInstance()
?.mute()
.catch((err) => {
console.log('MPV failed to toggle mute', err);
});
await getMpvInstance()
?.mute()
.catch((err) => {
console.log('MPV failed to toggle mute', err);
});
});
ipcMain.handle('player-get-time', async (): Promise<number | undefined> => {
return getMpvInstance()?.getTimePosition();
return getMpvInstance()?.getTimePosition();
});

View file

@ -2,26 +2,26 @@
import { BrowserWindow, globalShortcut } from 'electron';
export const enableMediaKeys = (window: BrowserWindow | null) => {
globalShortcut.register('MediaStop', () => {
window?.webContents.send('renderer-player-stop');
});
globalShortcut.register('MediaStop', () => {
window?.webContents.send('renderer-player-stop');
});
globalShortcut.register('MediaPlayPause', () => {
window?.webContents.send('renderer-player-play-pause');
});
globalShortcut.register('MediaPlayPause', () => {
window?.webContents.send('renderer-player-play-pause');
});
globalShortcut.register('MediaNextTrack', () => {
window?.webContents.send('renderer-player-next');
});
globalShortcut.register('MediaNextTrack', () => {
window?.webContents.send('renderer-player-next');
});
globalShortcut.register('MediaPreviousTrack', () => {
window?.webContents.send('renderer-player-previous');
});
globalShortcut.register('MediaPreviousTrack', () => {
window?.webContents.send('renderer-player-previous');
});
};
export const disableMediaKeys = () => {
globalShortcut.unregister('MediaStop');
globalShortcut.unregister('MediaPlayPause');
globalShortcut.unregister('MediaNextTrack');
globalShortcut.unregister('MediaPreviousTrack');
globalShortcut.unregister('MediaStop');
globalShortcut.unregister('MediaPlayPause');
globalShortcut.unregister('MediaNextTrack');
globalShortcut.unregister('MediaPreviousTrack');
};

View file

@ -4,47 +4,47 @@ import Store from 'electron-store';
export const store = new Store();
ipcMain.handle('settings-get', (_event, data: { property: string }) => {
return store.get(`${data.property}`);
return store.get(`${data.property}`);
});
ipcMain.on('settings-set', (__event, data: { property: string; value: any }) => {
store.set(`${data.property}`, data.value);
store.set(`${data.property}`, data.value);
});
ipcMain.handle('password-get', (_event, server: string): string | null => {
if (safeStorage.isEncryptionAvailable()) {
const servers = store.get('server') as Record<string, string> | undefined;
if (safeStorage.isEncryptionAvailable()) {
const servers = store.get('server') as Record<string, string> | undefined;
if (!servers) {
return null;
if (!servers) {
return null;
}
const encrypted = servers[server];
if (!encrypted) return null;
const decrypted = safeStorage.decryptString(Buffer.from(encrypted, 'hex'));
return decrypted;
}
const encrypted = servers[server];
if (!encrypted) return null;
const decrypted = safeStorage.decryptString(Buffer.from(encrypted, 'hex'));
return decrypted;
}
return null;
return null;
});
ipcMain.on('password-remove', (_event, server: string) => {
const passwords = store.get('server', {}) as Record<string, string>;
if (server in passwords) {
delete passwords[server];
}
store.set({ server: passwords });
const passwords = store.get('server', {}) as Record<string, string>;
if (server in passwords) {
delete passwords[server];
}
store.set({ server: passwords });
});
ipcMain.handle('password-set', (_event, password: string, server: string) => {
if (safeStorage.isEncryptionAvailable()) {
const encrypted = safeStorage.encryptString(password);
const passwords = store.get('server', {}) as Record<string, string>;
passwords[server] = encrypted.toString('hex');
store.set({ server: passwords });
if (safeStorage.isEncryptionAvailable()) {
const encrypted = safeStorage.encryptString(password);
const passwords = store.get('server', {}) as Record<string, string>;
passwords[server] = encrypted.toString('hex');
store.set({ server: passwords });
return true;
}
return false;
return true;
}
return false;
});

View file

@ -5,164 +5,166 @@ import { getMainWindow } from '../../main';
import { PlayerRepeat, PlayerShuffle, PlayerStatus } from '/@/renderer/types';
const mprisPlayer = Player({
identity: 'Feishin',
maximumRate: 1.0,
minimumRate: 1.0,
name: 'Feishin',
rate: 1.0,
supportedInterfaces: ['player'],
supportedMimeTypes: ['audio/mpeg', 'application/ogg'],
supportedUriSchemes: ['file'],
identity: 'Feishin',
maximumRate: 1.0,
minimumRate: 1.0,
name: 'Feishin',
rate: 1.0,
supportedInterfaces: ['player'],
supportedMimeTypes: ['audio/mpeg', 'application/ogg'],
supportedUriSchemes: ['file'],
});
mprisPlayer.on('quit', () => {
process.exit();
process.exit();
});
mprisPlayer.on('stop', () => {
getMainWindow()?.webContents.send('renderer-player-stop');
mprisPlayer.playbackStatus = 'Paused';
getMainWindow()?.webContents.send('renderer-player-stop');
mprisPlayer.playbackStatus = 'Paused';
});
mprisPlayer.on('pause', () => {
getMainWindow()?.webContents.send('renderer-player-pause');
mprisPlayer.playbackStatus = 'Paused';
getMainWindow()?.webContents.send('renderer-player-pause');
mprisPlayer.playbackStatus = 'Paused';
});
mprisPlayer.on('play', () => {
getMainWindow()?.webContents.send('renderer-player-play');
mprisPlayer.playbackStatus = 'Playing';
getMainWindow()?.webContents.send('renderer-player-play');
mprisPlayer.playbackStatus = 'Playing';
});
mprisPlayer.on('playpause', () => {
getMainWindow()?.webContents.send('renderer-player-play-pause');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
} else {
mprisPlayer.playbackStatus = 'Paused';
}
getMainWindow()?.webContents.send('renderer-player-play-pause');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
} else {
mprisPlayer.playbackStatus = 'Paused';
}
});
mprisPlayer.on('next', () => {
getMainWindow()?.webContents.send('renderer-player-next');
getMainWindow()?.webContents.send('renderer-player-next');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
}
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = 'Playing';
}
});
mprisPlayer.on('previous', () => {
getMainWindow()?.webContents.send('renderer-player-previous');
getMainWindow()?.webContents.send('renderer-player-previous');
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING;
}
if (mprisPlayer.playbackStatus !== 'Playing') {
mprisPlayer.playbackStatus = Player.PLAYBACK_STATUS_PLAYING;
}
});
mprisPlayer.on('volume', (event: any) => {
getMainWindow()?.webContents.send('mpris-request-volume', {
volume: event,
});
getMainWindow()?.webContents.send('mpris-request-volume', {
volume: event,
});
});
mprisPlayer.on('shuffle', (event: boolean) => {
getMainWindow()?.webContents.send('mpris-request-toggle-shuffle', { shuffle: event });
mprisPlayer.shuffle = event;
getMainWindow()?.webContents.send('mpris-request-toggle-shuffle', { shuffle: event });
mprisPlayer.shuffle = event;
});
mprisPlayer.on('loopStatus', (event: string) => {
getMainWindow()?.webContents.send('mpris-request-toggle-repeat', { repeat: event });
mprisPlayer.loopStatus = event;
getMainWindow()?.webContents.send('mpris-request-toggle-repeat', { repeat: event });
mprisPlayer.loopStatus = event;
});
mprisPlayer.on('position', (event: any) => {
getMainWindow()?.webContents.send('mpris-request-position', {
position: event.position / 1e6,
});
getMainWindow()?.webContents.send('mpris-request-position', {
position: event.position / 1e6,
});
});
mprisPlayer.on('seek', (event: number) => {
getMainWindow()?.webContents.send('mpris-request-seek', {
offset: event / 1e6,
});
getMainWindow()?.webContents.send('mpris-request-seek', {
offset: event / 1e6,
});
});
ipcMain.on('mpris-update-position', (_event, arg) => {
mprisPlayer.getPosition = () => arg * 1e6;
mprisPlayer.getPosition = () => arg * 1e6;
});
ipcMain.on('mpris-update-seek', (_event, arg) => {
mprisPlayer.seeked(arg * 1e6);
mprisPlayer.seeked(arg * 1e6);
});
ipcMain.on('mpris-update-volume', (_event, arg) => {
mprisPlayer.volume = Number(arg);
mprisPlayer.volume = Number(arg);
});
ipcMain.on('mpris-update-repeat', (_event, arg) => {
mprisPlayer.loopStatus = arg;
mprisPlayer.loopStatus = arg;
});
ipcMain.on('mpris-update-shuffle', (_event, arg) => {
mprisPlayer.shuffle = arg;
mprisPlayer.shuffle = arg;
});
ipcMain.on(
'mpris-update-song',
(
_event,
args: {
currentTime: number;
repeat: PlayerRepeat;
shuffle: PlayerShuffle;
song: QueueSong;
status: PlayerStatus;
'mpris-update-song',
(
_event,
args: {
currentTime: number;
repeat: PlayerRepeat;
shuffle: PlayerShuffle;
song: QueueSong;
status: PlayerStatus;
},
) => {
const { song, status, repeat, shuffle } = args || {};
try {
mprisPlayer.playbackStatus = status;
if (repeat) {
mprisPlayer.loopStatus =
repeat === 'all' ? 'Playlist' : repeat === 'one' ? 'Track' : 'None';
}
if (shuffle) {
mprisPlayer.shuffle = shuffle !== 'none';
}
if (!song) return;
const upsizedImageUrl = song.imageUrl
? song.imageUrl
?.replace(/&size=\d+/, '&size=300')
.replace(/\?width=\d+/, '?width=300')
.replace(/&height=\d+/, '&height=300')
: null;
mprisPlayer.metadata = {
'mpris:artUrl': upsizedImageUrl,
'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e6) : null,
'mpris:trackid': song?.id
? mprisPlayer.objectPath(`track/${song.id?.replace('-', '')}`)
: '',
'xesam:album': song.album || null,
'xesam:albumArtist': song.albumArtists?.length ? song.albumArtists[0].name : null,
'xesam:artist':
song.artists?.length !== 0
? song.artists?.map((artist: RelatedArtist) => artist.name)
: null,
'xesam:discNumber': song.discNumber ? song.discNumber : null,
'xesam:genre': song.genres?.length
? song.genres.map((genre: any) => genre.name)
: null,
'xesam:title': song.name || null,
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
'xesam:useCount':
song.playCount !== null && song.playCount !== undefined ? song.playCount : null,
};
} catch (err) {
console.log(err);
}
},
) => {
const { song, status, repeat, shuffle } = args || {};
try {
mprisPlayer.playbackStatus = status;
if (repeat) {
mprisPlayer.loopStatus =
repeat === 'all' ? 'Playlist' : repeat === 'one' ? 'Track' : 'None';
}
if (shuffle) {
mprisPlayer.shuffle = shuffle !== 'none';
}
if (!song) return;
const upsizedImageUrl = song.imageUrl
? song.imageUrl
?.replace(/&size=\d+/, '&size=300')
.replace(/\?width=\d+/, '?width=300')
.replace(/&height=\d+/, '&height=300')
: null;
mprisPlayer.metadata = {
'mpris:artUrl': upsizedImageUrl,
'mpris:length': song.duration ? Math.round((song.duration || 0) * 1e6) : null,
'mpris:trackid': song?.id
? mprisPlayer.objectPath(`track/${song.id?.replace('-', '')}`)
: '',
'xesam:album': song.album || null,
'xesam:albumArtist': song.albumArtists?.length ? song.albumArtists[0].name : null,
'xesam:artist':
song.artists?.length !== 0
? song.artists?.map((artist: RelatedArtist) => artist.name)
: null,
'xesam:discNumber': song.discNumber ? song.discNumber : null,
'xesam:genre': song.genres?.length ? song.genres.map((genre: any) => genre.name) : null,
'xesam:title': song.name || null,
'xesam:trackNumber': song.trackNumber ? song.trackNumber : null,
'xesam:useCount':
song.playCount !== null && song.playCount !== undefined ? song.playCount : null,
};
} catch (err) {
console.log(err);
}
},
);

File diff suppressed because it is too large Load diff

View file

@ -1,269 +1,279 @@
import { app, Menu, shell, BrowserWindow, MenuItemConstructorOptions } from 'electron';
interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions {
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
selector?: string;
submenu?: DarwinMenuItemConstructorOptions[] | Menu;
}
export default class MenuBuilder {
mainWindow: BrowserWindow;
mainWindow: BrowserWindow;
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
buildMenu(): Menu {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
const template =
process.platform === 'darwin' ? this.buildDarwinTemplate() : this.buildDefaultTemplate();
buildMenu(): Menu {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
const template =
process.platform === 'darwin'
? this.buildDarwinTemplate()
: this.buildDefaultTemplate();
return menu;
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
setupDevelopmentEnvironment(): void {
this.mainWindow.webContents.on('context-menu', (_, props) => {
const { x, y } = props;
return menu;
}
Menu.buildFromTemplate([
{
click: () => {
this.mainWindow.webContents.inspectElement(x, y);
},
label: 'Inspect element',
},
]).popup({ window: this.mainWindow });
});
}
setupDevelopmentEnvironment(): void {
this.mainWindow.webContents.on('context-menu', (_, props) => {
const { x, y } = props;
buildDarwinTemplate(): MenuItemConstructorOptions[] {
const subMenuAbout: DarwinMenuItemConstructorOptions = {
label: 'Electron',
submenu: [
{
label: 'About ElectronReact',
selector: 'orderFrontStandardAboutPanel:',
},
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{
accelerator: 'Command+H',
label: 'Hide ElectronReact',
selector: 'hide:',
},
{
accelerator: 'Command+Shift+H',
label: 'Hide Others',
selector: 'hideOtherApplications:',
},
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{
accelerator: 'Command+Q',
click: () => {
app.quit();
},
label: 'Quit',
},
],
};
const subMenuEdit: DarwinMenuItemConstructorOptions = {
label: 'Edit',
submenu: [
{ accelerator: 'Command+Z', label: 'Undo', selector: 'undo:' },
{ accelerator: 'Shift+Command+Z', label: 'Redo', selector: 'redo:' },
{ type: 'separator' },
{ accelerator: 'Command+X', label: 'Cut', selector: 'cut:' },
{ accelerator: 'Command+C', label: 'Copy', selector: 'copy:' },
{ accelerator: 'Command+V', label: 'Paste', selector: 'paste:' },
{
accelerator: 'Command+A',
label: 'Select All',
selector: 'selectAll:',
},
],
};
const subMenuViewDev: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: 'Reload',
},
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
{
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle Developer Tools',
},
],
};
const subMenuViewProd: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
],
};
const subMenuWindow: DarwinMenuItemConstructorOptions = {
label: 'Window',
submenu: [
{
accelerator: 'Command+M',
label: 'Minimize',
selector: 'performMiniaturize:',
},
{ accelerator: 'Command+W', label: 'Close', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
],
};
const subMenuHelp: MenuItemConstructorOptions = {
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme');
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
};
const subMenuView =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? subMenuViewDev
: subMenuViewProd;
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
accelerator: 'Ctrl+O',
label: '&Open',
},
{
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
label: '&Close',
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? [
Menu.buildFromTemplate([
{
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: '&Reload',
click: () => {
this.mainWindow.webContents.inspectElement(x, y);
},
label: 'Inspect element',
},
]).popup({ window: this.mainWindow });
});
}
buildDarwinTemplate(): MenuItemConstructorOptions[] {
const subMenuAbout: DarwinMenuItemConstructorOptions = {
label: 'Electron',
submenu: [
{
label: 'About ElectronReact',
selector: 'orderFrontStandardAboutPanel:',
},
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{
accelerator: 'Command+H',
label: 'Hide ElectronReact',
selector: 'hide:',
},
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle &Full Screen',
accelerator: 'Command+Shift+H',
label: 'Hide Others',
selector: 'hideOtherApplications:',
},
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{
accelerator: 'Command+Q',
click: () => {
app.quit();
},
label: 'Quit',
},
],
};
const subMenuEdit: DarwinMenuItemConstructorOptions = {
label: 'Edit',
submenu: [
{ accelerator: 'Command+Z', label: 'Undo', selector: 'undo:' },
{ accelerator: 'Shift+Command+Z', label: 'Redo', selector: 'redo:' },
{ type: 'separator' },
{ accelerator: 'Command+X', label: 'Cut', selector: 'cut:' },
{ accelerator: 'Command+C', label: 'Copy', selector: 'copy:' },
{ accelerator: 'Command+V', label: 'Paste', selector: 'paste:' },
{
accelerator: 'Command+A',
label: 'Select All',
selector: 'selectAll:',
},
],
};
const subMenuViewDev: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Command+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: 'Reload',
},
{
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle &Developer Tools',
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
]
: [
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle &Full Screen',
accelerator: 'Alt+Command+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle Developer Tools',
},
],
},
{
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/tree/main/docs#readme');
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
},
];
],
};
const subMenuViewProd: MenuItemConstructorOptions = {
label: 'View',
submenu: [
{
accelerator: 'Ctrl+Command+F',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
},
label: 'Toggle Full Screen',
},
],
};
const subMenuWindow: DarwinMenuItemConstructorOptions = {
label: 'Window',
submenu: [
{
accelerator: 'Command+M',
label: 'Minimize',
selector: 'performMiniaturize:',
},
{ accelerator: 'Command+W', label: 'Close', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' },
],
};
const subMenuHelp: MenuItemConstructorOptions = {
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal(
'https://github.com/electron/electron/tree/main/docs#readme',
);
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
};
return templateDefault;
}
const subMenuView =
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? subMenuViewDev
: subMenuViewProd;
return [subMenuAbout, subMenuEdit, subMenuView, subMenuWindow, subMenuHelp];
}
buildDefaultTemplate() {
const templateDefault = [
{
label: '&File',
submenu: [
{
accelerator: 'Ctrl+O',
label: '&Open',
},
{
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
},
label: '&Close',
},
],
},
{
label: '&View',
submenu:
process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'
? [
{
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
},
label: '&Reload',
},
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen(),
);
},
label: 'Toggle &Full Screen',
},
{
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.webContents.toggleDevTools();
},
label: 'Toggle &Developer Tools',
},
]
: [
{
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(
!this.mainWindow.isFullScreen(),
);
},
label: 'Toggle &Full Screen',
},
],
},
{
label: 'Help',
submenu: [
{
click() {
shell.openExternal('https://electronjs.org');
},
label: 'Learn More',
},
{
click() {
shell.openExternal(
'https://github.com/electron/electron/tree/main/docs#readme',
);
},
label: 'Documentation',
},
{
click() {
shell.openExternal('https://www.electronjs.org/community');
},
label: 'Community Discussions',
},
{
click() {
shell.openExternal('https://github.com/electron/electron/issues');
},
label: 'Search Issues',
},
],
},
];
return templateDefault;
}
}

View file

@ -8,12 +8,12 @@ import { mpvPlayer, mpvPlayerListener } from './preload/mpv-player';
import { utils } from './preload/utils';
contextBridge.exposeInMainWorld('electron', {
browser,
ipc,
localSettings,
lyrics,
mpris,
mpvPlayer,
mpvPlayerListener,
utils,
browser,
ipc,
localSettings,
lyrics,
mpris,
mpvPlayer,
mpvPlayerListener,
utils,
});

View file

@ -1,26 +1,26 @@
import { ipcRenderer } from 'electron';
const exit = () => {
ipcRenderer.send('window-close');
ipcRenderer.send('window-close');
};
const maximize = () => {
ipcRenderer.send('window-maximize');
ipcRenderer.send('window-maximize');
};
const minimize = () => {
ipcRenderer.send('window-minimize');
ipcRenderer.send('window-minimize');
};
const unmaximize = () => {
ipcRenderer.send('window-unmaximize');
ipcRenderer.send('window-unmaximize');
};
const devtools = () => {
ipcRenderer.send('window-dev-tools');
ipcRenderer.send('window-dev-tools');
};
export const browser = {
devtools,
exit,
maximize,
minimize,
unmaximize,
devtools,
exit,
maximize,
minimize,
unmaximize,
};

View file

@ -1,14 +1,14 @@
import { ipcRenderer } from 'electron';
const removeAllListeners = (channel: string) => {
ipcRenderer.removeAllListeners(channel);
ipcRenderer.removeAllListeners(channel);
};
const send = (channel: string, ...args: any[]) => {
ipcRenderer.send(channel, ...args);
ipcRenderer.send(channel, ...args);
};
export const ipc = {
removeAllListeners,
send,
removeAllListeners,
send,
};

View file

@ -4,49 +4,49 @@ import Store from 'electron-store';
const store = new Store();
const set = (property: string, value: string | Record<string, unknown> | boolean | string[]) => {
store.set(`${property}`, value);
store.set(`${property}`, value);
};
const get = (property: string) => {
return store.get(`${property}`);
return store.get(`${property}`);
};
const restart = () => {
ipcRenderer.send('app-restart');
ipcRenderer.send('app-restart');
};
const enableMediaKeys = () => {
ipcRenderer.send('global-media-keys-enable');
ipcRenderer.send('global-media-keys-enable');
};
const disableMediaKeys = () => {
ipcRenderer.send('global-media-keys-disable');
ipcRenderer.send('global-media-keys-disable');
};
const passwordGet = async (server: string): Promise<string | null> => {
return ipcRenderer.invoke('password-get', server);
return ipcRenderer.invoke('password-get', server);
};
const passwordRemove = (server: string) => {
ipcRenderer.send('password-remove', server);
ipcRenderer.send('password-remove', server);
};
const passwordSet = async (password: string, server: string): Promise<boolean> => {
return ipcRenderer.invoke('password-set', password, server);
return ipcRenderer.invoke('password-set', password, server);
};
const setZoomFactor = (zoomFactor: number) => {
webFrame.setZoomFactor(zoomFactor / 100);
webFrame.setZoomFactor(zoomFactor / 100);
};
export const localSettings = {
disableMediaKeys,
enableMediaKeys,
get,
passwordGet,
passwordRemove,
passwordSet,
restart,
set,
setZoomFactor,
disableMediaKeys,
enableMediaKeys,
get,
passwordGet,
passwordRemove,
passwordSet,
restart,
set,
setZoomFactor,
};

View file

@ -2,22 +2,22 @@ import { ipcRenderer } from 'electron';
import { LyricSearchQuery, QueueSong } from '/@/renderer/api/types';
const getRemoteLyricsBySong = (song: QueueSong) => {
const result = ipcRenderer.invoke('lyric-by-song', song);
return result;
const result = ipcRenderer.invoke('lyric-by-song', song);
return result;
};
const searchRemoteLyrics = (params: LyricSearchQuery) => {
const result = ipcRenderer.invoke('lyric-search', params);
return result;
const result = ipcRenderer.invoke('lyric-search', params);
return result;
};
const getRemoteLyricsByRemoteId = (id: string) => {
const result = ipcRenderer.invoke('lyric-by-remote-id', id);
return result;
const result = ipcRenderer.invoke('lyric-by-remote-id', id);
return result;
};
export const lyrics = {
getRemoteLyricsByRemoteId,
getRemoteLyricsBySong,
searchRemoteLyrics,
getRemoteLyricsByRemoteId,
getRemoteLyricsBySong,
searchRemoteLyrics,
};

View file

@ -2,69 +2,69 @@ import { IpcRendererEvent, ipcRenderer } from 'electron';
import { QueueSong } from '/@/renderer/api/types';
const updateSong = (args: { currentTime: number; song: QueueSong }) => {
ipcRenderer.send('mpris-update-song', args);
ipcRenderer.send('mpris-update-song', args);
};
const updatePosition = (timeSec: number) => {
ipcRenderer.send('mpris-update-position', timeSec);
ipcRenderer.send('mpris-update-position', timeSec);
};
const updateSeek = (timeSec: number) => {
ipcRenderer.send('mpris-update-seek', timeSec);
ipcRenderer.send('mpris-update-seek', timeSec);
};
const updateVolume = (volume: number) => {
ipcRenderer.send('mpris-update-volume', volume);
ipcRenderer.send('mpris-update-volume', volume);
};
const updateRepeat = (repeat: string) => {
ipcRenderer.send('mpris-update-repeat', repeat);
ipcRenderer.send('mpris-update-repeat', repeat);
};
const updateShuffle = (shuffle: boolean) => {
ipcRenderer.send('mpris-update-shuffle', shuffle);
ipcRenderer.send('mpris-update-shuffle', shuffle);
};
const toggleRepeat = () => {
ipcRenderer.send('mpris-toggle-repeat');
ipcRenderer.send('mpris-toggle-repeat');
};
const toggleShuffle = () => {
ipcRenderer.send('mpris-toggle-shuffle');
ipcRenderer.send('mpris-toggle-shuffle');
};
const requestPosition = (cb: (event: IpcRendererEvent, data: { position: number }) => void) => {
ipcRenderer.on('mpris-request-position', cb);
ipcRenderer.on('mpris-request-position', cb);
};
const requestSeek = (cb: (event: IpcRendererEvent, data: { offset: number }) => void) => {
ipcRenderer.on('mpris-request-seek', cb);
ipcRenderer.on('mpris-request-seek', cb);
};
const requestVolume = (cb: (event: IpcRendererEvent, data: { volume: number }) => void) => {
ipcRenderer.on('mpris-request-volume', cb);
ipcRenderer.on('mpris-request-volume', cb);
};
const requestToggleRepeat = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('mpris-request-toggle-repeat', cb);
ipcRenderer.on('mpris-request-toggle-repeat', cb);
};
const requestToggleShuffle = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('mpris-request-toggle-shuffle', cb);
ipcRenderer.on('mpris-request-toggle-shuffle', cb);
};
export const mpris = {
requestPosition,
requestSeek,
requestToggleRepeat,
requestToggleShuffle,
requestVolume,
toggleRepeat,
toggleShuffle,
updatePosition,
updateRepeat,
updateSeek,
updateShuffle,
updateSong,
updateVolume,
requestPosition,
requestSeek,
requestToggleRepeat,
requestToggleShuffle,
requestVolume,
toggleRepeat,
toggleShuffle,
updatePosition,
updateRepeat,
updateSeek,
updateShuffle,
updateSong,
updateVolume,
};

View file

@ -2,213 +2,213 @@ import { ipcRenderer, IpcRendererEvent } from 'electron';
import { PlayerData } from '/@/renderer/store';
const initialize = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
ipcRenderer.send('player-initialize', data);
ipcRenderer.send('player-initialize', data);
};
const restart = (data: { extraParameters?: string[]; properties?: Record<string, any> }) => {
ipcRenderer.send('player-restart', data);
ipcRenderer.send('player-restart', data);
};
const isRunning = () => {
return ipcRenderer.invoke('player-is-running');
return ipcRenderer.invoke('player-is-running');
};
const cleanup = () => {
return ipcRenderer.invoke('player-clean-up');
return ipcRenderer.invoke('player-clean-up');
};
const setProperties = (data: Record<string, any>) => {
console.log('Setting property :>>', data);
ipcRenderer.send('player-set-properties', data);
console.log('Setting property :>>', data);
ipcRenderer.send('player-set-properties', data);
};
const autoNext = (data: PlayerData) => {
ipcRenderer.send('player-auto-next', data);
ipcRenderer.send('player-auto-next', data);
};
const currentTime = () => {
ipcRenderer.send('player-current-time');
ipcRenderer.send('player-current-time');
};
const mute = () => {
ipcRenderer.send('player-mute');
ipcRenderer.send('player-mute');
};
const next = () => {
ipcRenderer.send('player-next');
ipcRenderer.send('player-next');
};
const pause = () => {
ipcRenderer.send('player-pause');
ipcRenderer.send('player-pause');
};
const play = () => {
ipcRenderer.send('player-play');
ipcRenderer.send('player-play');
};
const previous = () => {
ipcRenderer.send('player-previous');
ipcRenderer.send('player-previous');
};
const restoreQueue = () => {
ipcRenderer.send('player-restore-queue');
ipcRenderer.send('player-restore-queue');
};
const saveQueue = (data: Record<string, any>) => {
ipcRenderer.send('player-save-queue', data);
ipcRenderer.send('player-save-queue', data);
};
const seek = (seconds: number) => {
ipcRenderer.send('player-seek', seconds);
ipcRenderer.send('player-seek', seconds);
};
const seekTo = (seconds: number) => {
ipcRenderer.send('player-seek-to', seconds);
ipcRenderer.send('player-seek-to', seconds);
};
const setQueue = (data: PlayerData, pause?: boolean) => {
ipcRenderer.send('player-set-queue', data, pause);
ipcRenderer.send('player-set-queue', data, pause);
};
const setQueueNext = (data: PlayerData) => {
ipcRenderer.send('player-set-queue-next', data);
ipcRenderer.send('player-set-queue-next', data);
};
const stop = () => {
ipcRenderer.send('player-stop');
ipcRenderer.send('player-stop');
};
const volume = (value: number) => {
ipcRenderer.send('player-volume', value);
ipcRenderer.send('player-volume', value);
};
const quit = () => {
ipcRenderer.send('player-quit');
ipcRenderer.send('player-quit');
};
const getCurrentTime = async () => {
return ipcRenderer.invoke('player-get-time');
return ipcRenderer.invoke('player-get-time');
};
const rendererAutoNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-auto-next', cb);
ipcRenderer.on('renderer-player-auto-next', cb);
};
const rendererCurrentTime = (cb: (event: IpcRendererEvent, data: number) => void) => {
ipcRenderer.on('renderer-player-current-time', cb);
ipcRenderer.on('renderer-player-current-time', cb);
};
const rendererNext = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-next', cb);
ipcRenderer.on('renderer-player-next', cb);
};
const rendererPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-pause', cb);
ipcRenderer.on('renderer-player-pause', cb);
};
const rendererPlay = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play', cb);
ipcRenderer.on('renderer-player-play', cb);
};
const rendererPlayPause = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-play-pause', cb);
ipcRenderer.on('renderer-player-play-pause', cb);
};
const rendererPrevious = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-previous', cb);
ipcRenderer.on('renderer-player-previous', cb);
};
const rendererStop = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-stop', cb);
ipcRenderer.on('renderer-player-stop', cb);
};
const rendererSkipForward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-forward', cb);
ipcRenderer.on('renderer-player-skip-forward', cb);
};
const rendererSkipBackward = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-skip-backward', cb);
ipcRenderer.on('renderer-player-skip-backward', cb);
};
const rendererVolumeUp = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-up', cb);
ipcRenderer.on('renderer-player-volume-up', cb);
};
const rendererVolumeDown = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-down', cb);
ipcRenderer.on('renderer-player-volume-down', cb);
};
const rendererVolumeMute = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-volume-mute', cb);
ipcRenderer.on('renderer-player-volume-mute', cb);
};
const rendererToggleRepeat = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-repeat', cb);
ipcRenderer.on('renderer-player-toggle-repeat', cb);
};
const rendererToggleShuffle = (cb: (event: IpcRendererEvent, data: PlayerData) => void) => {
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
ipcRenderer.on('renderer-player-toggle-shuffle', cb);
};
const rendererQuit = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-quit', cb);
ipcRenderer.on('renderer-player-quit', cb);
};
const rendererSaveQueue = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-save-queue', cb);
ipcRenderer.on('renderer-player-save-queue', cb);
};
const rendererRestoreQueue = (cb: (event: IpcRendererEvent) => void) => {
ipcRenderer.on('renderer-player-restore-queue', cb);
ipcRenderer.on('renderer-player-restore-queue', cb);
};
const rendererError = (cb: (event: IpcRendererEvent, data: string) => void) => {
ipcRenderer.on('renderer-player-error', cb);
ipcRenderer.on('renderer-player-error', cb);
};
export const mpvPlayer = {
autoNext,
cleanup,
currentTime,
getCurrentTime,
initialize,
isRunning,
mute,
next,
pause,
play,
previous,
quit,
restart,
restoreQueue,
saveQueue,
seek,
seekTo,
setProperties,
setQueue,
setQueueNext,
stop,
volume,
autoNext,
cleanup,
currentTime,
getCurrentTime,
initialize,
isRunning,
mute,
next,
pause,
play,
previous,
quit,
restart,
restoreQueue,
saveQueue,
seek,
seekTo,
setProperties,
setQueue,
setQueueNext,
stop,
volume,
};
export const mpvPlayerListener = {
rendererAutoNext,
rendererCurrentTime,
rendererError,
rendererNext,
rendererPause,
rendererPlay,
rendererPlayPause,
rendererPrevious,
rendererQuit,
rendererRestoreQueue,
rendererSaveQueue,
rendererSkipBackward,
rendererSkipForward,
rendererStop,
rendererToggleRepeat,
rendererToggleShuffle,
rendererVolumeDown,
rendererVolumeMute,
rendererVolumeUp,
rendererAutoNext,
rendererCurrentTime,
rendererError,
rendererNext,
rendererPause,
rendererPlay,
rendererPlayPause,
rendererPrevious,
rendererQuit,
rendererRestoreQueue,
rendererSaveQueue,
rendererSkipBackward,
rendererSkipForward,
rendererStop,
rendererToggleRepeat,
rendererToggleShuffle,
rendererVolumeDown,
rendererVolumeMute,
rendererVolumeUp,
};

View file

@ -1,7 +1,7 @@
import { isMacOS, isWindows, isLinux } from '../utils';
export const utils = {
isLinux,
isMacOS,
isWindows,
isLinux,
isMacOS,
isWindows,
};

View file

@ -6,47 +6,47 @@ import { URL } from 'url';
export let resolveHtmlPath: (htmlFileName: string) => string;
if (process.env.NODE_ENV === 'development') {
const port = process.env.PORT || 4343;
resolveHtmlPath = (htmlFileName: string) => {
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
};
const port = process.env.PORT || 4343;
resolveHtmlPath = (htmlFileName: string) => {
const url = new URL(`http://localhost:${port}`);
url.pathname = htmlFileName;
return url.href;
};
} else {
resolveHtmlPath = (htmlFileName: string) => {
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
};
resolveHtmlPath = (htmlFileName: string) => {
return `file://${path.resolve(__dirname, '../renderer/', htmlFileName)}`;
};
}
export const isMacOS = () => {
return process.platform === 'darwin';
return process.platform === 'darwin';
};
export const isWindows = () => {
return process.platform === 'win32';
return process.platform === 'win32';
};
export const isLinux = () => {
return process.platform === 'linux';
return process.platform === 'linux';
};
export const hotkeyToElectronAccelerator = (hotkey: string) => {
let accelerator = hotkey;
let accelerator = hotkey;
const replacements = {
mod: 'CmdOrCtrl',
numpad: 'num',
numpadadd: 'numadd',
numpaddecimal: 'numdec',
numpaddivide: 'numdiv',
numpadenter: 'numenter',
numpadmultiply: 'nummult',
numpadsubtract: 'numsub',
};
const replacements = {
mod: 'CmdOrCtrl',
numpad: 'num',
numpadadd: 'numadd',
numpaddecimal: 'numdec',
numpaddivide: 'numdiv',
numpadenter: 'numenter',
numpadmultiply: 'nummult',
numpadsubtract: 'numsub',
};
Object.keys(replacements).forEach((key) => {
accelerator = accelerator.replace(key, replacements[key as keyof typeof replacements]);
});
Object.keys(replacements).forEach((key) => {
accelerator = accelerator.replace(key, replacements[key as keyof typeof replacements]);
});
return accelerator;
return accelerator;
};