diff --git a/apps/web/app/components/search/search-sections.tsx b/apps/web/app/components/search/search-sections.tsx index a3064cf6..14128a00 100644 --- a/apps/web/app/components/search/search-sections.tsx +++ b/apps/web/app/components/search/search-sections.tsx @@ -2,6 +2,7 @@ import type { TmdbCandidate } from "@umt/contracts"; import { ArrowRight, Film, Plus } from "lucide-react"; import { Link, useNavigate } from "react-router"; import { toast } from "sonner"; +import { useEntityFromCandidate } from "~/api/files.ts"; import { useSearch } from "~/api/search.ts"; import { useCreateSubscription } from "~/api/subscriptions.ts"; import { EmptyState, ErrorState, LoadingState } from "~/components/console/empty-state.tsx"; @@ -22,17 +23,24 @@ function SectionTitle({ id, title, extra }: { id: string; title: string; extra?: ); } -/** 从 TMDB 候选建订阅:按标题追踪(建档后自动关联作品) */ +/** 从 TMDB 候选建订阅:先按 TMDB id 建档(季/集与总集数随之就位)再订阅作品;建档失败退回按标题追踪 */ export function useSubscribeTmdb() { const create = useCreateSubscription(); + const entity = useEntityFromCandidate(); const navigate = useNavigate(); return { - pending: create.isPending, - subscribe: (c: TmdbCandidate, onDone?: () => void) => { + pending: create.isPending || entity.isPending, + subscribe: async (c: TmdbCandidate, onDone?: () => void) => { const kind = c.entityType === "movie" ? "movie" : "series"; + const mediaEntityId = + c.localEntityId ?? + (await entity + .mutateAsync({ provider: "tmdb", externalId: c.externalId, kind: c.entityType === "movie" ? "movie" : "tv" }) + .then((r) => r.mediaEntityId) + .catch(() => null)); create.mutate( { - titleQuery: c.year ? `${c.title} ${c.year}` : c.title, + ...(mediaEntityId ? { mediaEntityId } : { titleQuery: c.year ? `${c.title} ${c.year}` : c.title }), mediaKind: kind, mode: kind === "movie" ? "once" : "continuous", seasonScope: kind === "movie" ? "current_season" : "following_seasons", diff --git a/apps/web/app/components/subscriptions/subscription-form.tsx b/apps/web/app/components/subscriptions/subscription-form.tsx index 2e3d10df..b4c2ff6b 100644 --- a/apps/web/app/components/subscriptions/subscription-form.tsx +++ b/apps/web/app/components/subscriptions/subscription-form.tsx @@ -13,6 +13,7 @@ import { ExternalLink, Search, X } from "lucide-react"; import { type ReactNode, useEffect, useId, useMemo, useState } from "react"; import { Link } from "react-router"; import { toast } from "sonner"; +import { useEntityFromCandidate } from "~/api/files.ts"; import { downloaders, useSiteOptions } from "~/api/instances.ts"; import { useEntityDetail, useLibraryRoots } from "~/api/library.ts"; import { usePolicyRevisions } from "~/api/policies.ts"; @@ -141,11 +142,19 @@ function WorkPicker({ onPick, error }: { onPick: (t: Target) => void; error?: st year: h.entity.year, mediaKind: toSubKind(h.entity.kind), }); - const pickTmdb = (c: TmdbCandidate) => - c.localEntityId + const entity = useEntityFromCandidate(); + const pickTmdb = async (c: TmdbCandidate) => { + // 库里没有:先按 TMDB id 建档(季/集与总集数随之就位),失败才退回按标题追踪 + const id = + c.localEntityId ?? + (await entity + .mutateAsync({ provider: "tmdb", externalId: c.externalId, kind: c.entityType === "movie" ? "movie" : "tv" }) + .then((r) => r.mediaEntityId) + .catch(() => null)); + return id ? onPick({ kind: "entity", - id: c.localEntityId, + id, title: c.title, year: c.year, mediaKind: c.entityType === "movie" ? "movie" : "series", @@ -154,8 +163,9 @@ function WorkPicker({ onPick, error }: { onPick: (t: Target) => void; error?: st kind: "title", titleQuery: c.year ? `${c.title} ${c.year}` : c.title, mediaKind: c.entityType === "movie" ? "movie" : "series", - note: `TMDB ${c.externalId},建档后自动关联`, + note: `TMDB ${c.externalId},建档失败,按标题追踪`, }); + }; const row = (key: string, main: ReactNode, sub: string, onClick: () => void) => (