feat(server): W2-C rules, downloads, import, organization records, library and dashboard
- Rules API: subscriptions, acquisition slots (revision lock), policy revisions (draft -> publish = automation_grant, rollback as new revision), preference tags with suppression records (agent cannot re-add suppressed fingerprints). - Tasks: evaluateAcquisition (mode/evaluate/budget/cache volume -> frozen plan with slot reservation, candidate reasons as evidence artifacts), submitDownload (withAttemptToken fencing, already_exists adoption), verifyAndImport (frozen organization_record + file results, resumable copy/move/cross-volume steps, assets/locations/bindings in one tx), cleanupDownloadCache (verified+retention+H&R only, assertSafeToDeleteData), reconcileLibrary (root sentinel, suspected -> confirmed missing). - Worker loop: qB sync with tag adoption, no-progress evidence, hard threshold pause / resume-watermark resume, sweepers for deferred plans and imports. - Public organization records API incl. changes cursor (change_xid guard), media library list/detail, inventory overlay, Emby inventory, dashboard. - Contracts: SlotTemplate, AcquisitionPolicyRules, CandidateEvaluation, library/inventory/dashboard read models. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
1cd523d3e6
commit
829ca4f7ac
24 files changed
+7488
-9
No files matched your search
@@ -245,3 +245,54 @@ export type PreferenceSuppressionCreate = z.infer<typeof PreferenceSuppressionCr
|
||||
/** 唯一允许的更新是解除抑制 */
|
||||
export const PreferenceSuppressionUpdate = z.object({ active: z.literal(false), reason: z.string().optional() });
|
||||
export type PreferenceSuppressionUpdate = z.infer<typeof PreferenceSuppressionUpdate>;
|
||||
|
||||
// ── 收录策略规则与候选评估(W2-C) ──
|
||||
|
||||
/** Slot 模板:策略规则里用于自动建 Slot(新作品收录 / 订阅缺省 Slot);字段同 AcquisitionSlot 规则列 */
|
||||
export const SlotTemplate = z.object({
|
||||
name: z.string().min(1).max(64),
|
||||
hardConditions: JsonObject.optional(),
|
||||
softPreferences: JsonObject.optional(),
|
||||
fallback: JsonObject.optional(),
|
||||
upgradeThreshold: JsonObject.optional(),
|
||||
completion: JsonObject.optional(),
|
||||
});
|
||||
export type SlotTemplate = z.infer<typeof SlotTemplate>;
|
||||
|
||||
/**
|
||||
* kind=acquisition 的 policy_revision.rules 结构(结构化数据,不是可执行脚本)。
|
||||
* 发布即对 scope 范围内的正常下载/追更/整理生成持续授权 automation_grant(REQ §6.4)。
|
||||
*/
|
||||
export const AcquisitionPolicyRules = z.object({
|
||||
/** 新作品收录:发行/首播在窗口内且库里没有的作品,按模板建订阅与 Slot */
|
||||
newTitle: z
|
||||
.object({
|
||||
enabled: z.boolean(),
|
||||
windowDays: z.number().int().min(1).max(3650).optional(),
|
||||
mediaKinds: z.array(SubscriptionMediaKind).optional(),
|
||||
slots: z.array(SlotTemplate),
|
||||
})
|
||||
.optional(),
|
||||
/** 订阅没有 Slot 时套用的缺省 Slot */
|
||||
defaultSlots: z.array(SlotTemplate).optional(),
|
||||
/** 整理方式覆盖(缺省读 settings.organize.defaultMode) */
|
||||
organizeMode: z.enum(["copy", "move"]).optional(),
|
||||
/** Move 在 H&R 未满足时经授权改用 Copy */
|
||||
allowCopyWhenHnrPending: z.boolean().optional(),
|
||||
});
|
||||
export type AcquisitionPolicyRules = z.infer<typeof AcquisitionPolicyRules>;
|
||||
|
||||
/** 候选对某个 Slot 的确定性评估(供作品详情"为什么选/不选") */
|
||||
export const CandidateEvaluation = z.object({
|
||||
slotId: Id,
|
||||
sourceReleaseId: Id,
|
||||
title: z.string(),
|
||||
/** accept / upgrade / reject / unknown_field / skipped(被模式、预算、阈值、总开关等挡住) */
|
||||
decision: z.enum(["accept", "upgrade", "reject", "unknown_field", "skipped"]),
|
||||
mode: z.enum(["new_title", "catch_up", "upgrade"]).nullable(),
|
||||
rejected: z.array(z.object({ code: z.string(), message: z.string() })),
|
||||
reasons: z.array(z.string()),
|
||||
planId: Id.nullable(),
|
||||
evaluatedAt: Timestamp,
|
||||
});
|
||||
export type CandidateEvaluation = z.infer<typeof CandidateEvaluation>;
|
||||
Reference in new issue
Block a user