Skip to content

Commit

Permalink
feat(ui): Implement album art for Jellyfin and Spotify
Browse files Browse the repository at this point in the history
  • Loading branch information
FoxxMD committed Oct 16, 2024
1 parent d63082a commit fa39209
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 7 deletions.
41 changes: 37 additions & 4 deletions src/backend/sources/JellyfinApiSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
// @ts-expect-error weird typings?
SessionInfo,
// @ts-expect-error weird typings?
SortOrder, UserDto, VirtualFolderInfo, CollectionType, CollectionTypeOptions
SortOrder, UserDto, VirtualFolderInfo, CollectionType, CollectionTypeOptions, ImageUrlsApi
} from "@jellyfin/sdk/lib/generated-client/index.js";
import {
// @ts-expect-error weird typings?
Expand All @@ -31,7 +31,9 @@ import {
// @ts-expect-error weird typings?
getApiKeyApi,
// @ts-expect-error weird typings?
getLibraryStructureApi
getLibraryStructureApi,
// @ts-expect-error weird typings?
getImageApi
} from "@jellyfin/sdk/lib/utils/api/index.js";
import dayjs from "dayjs";
import EventEmitter from "events";
Expand All @@ -48,7 +50,7 @@ import {
PlayPlatformId, REPORTED_PLAYER_STATUSES
} from "../common/infrastructure/Atomic.js";
import { JellyApiSourceConfig } from "../common/infrastructure/config/source/jellyfin.js";
import { combinePartsToString, genGroupIdStr, getPlatformIdFromData, parseBool, } from "../utils.js";
import { combinePartsToString, genGroupIdStr, getPlatformIdFromData, joinedUrl, parseBool, } from "../utils.js";
import { parseArrayFromMaybeString } from "../utils/StringUtils.js";
import MemorySource from "./MemorySource.js";
import { PlayerStateOptions } from "./PlayerState/AbstractPlayerState.js";
Expand All @@ -61,6 +63,7 @@ export default class JellyfinApiSource extends MemorySource {

client: Jellyfin
api: Api
imageApi!: ImageUrlsApi
wsClient!: WS;
address!: string;
user!: UserDto
Expand Down Expand Up @@ -174,6 +177,7 @@ export default class JellyfinApiSource extends MemorySource {
const servers = await this.client.discovery.getRecommendedServerCandidates(this.config.data.url);
const best = this.client.discovery.findBestServer(servers);
this.api = this.client.createApi(best.address);
this.imageApi = getImageApi(this.api);
this.address = best.address;
const info = await getSystemApi(this.api).getPublicSystemInfo();
return `Found Server ${info.data.ServerName} (${info.data.Version})`;
Expand Down Expand Up @@ -322,6 +326,35 @@ export default class JellyfinApiSource extends MemorySource {
return true;
}

formatPlayObjAware(obj: BaseItemDto, options: FormatPlayObjectOptions = {}): PlayObject {
const play = JellyfinApiSource.formatPlayObj(obj, options);

const {
ParentId,
AlbumId,
AlbumPrimaryImageTag,
ServerId
} = obj;


if(AlbumId !== undefined && AlbumPrimaryImageTag !== undefined) {
const existingArt = play.meta?.art || {};
existingArt.album = this.imageApi.getItemImageUrlById(AlbumId, undefined, {maxHeight: 500});
play.meta.art = existingArt;
}
if(ParentId !== undefined) {
const u = joinedUrl(new URL(this.address), '/web/#/details')
u.searchParams.append('id', ParentId);
u.searchParams.append('serviceId', ServerId);
play.meta.url = {
...(play.meta?.url || {}),
web: u.toString().replace('%23', '#')
}
}

return play;
}

static formatPlayObj(obj: BaseItemDto, options: FormatPlayObjectOptions = {}): PlayObject {

const {
Expand Down Expand Up @@ -410,7 +443,7 @@ export default class JellyfinApiSource extends MemorySource {

let play: PlayObject | undefined;
if(NowPlayingItem !== undefined) {
const sessionPlay = JellyfinApiSource.formatPlayObj(NowPlayingItem);
const sessionPlay = this.formatPlayObjAware(NowPlayingItem);
play = {
data: {
...sessionPlay.data
Expand Down
24 changes: 21 additions & 3 deletions src/backend/sources/SpotifySource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,11 @@ export default class SpotifySource extends MemorySource {
throw new Error('Could not determine format of spotify response data');
}

const {name: albumName, artists: albumArtists = []} = album || {};
const {
name: albumName,
artists: albumArtists = [],
images = []
} = album || {};

const trackArtistIds = artists.map(x => x.id);
let actualAlbumArtists: ArtistObjectSimplified[] = [];
Expand All @@ -165,7 +169,15 @@ export default class SpotifySource extends MemorySource {
actualAlbumArtists = albumArtists;
}

return {
let imageData: {url: string};
if(images.length > 0) {
imageData = images.find(x => x.height < 640);
if(imageData === undefined) {
imageData = images[0];
}
}

const play: PlayObject = {
data: {
artists: artists.map(x => x.name),
albumArtists: actualAlbumArtists.map(x => x.name),
Expand All @@ -187,6 +199,12 @@ export default class SpotifySource extends MemorySource {
}
}
};

if(imageData !== undefined) {
play.meta.art = {album: imageData.url};
}

return play;
}

buildSpotifyApi = async () => {
Expand Down Expand Up @@ -345,7 +363,7 @@ export default class SpotifySource extends MemorySource {
limit
});
const result = await this.callApi<ReturnType<typeof this.spotifyApi.getMyRecentlyPlayedTracks>>(func);
return result.body.items.map((x: any) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate);
return result.body.items.map((x: PlayHistoryObject) => SpotifySource.formatPlayObj(x)).sort(sortByOldestPlayDate);
}

getUpstreamRecentlyPlayed = async (options: RecentlyPlayedOptions = {}): Promise<PlayObject[]> => {
Expand Down

0 comments on commit fa39209

Please sign in to comment.