fix(people): refresh stale person metadata on view

This commit is contained in:
zZebrahz
2026-05-30 02:19:11 +00:00
parent 449fcf394c
commit 1bfbdd2826
2 changed files with 48 additions and 1 deletions
+27
View File
@@ -39,6 +39,8 @@ var personRefreshRate = ratelimit.Rate{
Burst: 10,
}
const personMetadataStaleAfter = 90 * 24 * time.Hour
// PeopleHandler serves person-related API endpoints.
type PeopleHandler struct {
personRepo peopleRepository
@@ -131,6 +133,8 @@ func (h *PeopleHandler) HandleGetPerson(w http.ResponseWriter, r *http.Request)
return
}
h.enqueuePersonRefreshIfDue(*person)
writeJSON(w, http.StatusOK, h.toResponse(r.Context(), *person))
}
@@ -359,6 +363,29 @@ func (h *PeopleHandler) toResponse(ctx context.Context, p models.Person) personR
return resp
}
func (h *PeopleHandler) enqueuePersonRefreshIfDue(person models.Person) {
if h.refreshQueue == nil || !personHasRefreshableProviderID(person) {
return
}
if personMetadataIncomplete(person) {
h.refreshQueue.Enqueue(person.ID)
return
}
if person.UpdatedAt.Before(time.Now().Add(-personMetadataStaleAfter)) {
h.refreshQueue.Enqueue(person.ID)
}
}
func personHasRefreshableProviderID(person models.Person) bool {
return person.TmdbID != "" || person.ImdbID != "" || person.TvdbID != ""
}
func personMetadataIncomplete(person models.Person) bool {
return person.Bio == "" || person.PhotoPath == "" || person.PhotoPath == "-" || person.BirthDate == nil
}
func parseOptionalPersonDate(raw string) (*time.Time, error) {
if raw == "" {
return nil, nil
+21 -1
View File
@@ -4,7 +4,7 @@ import { useQuery } from "@tanstack/react-query";
import { Pencil, RefreshCw } from "lucide-react";
import { getPerson } from "@/api/client";
import { createEmptyQueryDefinition } from "@/api/types";
import { createEmptyQueryDefinition, type Person } from "@/api/types";
import type { CatalogSearchState } from "@/pages/catalogSearchParams";
import EditPersonDialog from "@/components/EditPersonDialog";
import ItemGrid from "@/components/ItemGrid";
@@ -25,6 +25,7 @@ export default function PersonDetail() {
const { id } = useParams<{ id: string }>();
const [typeFilter, setTypeFilter] = useState<TypeFilter>("all");
const [editOpen, setEditOpen] = useState(false);
const autoRefreshWindowRef = useRef<{ personId: number; until: number } | null>(null);
const { user } = useAuth();
const isAdmin = user?.role === "admin";
const refreshMutation = useRefreshPerson(id, isAdmin);
@@ -33,6 +34,21 @@ export default function PersonDetail() {
queryKey: personKeys.detail(id!),
queryFn: () => getPerson(id!),
enabled: !!id,
refetchInterval: (query) => {
const data = query.state.data;
if (!data || !isPersonMetadataIncomplete(data)) {
autoRefreshWindowRef.current = null;
return false;
}
const current = autoRefreshWindowRef.current;
if (!current || current.personId !== data.id) {
autoRefreshWindowRef.current = { personId: data.id, until: Date.now() + 30_000 };
return 3_000;
}
return Date.now() < current.until ? 3_000 : false;
},
});
useDocumentTitle(person?.name ?? "Person");
@@ -212,6 +228,10 @@ export default function PersonDetail() {
);
}
function isPersonMetadataIncomplete(person: Person) {
return !person.bio || !person.photo_url || !person.birth_date;
}
function PersonDetailSkeleton() {
return (
<div className="page-shell pt-8 sm:pt-10">