jxxghp
4955b7b9ea
fix: harden backend isolation and execution safety
2026-10-16 09:11:28 +08:00
jxxghp
2c889a1735
fix(subscribe): 站点本地流控按限流窗口退避 ( #6996 ) ( #7001 )
...
订阅分页搜索遇到站点本地流控(limit_interval/limit_count/limit_seconds)
拒绝时只按秒级 cooldown 反复重试,在整个限流窗口内持续触发风控。现在:
- 本地流控被拒时按站点配置窗口计算退避(访问间隔与计数窗口取最大值,
并多留 1 秒越过窗口的严格比较边界);
- 同一退避写入站点预算冷却,窗口内其它订阅任务不会继续访问该站点;
- 本地流控没有发出请求,不计入连续失败与错误退避类别。
2026-10-15 09:14:13 +08:00
flippedyizheng
19f9098d68
fix(mediaserver): require unambiguous title-year match for cross-source fallback ( #6998 )
2026-10-15 07:15:06 +08:00
jxxghp
b82bbcb420
fix(webhook)!: guard unknown movie paths in transfer history
...
Recover Jellyfin deletion paths from webhook payloads or bounded server-scoped caches. Avoid querying deleted items and preserve TV season/series lookup behavior.
BREAKING CHANGE: Movie and unspecified-type history queries without a destination now return no records unless match_all_versions=True is explicitly requested.
Refs #6988
2026-10-13 23:48:10 +08:00
jxxghp
435703573e
fix(transfer): persist provider checkpoint planning time
...
Write planned_at atomically for provider_pending checkpoints and refresh it when host planning takes over. Cover real admission-to-execution persistence and distinguish incomplete plan fields from inconsistent execution checkpoint fields.
Refs #6973
2026-10-13 07:13:42 +08:00
jxxghp
86c7fccfd5
feat(subscribe): persist business activity with scoped details
2026-10-12 23:43:01 +08:00
jxxghp
bd1e6eb899
fix(agent): separate public API queries from bound user operations
2026-10-12 07:30:35 +08:00
jxxghp
bb29451222
fix: isolate unhandled durable transfer failures
2026-10-10 12:17:07 +08:00
freeman
f3ad81b39e
style: apply safe ruff auto-fixes and exempt startup late imports
...
Run `ruff check app tests scripts --fix` with the configured rule set
(E4/E7/E9/F/I): import sorting (I001), f-strings without placeholders
(F541), lambda/variable/unused-import cleanups that ruff marks safe.
Nine files whose only ruff debt sits next to pre-existing pylint
import-resolution errors (lazy exports, sys.path script imports) or
whose "unused" imports are reached through attribute access by tests are
left untouched so the changed-file pylint gate and the benchmark tests
stay green; their diagnostics remain in the baseline.
app/startup/lifecycle/__init__.py and app/main.py import business
modules after cache composition, import-path and stdio setup on purpose
(the source comments document the contract), so declare E402
per-file-ignores for them instead of adding per-line noqa.
Ruff ratchet watermark drops from 498 to 100; sync the baseline fixture
and the optimization checklist figure.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com >
2026-10-09 13:29:09 +08:00
ChantXu64
e5e2558585
feat(search): 订阅补全逐页搜索、断点续搜与资源页手动分页 ( #6907 )
2026-10-07 14:40:55 +08:00
jxxghp
e51fd44964
fix(agent): resolve alternative QQ and Feishu user bindings
...
Keep strict binding lookup as the default and explicitly match alternative channel identifiers for the Agent API gateway. Reject ambiguous active-user matches and cover single-field bindings with database-backed tests.
Refs #6905
2026-10-07 09:16:33 +08:00
jxxghp
3726ca078a
fix(mediaserver): isolate cached item ids by server ( #6850 )
2026-10-01 17:07:23 +08:00
jxxghp
47d51c3630
feat(agent): align history recall with Hermes SQLite FTS5
2026-09-30 16:28:39 +08:00
jxxghp
4a2ef75706
fix(music): retry transient recognition before freezing file operations
2026-09-30 06:36:04 +08:00
jxxghp
b4175a4f88
fix(image): cache site allowlist and include custom image hosts
2026-09-30 01:09:32 +08:00
moviepilot-codex[bot]
9df35e7b83
fix: 修复配置快照、依赖同步和 Agent 上限
2026-09-21 04:26:48 +08:00
jxxghp
67ae166a54
fix(servarr): restore subscription repository async contract
2026-09-18 22:13:33 +08:00
jxxghp
4e9a64c0f0
refactor(application): 按模块子目录统一文件命名
2026-09-15 23:57:06 +08:00
jxxghp
880cef2c88
fix(config): 使用公共日志模块记录配置恢复
2026-09-15 11:43:12 +08:00
jxxghp
f483e5fe14
fix(config): 修复非法系统配置阻断启动 ( #6702 )
2026-09-15 11:16:29 +08:00
Aqr-K
61aa3cd649
refactor(sdk): 插件契约基类迁入 SDK 并由 Compat 承接旧包根符号 ( #6701 )
2026-09-15 07:01:00 +08:00
jxxghp
6a165a72aa
fix(mediaserver): satisfy type ratchet
2026-09-15 00:28:19 +08:00
jxxghp
c587ff07f7
fix(mediaserver): support cross-source library status
2026-09-15 00:17:20 +08:00
jxxghp
ebddeee23b
feat(subscribe): accumulate music album track progress
2026-09-14 19:35:04 +08:00
Aqr-K
5e232ca14d
fix(plugin): 安装记录大小写不敏感查询、游标物化,site 端点改从 Application 取 ( #6682 )
...
* fix(plugin): 安装 journal 按大小写不敏感列出事务记录
create 用 func.lower 判定同一物理插件并据此拒绝重复占位,list 却还是等值过滤。
判据分叉后,恢复流程查不到那条被 create 认定为未收尾的 journal,会认为没有待恢复
事务,而新事务又永远被同一行挡住。list 改用与 create 相同的 func.lower 判据。
* fix(plugin): 批量读插件身份时在事务内物化游标
list_by_plugin_ids 无会话调用时由组合根另开短事务,会话在 lambda 返回后立即关闭,
外层再 list() 消费惰性游标会撞上 "identity map is no longer valid",一行都读不到。
补上 .all(),让行在事务内取完。
* refactor(api): site 端点从 Application 取插件路由刷新
register_plugin_api 的唯一拥有者是 app.application.plugin.routes,
dependencies/plugin.py、scheduler/reconcile.py 与 initializers/plugins.py 都已从那里
取用,只剩 site 端点还在向 plugin 端点横向借。改为同一来源,并加门禁锁住这条判据。
依赖基线相应下降一条边(8650 -> 8649:site 不再到达 app.api.endpoints 与
app.api.endpoints.plugin,改为到达 app.application.plugin.routes),mypy 低水位同步
下降一条 attr-defined,两处文档数字随之更新。
---------
Co-authored-by: jxxghp <jxxghp@gmail.com >
2026-09-14 07:18:50 +08:00
Aqr-K
6ea1cc9433
fix(plugin): 插件降级重装时拒绝迁移树不认识的库 revision ( #6681 )
2026-09-14 07:01:32 +08:00
Aqr-K
5fd04505c1
feat(plugin): 分身彻底清理,并按包含性校验收口插件数据目录解析 ( #6671 )
2026-09-13 21:05:04 +08:00
Aqr-K
d44480d739
Merge pull request #6666 from Aqr-K/feat/plugin-instance-target-enablement
...
feat(plugin): 插件实例默认调用目标与启停,未指定实例时不再兜底取第一个
2026-09-13 16:37:27 +08:00
jxxghp
c60949ffff
Merge branch 'v3' into feat/plugin-instance-config-logging
2026-09-13 14:30:42 +08:00
Aqr-K
3d594ecee3
refactor(plugin): 插件配置改走独立通道,SystemConfig 不再按键值分支
...
插件配置此前借用 systemconfig 的 plugin.<实例ID> 单键,为了换表又在
SystemConfigOper 的 set/delete/load_snapshot 里按该前缀开分支。这是反向依赖:
一个对所有键一视同仁的通用键值存储被迫知道「插件」这个概念,读写路径依键值而
异,「这个键落在哪张表」只有读代码才说得清;规范方法按值加特例,也让此后任何新
键都要先确认自己会不会被拦截。
本次把插件配置整条链拆出,形状与本分支日志等级那条链一致:
- SystemConfigOper 整体回退到官方实现,与 f70d7c141 逐字节相同,对所有键一视同仁。
- 配置端口改按实例 ID 寻址:PluginStorage 新增 read_config / write_config /
async_write_config / delete_config,PluginConfigStore 去掉 key_prefix 与 _key,
不再有任何字符串键经过;plugin_exists 存在性规则、空键过滤与 force 语义不变。
- PluginInstanceOper 补齐读取与异步写入原语 get_config_data /
async_save_config_data,同步与异步共用同一份落库决策,避免两条路径对「值未变化
不写」和「建行用哪个源插件」各自分叉。
- _PluginBase 的 get_config / update_config 直连 PluginInstanceOper,方法名、签名、
返回与 docstring 不变;self.systemconfig 保留,插件读 SystemConfigKey 枚举值仍
然合法,插件公开面没有扩大。
插件直接用 SystemConfig 存取自身配置不予支持。它本就不是封装过的插件配置通道,
为它设计兼容或加守卫拦截,等于再把「插件」塞回通用存储里——那正是本次要拆掉的东
西。拆开后 plugin.<ID> 只是一个与插件无关的普通 systemconfig 键:迁移已经把旧的
plugin.* 行搬进插件实例表,读它拿到 None,写它会新增一条谁也不读的 systemconfig
记录,插件配置通道既不读也不写它,两边不会再有任何交叉。
代价是读取从内存快照变成一次按实例 ID 的主键查询。调用点都不在热路径上:宿主侧
只有插件启动/重载、配置表单接口与分身创建三处,插件侧 get_config() 惯例在
init_plugin 里取一次。写入侧没有变化,本来就是一次事务。
回归防线:新增用例断言 SystemConfigOper 对 plugin. 前缀没有任何特殊行为——写进去
就是一行普通 systemconfig 记录,读出来就是刚写的值,不会被路由到插件实例表。
兼容层零改动,三个 alembic 迁移一行未改。
2026-09-13 01:41:57 -04:00
jxxghp
8072b7d78f
Refactor application architecture and compatibility boundaries
2026-09-13 12:53:00 +08:00
Aqr-K
e47d7defe7
feat(plugin): 插件本体与分身可各自设置独立日志等级
...
全局日志等级是一个开关,调试单个插件时只能整体下调。一旦下调,被调试插件那几行
日志会淹没在其他几十个插件与宿主自身的 DEBUG 输出里,找不出来;不下调又什么都
看不见。插件实例数量只会继续增长,这个矛盾不会自己消失。
因此把等级下放到实例:plugininstance 行加 log_level / log_expires_at 两列,本体与
分身共用同一张表、同一套语义。等级落在实例自己那一行而不是另起一张按实例 ID 索引
的表——它和业务参数一样是用户按实例设置的东西,同一个生命周期,实例被删时应当随行
一起消失。清除覆盖后若本体行只剩一对身份列,整行按 carries_only_identity 回收,与
清空业务参数同一规则。
覆盖带失效时间,因为它是调试设置不是长期配置:开了 DEBUG 忘记关,下次谁也想不起
某个插件为什么一直在刷日志。过期判定在读取时惰性执行并就地清理,不另起后台线程去
扫一张最多几十条的表。expires_at 在写入侧先归一到 UTC 再分发给缓存与落盘两处:客户端
提交的时间可能不带时区,两处各按进程时区折算一次,就会与读取口返回的 UTC 值差出一个
时区,表现为「提交的失效时刻和读回来的对不上」。
运行期用 ContextVar 在宿主自己控制的四类调用点绑定当前实例——构造与 init_plugin、
事件处理器、定时服务回调、插件声明的 HTTP 端点——而不是沿用 logger() 已有的栈帧内省。
内省认的是栈顶的模块与类,插件通过宿主公共方法转发调用时栈顶是宿主而不是发起调用的
插件,会把日志记到错误的来源上;分身更甚:分身共享源码,类的 __qualname__ 保持源类名
不变,内省根本分不出是哪一个分身在打日志。内省结果继续只用于文件路由,不参与等级判定。
等级过滤必须排在全局等级判定之前。官方现状是先按全局等级短路、之后才做内省,先短路
就会把实例调低的等级挡在门外,后面再怎么识别来源都看不到这条日志。
回调在注册时被捕获、稍后才由调度器或 FastAPI 调用,那时已经不在任何绑定作用域内,
故这两处改用 wrap_for_plugin_instance 把绑定包进回调自身;同一实例重复包装直接返回
原对象,避免插件缓存声明时包装链随重载次数无限增长。
进程内覆盖表按实例 ID 常驻,删分身时必须一并清掉:只删库里那一行的话,同一进程内用
相同后缀重建的分身会继承上一个分身的等级,界面显示「跟随全局」而实际仍按旧等级输出。
进程重启后反向从库预热缓存。
迁移链在 F1 之后追加:c4e1a7b9d2f6(3.0.35)-> 487f7e681955(3.0.36 加两列)。
顺带补了 agent 策略里缺失的 media.classification.policy.get 的 template 字段说明:
少了它,api_mcp_schema.json 与技能文档的两个生成脚本都跑不起来。
2026-09-12 19:05:05 -04:00
Aqr-K
4dbb8b8204
feat(plugin): 插件配置与实例描述符迁入独立表
...
插件配置此前寄存在 systemconfig 的 plugin.<实例ID> 单键下,实例描述符则整份挤在
PluginInstances 这一个 JSON 键里,两者都不成立:
- plugin.<ID> 是裸字符串 key,而仓库规则本身禁止用裸字符串做 SystemConfig key,
只允许先定义 SystemConfigKey 枚举项;插件 ID 由用户安装决定,永远枚举不出常量。
条目数随安装量增长,混在系统设置表里会把主程序自己的设置项淹掉。
- 那个键存的其实是实例 ID,表里没有任何一列说得出它属于哪个插件,想列出某个插件
的全部实例配置只能靠字符串前缀去猜。
- 实例描述符整份读出再整份写回,改一个分身要重写全部分身。
改为 plugininstance 一实例一行:instance_id 与 source_plugin_id 构成身份,本体与
分身由两者是否相等派生而不设模式列——模式列会是这个等式的冗余副本,两者一旦失步
同一行就会在不同读取口被判成不同角色。展示信息与业务参数同存一行,属于同一个
生命周期,分表只会让建分身、删分身退化成两张表之间的协调问题。
读写路由落在 SystemConfigOper.get/set/delete,而不是在各个调用方各改一处:第三方
插件可能直接用 self.systemconfig.get("plugin.xxx") 读写自己的配置,只改
PluginConfigStore 与 _PluginBase.get_config/update_config 必然漏掉它们。路由只做
前缀识别,插入、更新与空本体行回收都委托 PluginInstanceOper,不在配置层重抄一遍。
PluginInstances 旧键迁移后不删,留作回滚依据,并以
SystemConfigKey.PluginInstancesImported 标志防止重复导入;判据不能是「表当前为空」,
否则用户把分身全部删光后,下次启动会把它们整批导回来。
迁移链:b2d4f6a8c1e3 -> 281965691a20(3.0.34 建表并搬描述符)
-> c4e1a7b9d2f6(3.0.35 加 config_data 并搬 plugin.* 配置)。
依赖基线与启动模块数随两个新模块重算,架构文档与数据库技能表目录同步登记新表。
2026-09-12 17:39:36 -04:00
jxxghp
1ba4d087c5
fix: scope subscription maintenance by media type
2026-09-12 20:56:05 +08:00
InfinityPacer
b3f748ffa8
feat(database): support plugin database backup
...
支持插件 SQLite 数据库备份并校验 PostgreSQL schema 权限
2026-09-12 20:22:33 +08:00
InfinityPacer
1ec7005b51
feat(database): back up plugin sqlite databases
2026-09-12 19:53:38 +08:00
jxxghp
ba0b737783
fix(site): use list default for seeding info
2026-09-12 19:20:48 +08:00
jxxghp
889bdfe501
fix(classification): align migration defaults and source semantics
2026-09-12 17:43:36 +08:00
jxxghp
acfa5eee72
fix(history): suppress failure feedback on successful records
2026-09-10 11:51:08 +08:00
jxxghp
fcc83e6bbe
fix(servarr): restore sync subscription lookup ( #6635 )
2026-09-10 09:29:17 +08:00
jxxghp
02d528310b
fix(db): release worker capacity before publishing results
2026-09-10 00:29:09 +08:00
jxxghp
5fa5b414aa
feat(agent): harden tool outcomes, recovery and discovery
2026-09-09 19:55:21 +08:00
jxxghp
022dd006a5
fix(transfer): allow manual reorganization after stale task failures
...
Refs #6625
2026-09-09 17:53:24 +08:00
InfinityPacer
a13d13aeba
fix: 原子合并订阅搜索任务并跳过无效 RSS 地址 ( #6626 )
...
* fix(subscription): coalesce active search tasks with atomic upsert
* fix(rss): skip invalid feed URLs before network access
2026-09-09 17:00:50 +08:00
jxxghp
5a214afc19
fix(subscribe): keep scheduled searches progressing across site retries
2026-09-09 16:45:42 +08:00
jxxghp
9e1c25124b
fix(transfer): restore regression and architecture gates
2026-09-09 13:40:35 +08:00
jxxghp
ce690b1140
feat(transfer): add failure stage, recovery action, retry count, and cleanup status to transfer history
2026-09-09 13:17:36 +08:00
jxxghp
9780a93cdb
fix(transfer): preserve episode-mapped download history associations
2026-09-09 06:58:01 +08:00
jxxghp
d0c894f5eb
fix(transfer): close durable error recovery loop
2026-09-08 22:38:59 +08:00
jxxghp
d97906f0a9
fix(transfer): clarify execution conflict errors
2026-09-08 22:01:55 +08:00