- classify enrich sources: encyclopedia, platform pages (hongguo, kuaikaw, video
platforms), douban (search snippet only) and other sites' search snippets
(snippet mode, pages never opened); one extractor per source, fields carry
source kind and confidence, merged by noisy-OR across hosts
- new cast field (applied as MediaCredit with placeholder ids)
- robots.txt check (cached per host 1 day) and login-wall/captcha give-up
- per-enrichment source attempts + GET /identity/enrich-stats, 30-day hit-rate
table under settings -> metadata -> identity
- setting identity.enrichSnippetAllSites (default on); default domains add
baike.com and kuaikaw.cn
- replay fixtures from the real run on the 4 user-library short dramas
(sanitized Brave responses, hongguo page, robots.txt)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Takeover identify (item 2):
- Adopted tasks without source release / plan: on completion planImport
parses main video path (file + parent + grandparent dirs, like MP
MetaInfoPath) merged with the torrent name, then runs ID-2 matching via
identifyParsed (local recall + TMDB; trigger "takeover" may query TMDB).
auto -> bind/create entity, backfill download_task.media_entity_id and
organize right away; no pool entry, no identity decision.
- Failure / ambiguity: organize_identity decision with evidence and
options: candidates, select entity (local / TMDB), provisional entity,
dismiss.
Decision closure (item 3):
- decision-hooks registry: the decisions resolve route runs the owning
module's executor before closing; errors keep the decision open.
- organize_identity: backfill entity, freeze manual plan, enqueue run.
- organize_blocked with target_exists: replace (verify + trash old) /
keep both (rename "<name> (n)" with sidecars) / skip.
- Successful organize closes both organize_identity and organize_blocked
of the same download task (resolution manual_organize), also for manual
path organizes matched to download tasks via path mappings.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- metadata.includeSpecials (default true): full refresh and the language
backfill fetch TMDB season 0 (specials) when present, like MP; season 0
stays out of the episode-total ledger
- refreshMetadata promotes provisional season/episode units that match the
TMDB season/episode layout to confirmed (source=tmdb), keeping ids and all
references; non-matching units stay provisional
- content_unit.source (migration 0053): release / file / tmdb / user
- duplicate units (same entity/kind/season/episode/content type/segment) are
merged as a refresh step and via `scripts/identity-cleanup.ts
--merge-duplicates` (dry-run by default); keeper = most referenced, then
confirmed, then earliest; references auto-discovered via FKs and repointed
- ensureUnits takes a per-unit advisory xact lock to stop concurrent lazy
creation from producing duplicates
- runbooks updated; user DB read-only dry-run: 征途 S01E09/E10 4 units each
-> 1 each (6 removed), not applied
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Orphan detection now only removes provisional units created by release
parsing that carry no metadata trace (field sources, overrides, title,
air date, total source) and whose entity has no accepted external
identity, or that fall outside the entity's external season/episode
layout. TMDB ledger units are never deleted. Dry-run output lists
deletions and kept units by category; --dry-run is explicit and
mutually exclusive with --apply; audit payload records each orphan.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- metadata.enrich (propose, low, replay safe): normalized-title cache (kv_cache
region enrich, 7 days) -> daily budget -> web.search (title + 短剧 / 电视剧 平台,
second query limited to whitelist via site:) -> web.fetch of up to 3 relevant
whitelisted pages (registered site domains never read, redirects out of the
whitelist dropped) -> upstream model extraction with rule fallback -> server-side
evidence check (rule_confirmed / multi_evidence / needs_review). Suggestion only.
- POST /identity-pool/{id}/enrich (suggest) and /enrich/apply (console session):
provisional entity or same-title merge, season/episode units, field-level
metadata with source web/agent + evidence, aliases, release assignment.
Poster proxy GET /img/enrich/{id} (og:image from whitelisted pages only).
- Auto-enrich once per pool entry when subscription/rule lookups find nothing on
TMDB (identity.autoEnrich, off by default); identity.enrichDailyBudget,
identity.enrichDomains settings; web.fetch now returns og:image.
- Pool sheet "元数据补全" section; entity detail shows evidence links.
- Migration 0049_enrichment (metadata_enrichment, metadata_field.evidence, source
web). Requires image rebuild / api + worker restart to take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
RSS/list releases that match no subscription or local work no longer hit
TMDB or open a pending item; they go to the identity pool as `unmatched`.
TMDB is queried only on a local alias hit, a published new-title rule whose
prefilter passes, manual/Agent retry, search results, or when the new
setting identity.eagerTmdbLookup (default off) is on. Mirrors MoviePilot:
torrents are recognised only when matched to a subscription or searched,
and recognition failures are logged, not alerted.
- Pending items only for related releases (ambiguous after lookup, or
related to a local work but unresolved); same source + normalized title
merge into one decision ("来源「X」的 N 条资源待识别"), resolve/dismiss
applies to the whole group, members detach as they get resolved.
- Pool governance: unmatched reviews capped (daily, 3x), title_alias
insert trigger wakes similar entries, stale unmatched entries archived
after identity.poolArchiveDays (30); review idempotency keys include the
due time.
- Migration 0047_identity_pool: reason/outcome `unmatched`,
identity_candidate.normalized_title (+trgm index), wake trigger, and
cleanup of open identity_unresolved items with no related work
(dismissed, resolution=moved_to_pool).
- Inbox: group by type + work/source with collapse, hide info-only items
by default (minSeverity=warning), badge counts warning+ only; pool sheet
gets manual TMDB/local-work assign and dismiss; settings card for the
new identity settings.
- Runbook docs/runbook/identity.md.
Requires rebuild/restart (migration runs on start).
Co-Authored-By: Claude Opus 5.5 <[email protected]>