mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 10:03:33 +00:00
navidrome cover art workaround
This commit is contained in:
parent
a8fb7ff11e
commit
39c714a137
8 changed files with 110 additions and 8 deletions
|
|
@ -513,6 +513,8 @@
|
|||
"discordListening_description": "show status as listening instead of playing",
|
||||
"discordRichPresence": "{{discord}} rich presence",
|
||||
"discordRichPresence_description": "enable playback status in {{discord}} rich presence. Image keys are: {{icon}}, {{playing}}, and {{paused}} ",
|
||||
"discordServeImage": "serve {{discord}} images from server",
|
||||
"discordServeImage_description": "share cover art for {{discord}} rich presence from server itself, only available for jellyfin and navidrome",
|
||||
"discordUpdateInterval": "{{discord}} rich presence update interval",
|
||||
"discordUpdateInterval_description": "the time in seconds between each update (minimum 15 seconds)",
|
||||
"doubleClickBehavior": "queue all searched tracks when double clicking",
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@ export const controller: GeneralController = {
|
|||
getMusicFolderList(args) {
|
||||
return apiController('getMusicFolderList', args.apiClientProps.server?.type)?.(args);
|
||||
},
|
||||
getAlbumInfo(args) {
|
||||
return apiController('getAlbumInfo', args.apiClientProps.server?.type)?.(args);
|
||||
},
|
||||
getPlaylistDetail(args) {
|
||||
return apiController('getPlaylistDetail', args.apiClientProps.server?.type)?.(args);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -242,6 +242,26 @@ export const NavidromeController: ControllerEndpoint = {
|
|||
apiClientProps.server,
|
||||
);
|
||||
},
|
||||
getAlbumInfo: async (args) => {
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
const albumInfo = await ssApiClient(apiClientProps).getAlbumInfo2({
|
||||
query: {
|
||||
id: query.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (albumInfo.status !== 200) {
|
||||
throw new Error('Failed to get album info');
|
||||
}
|
||||
|
||||
const info = albumInfo.body.albumInfo;
|
||||
|
||||
return {
|
||||
imageUrl: info.largeImageUrl || info.mediumImageUrl || info.smallImageUrl || null,
|
||||
notes: info.notes || null,
|
||||
};
|
||||
},
|
||||
getAlbumList: async (args) => {
|
||||
const { query, apiClientProps } = args;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,14 @@ export const contract = c.router({
|
|||
200: ssType._response.getAlbum,
|
||||
},
|
||||
},
|
||||
getAlbumInfo2: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumInfo2.view',
|
||||
query: ssType._parameters.albumInfo,
|
||||
responses: {
|
||||
200: ssType._response.albumInfo,
|
||||
},
|
||||
},
|
||||
getAlbumList2: {
|
||||
method: 'GET',
|
||||
path: 'getAlbumList2.view',
|
||||
|
|
|
|||
|
|
@ -522,8 +522,24 @@ const getAlbumList2 = z.object({
|
|||
}),
|
||||
});
|
||||
|
||||
const albumInfoParameters = z.object({
|
||||
id: z.string(),
|
||||
});
|
||||
|
||||
const albumInfo = z.object({
|
||||
albumInfo: z.object({
|
||||
largeImageUrl: z.string().optional(),
|
||||
lastFmUrl: z.string().optional(),
|
||||
mediumImageUrl: z.string().optional(),
|
||||
musicBrainzId: z.string().optional(),
|
||||
notes: z.string().optional(),
|
||||
smallImageUrl: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const ssType = {
|
||||
_parameters: {
|
||||
albumInfo: albumInfoParameters,
|
||||
albumList: albumListParameters,
|
||||
artistInfo: artistInfoParameters,
|
||||
authenticate: authenticateParameters,
|
||||
|
|
@ -555,6 +571,7 @@ export const ssType = {
|
|||
album,
|
||||
albumArtist,
|
||||
albumArtistList,
|
||||
albumInfo,
|
||||
albumList,
|
||||
albumListEntry,
|
||||
artistInfo,
|
||||
|
|
|
|||
|
|
@ -465,6 +465,11 @@ export type AlbumDetailQuery = { id: string };
|
|||
|
||||
export type AlbumDetailArgs = { query: AlbumDetailQuery } & BaseEndpointArgs;
|
||||
|
||||
export type AlbumInfo = {
|
||||
imageUrl: string | null;
|
||||
notes: string | null;
|
||||
};
|
||||
|
||||
// Song List
|
||||
export type SongListResponse = BasePaginatedResponse<Song[]> | null | undefined;
|
||||
|
||||
|
|
@ -1246,6 +1251,7 @@ export type ControllerEndpoint = {
|
|||
getAlbumArtistList: (args: AlbumArtistListArgs) => Promise<AlbumArtistListResponse>;
|
||||
getAlbumArtistListCount: (args: AlbumArtistListArgs) => Promise<number>;
|
||||
getAlbumDetail: (args: AlbumDetailArgs) => Promise<AlbumDetailResponse>;
|
||||
getAlbumInfo?: (args: AlbumDetailArgs) => Promise<AlbumInfo>;
|
||||
getAlbumList: (args: AlbumListArgs) => Promise<AlbumListResponse>;
|
||||
getAlbumListCount: (args: AlbumListArgs) => Promise<number>;
|
||||
// getArtistInfo?: (args: any) => void;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import isElectron from 'is-electron';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import {
|
||||
getServerById,
|
||||
useCurrentSong,
|
||||
useCurrentStatus,
|
||||
useDiscordSetttings,
|
||||
|
|
@ -11,6 +12,7 @@ import {
|
|||
import { SetActivity } from '@xhayper/discord-rpc';
|
||||
import { PlayerStatus } from '/@/renderer/types';
|
||||
import { ServerType } from '/@/renderer/api/types';
|
||||
import { controller } from '/@/renderer/api/controller';
|
||||
|
||||
const discordRpc = isElectron() ? window.electron.discordRpc : null;
|
||||
|
||||
|
|
@ -61,16 +63,34 @@ export const useDiscordRpc = () => {
|
|||
activity.smallImageKey = 'paused';
|
||||
}
|
||||
|
||||
if (
|
||||
song?.serverType === ServerType.JELLYFIN &&
|
||||
discordSettings.showServerImage &&
|
||||
song?.imageUrl
|
||||
) {
|
||||
activity.largeImageKey = song?.imageUrl;
|
||||
if (discordSettings.showServerImage && song) {
|
||||
if (song.serverType === ServerType.JELLYFIN && song.imageUrl) {
|
||||
activity.largeImageKey = song.imageUrl;
|
||||
} else if (song.serverType === ServerType.NAVIDROME) {
|
||||
const server = getServerById(song.serverId);
|
||||
|
||||
try {
|
||||
const info = await controller.getAlbumInfo({
|
||||
apiClientProps: { server },
|
||||
query: { id: song.albumId },
|
||||
});
|
||||
|
||||
if (info.imageUrl) {
|
||||
console.log(info.imageUrl);
|
||||
activity.largeImageKey = info.imageUrl;
|
||||
}
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (generalSettings.lastfmApiKey && song?.album && song?.albumArtists.length) {
|
||||
console.log('Fetching album info for', song.album, song.albumArtists[0].name);
|
||||
if (
|
||||
activity.largeImageKey === undefined &&
|
||||
generalSettings.lastfmApiKey &&
|
||||
song?.album &&
|
||||
song?.albumArtists.length
|
||||
) {
|
||||
const albumInfo = await fetch(
|
||||
`https://ws.audioscrobbler.com/2.0/?method=album.getinfo&api_key=${generalSettings.lastfmApiKey}&artist=${encodeURIComponent(song.albumArtists[0].name)}&album=${encodeURIComponent(song.album)}&format=json`,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -147,6 +147,32 @@ export const DiscordSettings = () => {
|
|||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<Switch
|
||||
checked={settings.showServerImage}
|
||||
onChange={(e) => {
|
||||
setSettings({
|
||||
discord: {
|
||||
...settings,
|
||||
showServerImage: e.currentTarget.checked,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
description: t('setting.discordServeImage', {
|
||||
context: 'description',
|
||||
|
||||
discord: 'Discord',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
isHidden: !isElectron(),
|
||||
title: t('setting.discordServeImage', {
|
||||
discord: 'Discord',
|
||||
postProcess: 'sentenceCase',
|
||||
}),
|
||||
},
|
||||
{
|
||||
control: (
|
||||
<TextInput
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue