perf(plex): a card fetches its picture at a card's size, not the detail page's
Cards fetched the full cuts: a 400x600 poster (~38 KB) for a ~144px picture and the 1280x720 backdrop (~162 KB) for a ~212px shelf picture. The image proxy now also cuts thumb_sm (240x360) and art_sm (480x270), cached apart; card pictures (grid, shelves, collection tiles, Up next) carry both in srcset with sizes=auto, so the browser picks by laid-out width and DPR. Localdev: 17 KB a poster, 56 KB a backdrop. The detail page keeps the full cuts.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { cardImage } from "./plexImage";
|
||||
|
||||
describe("cardImage", () => {
|
||||
it("offers a poster's small cut beside the full one", () => {
|
||||
expect(cardImage("/api/plex/image/123")).toEqual({
|
||||
src: "/api/plex/image/123",
|
||||
srcSet: "/api/plex/image/123?variant=thumb_sm 240w, /api/plex/image/123 400w",
|
||||
sizes: "auto, 200px",
|
||||
});
|
||||
});
|
||||
|
||||
it("offers a backdrop's small cut beside the full one", () => {
|
||||
expect(cardImage("/api/plex/image/123?variant=art").srcSet).toBe(
|
||||
"/api/plex/image/123?variant=art_sm 480w, /api/plex/image/123?variant=art 1280w",
|
||||
);
|
||||
});
|
||||
|
||||
it("leaves any other picture alone", () => {
|
||||
expect(cardImage("https://image.tmdb.org/x.jpg")).toEqual({
|
||||
src: "https://image.tmdb.org/x.jpg",
|
||||
});
|
||||
expect(cardImage("")).toEqual({ src: "" });
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
// A Plex picture at a card's size (E31 S4e). The image proxy cuts each picture twice (backend
|
||||
// `app/plex/imgcache.py` SIZES): the full one a detail page shows and a card's `_sm` one. Cards fetched
|
||||
// the full cut — a 400px poster for a ~144px picture, the 1280px backdrop for a ~212px shelf picture.
|
||||
// A card now hands the browser both and lets it pick by the laid-out width: `sizes="auto"`, which the
|
||||
// browser honours on a LAZY image only; the 200px after it is the guess for a browser without it.
|
||||
|
||||
// The cut widths, as imgcache.SIZES has them: [small, full].
|
||||
const WIDTHS = { thumb: [240, 400], art: [480, 1280] } as const;
|
||||
const PROXY = /^\/api\/plex\/image\/([^?]+)(?:\?variant=(art))?$/;
|
||||
|
||||
export type CardImage = { src: string; srcSet?: string; sizes?: string };
|
||||
|
||||
/** `src` + `srcSet` + `sizes` for a lazy card picture; a URL that is not the Plex proxy's is left as it is. */
|
||||
export function cardImage(url: string): CardImage {
|
||||
const m = PROXY.exec(url);
|
||||
if (!m) return { src: url };
|
||||
const kind = m[2] === "art" ? "art" : "thumb";
|
||||
const [small, full] = WIDTHS[kind];
|
||||
// srcSet and sizes ahead of src: spread onto an <img>, the attributes land in this order.
|
||||
return {
|
||||
srcSet: `/api/plex/image/${m[1]}?variant=${kind}_sm ${small}w, ${url} ${full}w`,
|
||||
sizes: "auto, 200px",
|
||||
src: url,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user