perf(plex): batch bulk pushes, cache show enrichment; remove dead code + DRY (F6/F8/F10)
F6: coalesce whole-show/season Plex watch-pushes into ONE background task (push_bulk_state_to_plex) that reuses a single DB session + keep-alive Plex client, instead of scheduling one task (own session + HTTP client) per episode. F8: cache a show's live Plex enrichment (metadata + related) per rating_key for a short TTL and fetch the two calls in parallel; repeat opens skip the network. Raw payloads are cached; per-user shaping stays out. An empty related list is not cached (plex.related swallows a transient failure as [] — don't pin it for the TTL). F10: remove dead code — the pre-unified /browse route + browse(), the /people route + people() + _person_photo(), api.plexPeople, interface PlexPerson, and the orphaned plex.people i18n block. DRY: appendPlexFilters() shared by plexLibrary + plexFacets; one exported plexDetailUi.Filterable (was Fil + Filterable); PlexInfo migrated onto the shared plexDetailUi hooks/menu (DetailCustomizeMenu gained overlay + extra props; useDetailPrefs exposes savePref; PrefToggle exported). Reviewed (high) → clean. F7 (facet aggregate collapse) deferred: self-exclusion gives each sub-aggregate a distinct WHERE, and no measurement shows /facets slow.
This commit is contained in:
parent
9f8e410033
commit
b3af04a997
9 changed files with 180 additions and 450 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { lazy, Suspense, useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
|
||||
import { lazy, Suspense, useEffect, useLayoutEffect, useRef, useState, type CSSProperties } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useInfiniteQuery, useQuery, useQueryClient, type InfiniteData } from "@tanstack/react-query";
|
||||
import {
|
||||
|
|
@ -28,7 +28,7 @@ import {
|
|||
} from "../lib/api";
|
||||
import { useDebounced } from "../lib/useDebounced";
|
||||
import { useHistorySubview } from "../lib/history";
|
||||
import { DetailCustomizeMenu, useArtBackdrop, useDetailPrefs } from "../lib/plexDetailUi";
|
||||
import { DetailCustomizeMenu, Filterable, useArtBackdrop, useDetailPrefs } from "../lib/plexDetailUi";
|
||||
import PlexPlaylistAdd, { type PlexAddTarget } from "./PlexPlaylistAdd";
|
||||
import PlexCollectionEditor from "./PlexCollectionEditor";
|
||||
|
||||
|
|
@ -553,16 +553,6 @@ function epLabel(ep?: PlexCard | null): string | undefined {
|
|||
return ep.title;
|
||||
}
|
||||
|
||||
// A metadata value that filters the unified grid when clicked (if onClick is wired), else plain text.
|
||||
function Fil({ onClick, className, children }: { onClick?: () => void; className?: string; children: ReactNode }) {
|
||||
if (!onClick) return <span className={className}>{children}</span>;
|
||||
return (
|
||||
<button onClick={onClick} className={`${className ?? ""} cursor-pointer hover:text-accent transition`}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
function BackBtn({ onBack, label }: { onBack: () => void; label: string }) {
|
||||
return (
|
||||
<button
|
||||
|
|
@ -967,14 +957,14 @@ function PlexShowView({
|
|||
<h1 className="text-2xl font-semibold leading-tight">{show.title}</h1>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-0.5 text-sm text-muted">
|
||||
{show.year != null && (
|
||||
<Fil onClick={onFilter ? () => onFilter({ yearMin: show.year, yearMax: show.year }) : undefined}>
|
||||
<Filterable onClick={onFilter ? () => onFilter({ yearMin: show.year, yearMax: show.year }) : undefined}>
|
||||
{show.year}
|
||||
</Fil>
|
||||
</Filterable>
|
||||
)}
|
||||
{show.content_rating && (
|
||||
<Fil onClick={onFilter ? () => onFilter({ contentRatings: [show.content_rating!] }) : undefined}>
|
||||
<Filterable onClick={onFilter ? () => onFilter({ contentRatings: [show.content_rating!] }) : undefined}>
|
||||
· {show.content_rating}
|
||||
</Fil>
|
||||
</Filterable>
|
||||
)}
|
||||
{show.season_count != null && <span>· {t("plex.seasons", { count: show.season_count })}</span>}
|
||||
{show.imdb_rating != null && (
|
||||
|
|
@ -990,13 +980,13 @@ function PlexShowView({
|
|||
{show.genres.length > 0 && (
|
||||
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||
{show.genres.map((g) => (
|
||||
<Fil
|
||||
<Filterable
|
||||
key={g}
|
||||
className="text-[11px] rounded-full bg-surface px-2 py-0.5 text-muted"
|
||||
onClick={onFilter ? () => onFilter({ genres: [g] }) : undefined}
|
||||
>
|
||||
{g}
|
||||
</Fil>
|
||||
</Filterable>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue