feat(contracts,server,web): files G6–G8 fields and identify S-7 contract
- G6: FileRoot.pendingCount/pendingPartial for download roots (same rule as the unidentified filter, recursive, cached 5 min, invalidated by files.changed) - G7: FileEntry.parsedHint from parseReleasePath (file + parent dirs); the web subline uses it and no longer parses names itself - G8: FileIdentifyCandidate.posterUrl (TMDB poster_path via adapter and catalog.lookup, local entities from metadata.posterPath); thumbnails in the identify panel - S-7 contract: FilesIdentifyResult.remoteTaskId/remotePending, MediaServerInventory.partial, EventType identify.completed with IdentifyCompletedPayload; identify panel (useIdentifyLive) and organize wizard merge remote results by root + path, buffering events that beat the response Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fd2f7bc33d
commit
abc69886ea
26 files changed
+510
-134
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { EventType } from "@umt/contracts";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { type FilesIdentifyResult, type IdentifyCompletedPayload, useIdentify } from "~/api/files.ts";
|
||||
import { useSse } from "~/lib/sse.ts";
|
||||
import { applyIdentifyCompleted } from "./model.ts";
|
||||
|
||||
/**
|
||||
* useIdentify + 远端结果回填(S-7):响应带 remoteTaskId 时,用 SSE identify.completed 里同一 taskId 的完整结果
|
||||
* 按 root + path 替换条目。事件可能比 HTTP 响应先到,所以挂载期间一直订阅,并缓存最近几条。
|
||||
* data 是回填后的结果;remotePending 表示还在等远端。
|
||||
*/
|
||||
export function useIdentifyLive() {
|
||||
const m = useIdentify();
|
||||
const [data, setData] = useState<FilesIdentifyResult | undefined>(undefined);
|
||||
const early = useRef(new Map<string, IdentifyCompletedPayload>());
|
||||
useEffect(() => {
|
||||
const d = m.data;
|
||||
const hit = d?.remoteTaskId ? early.current.get(d.remoteTaskId) : undefined;
|
||||
setData(d && hit ? applyIdentifyCompleted(d, hit) : d);
|
||||
}, [m.data]);
|
||||
useSse([EventType.identifyCompleted], (ev) => {
|
||||
if (ev.type !== EventType.identifyCompleted) return;
|
||||
const p = ev.payload as IdentifyCompletedPayload;
|
||||
early.current.set(p.taskId, p);
|
||||
if (early.current.size > 20) early.current.delete(early.current.keys().next().value as string);
|
||||
setData((d) => (d ? applyIdentifyCompleted(d, p) : d));
|
||||
});
|
||||
return { ...m, data, remotePending: !!data?.remoteTaskId };
|
||||
}
|
||||
Reference in new issue
Block a user