Fetch the detail page (cached, rate-limited, per-site hourly cap
identity.detailIdsPerSiteHour) for search results, subscription-related
candidates, pool enrich and manual re-identify; resolve IMDb/TMDB via TMDB,
douban via cached douban subjects (site detail info block / META-2 snippets).
No identity_unresolved decisions for releases with site IDs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- 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]>
- adapters: overviewLanguages() limits overview/tagline chain to Chinese
variants (zh-CN→zh-SG→zh-TW→zh-HK); titles keep full chain; season
supplement fetch skips languages useless for overview
- setting metadata.overviewNonChineseFallback (default off) + UI switch
- media detail shows "暂无简介" for empty entity/episode overview
- scripts/overview-recompute.ts: recompute stored non-Chinese overviews
from kv_cache only (no TMDB requests), dry-run by default, --apply
- tests: chain unit tests, Heart Signal (103147) fixture, server
recompute test, MP differential for episode overview
- runbook + CHANGELOG
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- every outbound site request (adapter list/search/detail/download/health,
RSS, login, user data) carries the account UA via SourcesRuntime.siteFetch;
no more Deno default UA
- priority: account manual > pending > global setting > browser identity
(fallback: same-shape Windows Firefox UA); settings UA default is empty
- migration 0056: existing cookie/password accounts keep their old UA as
ua_pending until re-login or browser cookie write-back switches cookie+UA
in one transaction; browser_provider.identity caches the browser identity
- cf_clearance/__cf_bm stripped on browser write-back (names kept only)
- browser sessions get per-site egress (same proxy as HTTP, or direct)
- site account page shows UA source; raw UA in tech info
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Waiting plans (approval / disk space / automation off / queued) are
active-segment rows on /download-tasks/board, merged by created_at,
counted, with approve / cancel actions; ?plan= deep links and legacy
/downloads/plans routes redirect to the panel
- Download detail shows "why this download" (source, purpose, replaced
version, reasons); raw evaluation in tech info
- Failed / cancelled / expired plans appear in the entity download
history; rejected / revoked / expired approvals close draft plans
- New GET /acquisition-plans/{id}/why, POST /acquisition-plans/{id}/cancel
- User-facing copy no longer says 下载计划; glossary updated
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- compose: app gets a default NO_PROXY (UMT_NO_PROXY) listing compose
service names incl. qbittorrent / qbittorrent-nox / qb / transmission /
deluge / aria2, so a proxy override never swallows downloader traffic.
- runbook: proxy and test-qB override templates use the same list.
- Settings: one-line hint on library root "管理方式" (managed vs
register_only).
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]>
- Section header (h2 + description) and section blocks as h3; unified
block padding and title size
- Global settings forms use label-left / control-right rows, stacked
below md; control width fixed by type (number short, text/select
medium, wide/textarea full)
- Per-block SaveBar pinned at the block end with dirty hint; naming
template saves moved from the header to the bar
- Console shell uses overflow-x-clip so the sticky header and sticky
section nav actually stick; anchor jumps re-align while async content
loads and land below the header
- Section content uses a single minmax(0,1fr) column so wide tables
scroll inside their card instead of widening the page
- useSettings fills missing keys from DEFAULT_SETTINGS so an older
backend no longer crashes the metadata section
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Re-lands 901b01d, which was reverted wholesale by 28974d1 (BR-2 committed
from the shared index). The agent UI files 901b01d had rolled back are
left at their AGUI-2 versions; contracts, routes, db types and the runbook
part already reached main through later commits, so this carries the rest.
HHClub (憨憨) was checked read-only against a real account (one detail
request; list, user data and health came from the product's own fetch).
Parsing fixes:
- list titles carried the site's new-torrent marker "[新]" (58 of 100
rows; the detail page title has none); stripped with a replace filter
- refreshing a detail overwrote seeders / leechers / snatched / category
name / H&R with null because the HHClub div detail page lacks them;
detail values now fall back to the list snapshot (mergeDetailOverList),
and an unknown detail promotion keeps the list promotion
- level "贵宾" is VIP (umt.vipLevels)
- sanitized minimal fixture HHANCLUB_LIST_PAGE_2026 + tests; README marks
HHClub as verified 2026-09-29
Size, counts, category ids/names, +08:00 time and subtitle were correct.
Request restraint:
- site definitions may carry umt.defaults; merged with settings.sites
(longer intervals, fewer pages): new settings defaultSnapshotMaxPages 1,
defaultHealthCheckIntervalSec 6 h, defaultDetailCacheSec 24 h; default
burst 2. HHClub: snapshot 60 min page 1 only, health 6 h, search
concurrency 1, detail cache 24 h, 1 token / 3 s burst 2
- periodic / manual snapshots are capped to that page count; scheduled
health checks skip sites checked within the interval; detail refresh is
served from cache inside the window; live searches on one site queue
- every rate-limiter token is counted per site per hour
(site_request_count, migration 0046); GET /sites/{id}/request-stats;
site overview and resource list show requests in the last 24 hours
Resource list: two-layer rows. Main line: title + spec tags (episodes,
resolution, source, codec, HDR, group, 国语/中字, site tags) + promotion
with time left + H&R. Second line: subtitle (one line, ellipsis) and
fixed-width category / size / seed-leech-snatched / upload time
(relative, absolute on hover) columns, stale badge, inventory overlay;
folds into a card on phones.
Also aligns identity_candidate.normalized_title order in db types with
the migrations (schema drift test).
Takes effect after the stack is rebuilt (migration 0046).
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]>
- Inject the site account Cookie (manual + auto-login session, session wins)
into the camofox profile before the first tab opens; password accounts
without a session run refresh-login first; API Key sites are skipped.
- Cookie expired after injection -> credential.status=expired; cookie
accounts go to waiting/login_required ("log in once, applies site-wide").
- Write back site-domain cookies from the browser on handover, session close,
every 10 min of activity, and from refresh-login when the stored cookie is
invalid (verified before saving). user_info.cookieOrigin records
source=browser; source shown as manual/login/browser in the origin view.
- New camofox plugin umt-cookies (GET/POST /umt/sessions/:userId/cookies
under the access key); profile dir 0700/files 0600 in the desktop image.
- Single entry: sources-identity/service/credentials.ts.
- Real check: HHClub cookie injected into a fresh profile -> home page
logged in (logout link present), 5 cookies read back.
Requires rebuilding umt-browser-desktop (docker compose build camofox &&
docker compose up -d camofox) to take effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
HHClub (憨憨) was checked read-only against a real account (one detail
request; list, user data and health came from the product's own fetch).
Parsing fixes:
- list titles carried the site's new-torrent marker "[新]" (58 of 100
rows; the detail page title has none); stripped with a replace filter
- refreshing a detail overwrote seeders / leechers / snatched / category
name / H&R with null because the HHClub div detail page lacks them;
detail values now fall back to the list snapshot (mergeDetailOverList),
and an unknown detail promotion keeps the list promotion
- level "贵宾" is VIP (umt.vipLevels)
- sanitized minimal fixture HHANCLUB_LIST_PAGE_2026 + tests; README marks
HHClub as verified 2026-09-29
Size, counts, category ids/names, +08:00 time and subtitle were correct.
Request restraint:
- site definitions may carry umt.defaults; merged with settings.sites
(longer intervals, fewer pages): new settings defaultSnapshotMaxPages 1,
defaultHealthCheckIntervalSec 6 h, defaultDetailCacheSec 24 h; default
burst 2. HHClub: snapshot 60 min page 1 only, health 6 h, search
concurrency 1, detail cache 24 h, 1 token / 3 s burst 2
- periodic / manual snapshots are capped to that page count; scheduled
health checks skip sites checked within the interval; detail refresh is
served from cache inside the window; live searches on one site queue
- every rate-limiter token is counted per site per hour
(site_request_count, migration 0046); GET /sites/{id}/request-stats;
site overview and resource list show requests in the last 24 hours
Resource list: two-layer rows. Main line: title + spec tags (episodes,
resolution, source, codec, HDR, group, 国语/中字, site tags) + promotion
with time left + H&R. Second line: subtitle (one line, ellipsis) and
fixed-width category / size / seed-leech-snatched / upload time
(relative, absolute on hover) columns, stale badge, inventory overlay;
folds into a card on phones.
Also aligns identity_candidate.normalized_title order in db types with
the migrations (schema drift test).
Takes effect after the stack is rebuilt (migration 0046).
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]>
- POST /pi/upstreams/{id}/models/refresh calls the provider's model list
endpoint (OpenAI-shaped /models, Anthropic /v1/models, Google
/v1beta/models, OpenRouter /api/v1/models; Radius via pi-ai refresh),
normalizes {id, displayName, contextWindow, supportsVision,
supportsTools, pricing} and stores it in pi_upstream.models (0044)
- GET .../models returns {source, fetchedAt, models[]}: remote merged
with the static catalog (remote first); failures (401/network/
unsupported) fall back to static with a one-line error
- POST /pi/models/probe for trying credentials before creating
- wizard and edit drawer: fetch button, searchable dropdown with
vision/tool tags, manual id, source line; default upstream needs a model
- tests: mock /models, 401, network failure; smoke step; runbook
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- compose: camofox service on by default (internal only, volume umt-camofox,
healthcheck, CAMOFOX_REPLICAS=0 to skip); app depends on it with
service_started/required=false and auto-registers camofox-env from
BROWSER_PROVIDER_URL / BROWSER_ACCESS_KEY / BROWSER_ENABLED
- docker/camofox/Dockerfile: builds upstream tag v1.17.0 (389c996); fixes the
upstream Dockerfile (missing postinstall.js before npm ci, sh -c not
forwarding SIGTERM so logins were never persisted on restart)
- adapter fixes from the real container: coordinate click via evaluate +
selector, focus typing with mode=keyboard, password values redacted from
ARIA snapshots/extract, downloads field names, 410 after browser restart
treated as lost tab; navigate reopens a tab in the same session
- verified-capability table keyed by upstream version (1.17.0 all ten);
other versions stay unverified; probe reports auth failures
- settings: browser providers block (create/edit/delete/probe, capability
table); API POST/PATCH/DELETE /browser/providers, POST .../probe
- scripts/browser-verify: umtbr stack + mock NexusPHP run of REQ 4.7.4 items
- docs: runbook browser §1/§7 (real-instance record), deploy §2.1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GET /img/tmdb/{size}/{file} ported from MP ImageHelper / fetch_image
(sanitized cache path under DATA_DIR/cache/images, GLOBAL_IMAGE_CACHE
read switch, md5 ETag / 304, day-based cleanup job). Cache module adds
cache.sweep / cache.images_clean jobs and /cache/tmdb, /cache/images
list and clear endpoints. Library list/detail and the search palette
show posters with the neutral placeholder as fallback; metadata settings
get TMDB cache and image cache cards. Runbook docs/runbook/cache.md and
porting notes docs/porting/moviepilot-tmdb-cache.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Auto-fix of 602 string literals across 74 files (half-width punctuation next to
Chinese, parenthesised Chinese, quotes); biome reflow; e2/e6 assertions follow
the new full-width summaries. check:copy now runs with --strict in ci.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- session page shows the agent.plan TodoList (drawer: one-line progress); options and approval
cards sit at their step; composer has a "先看方案再执行" toggle instead of the mode switch
- settings expose option timeout, timeout action and approval validity; quick goals follow glossary
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Settings > Agent: MCP server card (endpoint, enable switch, allowed API keys,
live sessions, recent calls, copyable client config without secrets) and the
capability list (scope, risk, idempotency/replay, authorization state, 7-day
calls and failure rate, last call, schema sheet). Agent session tool cards get
a drawer with input/output schema and the redacted input/output of the call.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Account & security: TOTP setup renders the otpauth URL as a QR code
(qr-code-styling) with the secret as a fallback, shows the 8 recovery
codes once after enabling (copy / download), and offers regenerate and
disable (verification code or recovery code) when enabled.
- New Passkey block: add (named, @simplewebauthn/browser), rename, delete;
hidden outside a secure context.
- Login page: "用 Passkey 登录" when a passkey is registered and the
browser supports it; the TOTP step can switch to a recovery code.
- Account menu links to the settings block instead of its own TOTP
dialog, so enabling always shows the recovery codes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- glossary: 10 punctuation rules (full-width in Chinese, CJK/Latin spacing,
full-width parens around Chinese, single ellipsis, 「」 quotes, period rules)
- scripts/check-copy-punctuation.mjs: checks string literals, templates and JSX
text (not comments, types, keys, match/compare strings); --fix, --strict,
--by-file; wired into deno task ci as check:copy in warn mode
- --fix applied to web, server and contracts; files other lanes were editing
(subscriptions, sites, browser, agent, auth/account, inventory) left for them
- tests updated for the changed messages
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- U4: form save failures toast the first problem and scroll/focus the first
invalid field (policy draft, settings cards, resource drawers, subscription
form) via lib/form-errors.
- U5: FormField keeps 8px between a hint and the next label.
- U6: file manager status bar keeps free space left-aligned, clear of the
Agent button.
- U13: move/copy dialog stops "target is the source folder / inside the
source" before requesting a preview (transferTargetProblem + tests).
- U14: account popover uses the solid float material.
- U15/U16: inbox tile hint shortened; TMDB assign dialog uses the shared
Select and labelled fields (type, TMDB ID, note).
- Copy: "TMDB 已保存" spacing, API Key block no longer repeats the section
title, whole-day intervals read "每 7 天", "最高级别:提示",
"延后:已有计划在途", failed records show "未生成目标路径", upstream model
column "默认模型".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Conflicts resolved: routes.ts keeps both /console/status (FE-1) and
/console/search (FE-3); smoke list keeps FE-1/FE-4/FE-5 routes plus the
rules tabs, search results and preferences→rules redirects. Top bar mounts
SearchTrigger and CommandPalette next to the FE-1 inbox bell.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- template editor per movie / tv / variety with variable insertion, live preview by segment role, error line:col and reset to default
- settings > library: global templates, lockFileNameForBound and per-root overrides
- work detail: naming override dialog (preview uses the existing path binding)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Found in v0.2 visual QA (docs/plan/09-visual-qa.md):
- actor strings (admin_session:<uuid>, user:<uuid>, system) -> 管理员/系统/API Key
in policy revisions, subscriptions, inbox, trash, business tasks, organize detail
- cache volume without key shows the downloader name instead of instance:<uuid>
- download timeline/plan: task and organize statuses, plan purpose, priority and
status reason localized; audit gets labels for capability/files/site/feed actions
- organize record reason uses conflict detail (没有可入库的主视频) instead of
blocked:no_main_video; slot/candidate reasons drop inflight:<uuid>
- identity decision summary no longer shows no_viable_candidate
- upstream test: 'Connection error.' classified as network, with a Chinese hint
- new policy draft prefills key default (placeholder looked like a value)
- dialog/sheet close sr-only text in Chinese
Co-Authored-By: Claude Opus 5.5 <[email protected]>
pi.provider / baseUrl / model / thinkingLevel / apiKeySecretRef are now
DEPRECATED_SETTINGS_FIELDS: kept in the schema for stored JSON, dropped
from PATCH /settings and from the settings sections, never read by the
server. runAgent fails with a clear message when no upstream is set.
The agent demo gate checks the default upstream provider; e2e seeds a
fake upstream as default. OpenAPI summaries say Agent upstream.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- /console/settings#<section>[/<anchor>]; legacy ?tab= redirected in clientLoader
- SETTINGS_SECTIONS in contracts with field-ownership test
- Agent section: upstream list, create wizard (provider -> credentials/model -> login/test/default),
edit drawer with OAuth browser/device login, test, set default, delete
- sites section with site registration, snapshot/rate-limit drawer and account credentials
- path mappings moved into downloader / media-server drawers; metadata and account sections
Co-Authored-By: Claude Opus 5.5 <[email protected]>