fix(plex): use series title for media releases instead of season label (#6083)
This commit is contained in:
1 parent
915c8a3501
commit
2443b89b46
2 files changed
+151
-2
No files matched your search
@@ -127,11 +127,18 @@ export class PlexIntegration extends Integration implements IMediaServerIntegrat
|
||||
|
||||
const media =
|
||||
data.MediaContainer.Metadata?.filter((item) => item.Image).map((item) => {
|
||||
const title =
|
||||
item.type === "episode"
|
||||
? (item.grandparentTitle ?? item.title)
|
||||
: item.type === "season"
|
||||
? (item.parentTitle ?? item.title)
|
||||
: item.title;
|
||||
|
||||
return {
|
||||
id: item.Media?.at(0)?.id.toString() ?? item.key,
|
||||
type: mapType(item.type),
|
||||
title: item.title,
|
||||
subtitle: item.tagline,
|
||||
title,
|
||||
subtitle: title === item.title ? item.tagline : item.title,
|
||||
description: item.summary,
|
||||
releaseDate: item.originallyAvailableAt
|
||||
? new Date(item.originallyAvailableAt)
|
||||
@@ -222,6 +229,8 @@ const recentlyAddedSchema = z.object({
|
||||
studio: z.string().optional(),
|
||||
type: z.string(), // For example "movie", "album"
|
||||
title: z.string(),
|
||||
parentTitle: z.string().optional(),
|
||||
grandparentTitle: z.string().optional(),
|
||||
summary: z.string().optional(),
|
||||
duration: z.number().optional(),
|
||||
addedAt: z.number(),
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
// @vitest-environment node
|
||||
import { Response } from "undici";
|
||||
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import { fetchWithTrustedCertificatesAsync } from "@homarr/core/infrastructure/http";
|
||||
|
||||
import type { IntegrationSecret } from "../../base/types";
|
||||
import { PlexIntegration } from "../plex-integration";
|
||||
|
||||
const imageProxyMocks = vi.hoisted(() => ({
|
||||
createImageAsync: vi.fn((url: string) => Promise.resolve(`proxied:${url}`)),
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/core/infrastructure/http", () => ({
|
||||
fetchWithTrustedCertificatesAsync: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@homarr/image-proxy", () => ({
|
||||
ImageProxy: class {
|
||||
createImageAsync = imageProxyMocks.createImageAsync;
|
||||
},
|
||||
}));
|
||||
|
||||
const TEST_URL = "https://plex.example.com";
|
||||
const mockFetch = vi.mocked(fetchWithTrustedCertificatesAsync);
|
||||
|
||||
const secrets: IntegrationSecret[] = [{ kind: "apiKey", value: "plex-token" }];
|
||||
|
||||
const createIntegration = () =>
|
||||
new PlexIntegration({
|
||||
id: "test-plex",
|
||||
name: "Test Plex",
|
||||
url: TEST_URL,
|
||||
externalUrl: null,
|
||||
decryptedSecrets: secrets,
|
||||
});
|
||||
|
||||
const createMetadataItem = (key: string, overrides: Record<string, unknown>) => ({
|
||||
key: `/library/metadata/${key}`,
|
||||
type: "movie",
|
||||
title: "Movie",
|
||||
addedAt: 1_735_689_600,
|
||||
Image: [
|
||||
{
|
||||
type: "coverPoster",
|
||||
url: `/library/metadata/${key}/thumb`,
|
||||
},
|
||||
],
|
||||
...overrides,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetch.mockReset();
|
||||
imageProxyMocks.createImageAsync.mockClear();
|
||||
});
|
||||
|
||||
describe("PlexIntegration.getMediaReleasesAsync", () => {
|
||||
test("uses the series title for TV seasons and episodes", async () => {
|
||||
mockFetch.mockImplementation((url) => {
|
||||
const urlString = String(url);
|
||||
|
||||
if (urlString.endsWith("/identity")) {
|
||||
return Promise.resolve(
|
||||
new Response(JSON.stringify({ MediaContainer: { machineIdentifier: "server-id" } }), { status: 200 }),
|
||||
) as unknown as ReturnType<typeof fetchWithTrustedCertificatesAsync>;
|
||||
}
|
||||
|
||||
if (urlString.endsWith("/library/recentlyAdded")) {
|
||||
return Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
MediaContainer: {
|
||||
Metadata: [
|
||||
createMetadataItem("season", {
|
||||
type: "season",
|
||||
title: "Season 1",
|
||||
parentTitle: "Breaking Bad",
|
||||
}),
|
||||
createMetadataItem("episode", {
|
||||
type: "episode",
|
||||
title: "Review",
|
||||
parentTitle: "Season 1",
|
||||
grandparentTitle: "The Bear",
|
||||
}),
|
||||
createMetadataItem("movie", {
|
||||
type: "movie",
|
||||
title: "Inception",
|
||||
tagline: "Dreams feel real",
|
||||
}),
|
||||
createMetadataItem("show", {
|
||||
type: "show",
|
||||
title: "The Last of Us",
|
||||
tagline: "A series tagline",
|
||||
}),
|
||||
createMetadataItem("missing-parent", {
|
||||
type: "season",
|
||||
title: "Season 3",
|
||||
}),
|
||||
],
|
||||
},
|
||||
}),
|
||||
{ status: 200 },
|
||||
),
|
||||
) as unknown as ReturnType<typeof fetchWithTrustedCertificatesAsync>;
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected Plex request: ${urlString}`);
|
||||
});
|
||||
|
||||
const releases = await createIntegration().getMediaReleasesAsync();
|
||||
|
||||
expect(releases).toMatchObject([
|
||||
{
|
||||
title: "Breaking Bad",
|
||||
subtitle: "Season 1",
|
||||
type: "tv",
|
||||
},
|
||||
{
|
||||
title: "The Bear",
|
||||
subtitle: "Review",
|
||||
type: "tv",
|
||||
},
|
||||
{
|
||||
title: "Inception",
|
||||
subtitle: "Dreams feel real",
|
||||
type: "movie",
|
||||
},
|
||||
{
|
||||
title: "The Last of Us",
|
||||
subtitle: "A series tagline",
|
||||
type: "tv",
|
||||
},
|
||||
{
|
||||
title: "Season 3",
|
||||
subtitle: undefined,
|
||||
type: "tv",
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user