feat(web): MP-style media detail with seasons and episodes, shared by library and TMDB preview (MD-1)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:28:16 +08:00
1 parent 9cd5a28943
commit 97ac59c7ac
9 files changed
+1250 -190

No files matched your search

+34 -2
View File
@@ -10,6 +10,7 @@ import {
MediaEntity,
MediaEntityDetail,
type MediaKind,
type UnitMetadataField,
} from "@umt/contracts";
import type { z } from "zod";
import { ApiRequestError, api, type QueryParams } from "~/lib/api.ts";
@@ -144,11 +145,42 @@ export function useUnlockField(id: string) {
});
}
/** 刷新元数据(字段级;用户锁优先) */
/**
* 刷新元数据(字段级;用户锁优先)。full = 拉全部季的每集信息。刷新是后台任务:
* 提交后隔几秒再取一次详情,拿到任务写回的结果。
*/
export function useRefreshMetadata(id: string) {
const done = useInvalidateEntity(id);
return useMutation({
mutationFn: () => api.post<unknown>(`/media-entities/${id}/refresh`),
mutationFn: (opts?: { full?: boolean }) =>
api.post<unknown>(`/media-entities/${id}/refresh${opts?.full ? "?full=1" : ""}`),
onSuccess: () => {
done();
for (const ms of [3_000, 8_000, 20_000]) setTimeout(done, ms);
},
});
}
/** 季 / 集字段覆盖并锁定(MD-1) */
export function useLockUnitField(entityId: string) {
const done = useInvalidateEntity(entityId);
return useMutation({
mutationFn: (v: { unitId: string; field: UnitMetadataField; value: unknown; reason?: string }) =>
api.put<unknown>(`/content-units/${v.unitId}/fields/${v.field}`, {
value: v.value,
lock: true,
reason: v.reason,
}),
onSuccess: done,
});
}
/** 取消季 / 集字段覆盖与锁定 */
export function useUnlockUnitField(entityId: string) {
const done = useInvalidateEntity(entityId);
return useMutation({
mutationFn: (v: { unitId: string; field: UnitMetadataField }) =>
api.del<unknown>(`/content-units/${v.unitId}/fields/${v.field}`),
onSuccess: done,
});
}