mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-08 17:08:35 +08:00
fix(update): use managed progress execution
This commit is contained in:
@@ -16,7 +16,6 @@ from app.application.messaging.media import media_interaction_manager
|
||||
from app.application.messaging.site import site_interaction_manager
|
||||
from app.application.messaging.skill import skill_interaction_manager
|
||||
from app.application.messaging.subscribe import subscribe_interaction_manager
|
||||
from app.application.messaging.update import update_interaction_manager
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
@@ -158,6 +157,8 @@ class InteractionRouter:
|
||||
|
||||
def has_pending_interaction(user_id: Union[str, int]) -> bool:
|
||||
"""供 WebAgent 判断用户是否处于传统交互会话。"""
|
||||
from app.application.messaging.update import update_interaction_manager
|
||||
|
||||
return any(
|
||||
manager.get_by_user(user_id) is not None
|
||||
for manager in (
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine
|
||||
from collections.abc import Awaitable, Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from threading import Lock
|
||||
from typing import Any, Optional, Protocol, Union
|
||||
@@ -49,6 +49,7 @@ class SystemUpdateInteractionActions(Protocol):
|
||||
|
||||
|
||||
UpdateMonitorSubmitter = Callable[[Coroutine[Any, Any, None]], Any]
|
||||
UpdateOperationRunner = Callable[..., Awaitable[Any]]
|
||||
RestartMarker = Callable[[NotificationChannel, Union[str, int], Optional[str]], None]
|
||||
RestartMarkerClearer = Callable[[], None]
|
||||
|
||||
@@ -73,6 +74,7 @@ class SystemUpdateInteractionHandler:
|
||||
messenger: MessageGateway,
|
||||
actions: SystemUpdateInteractionActions,
|
||||
submit_monitor: UpdateMonitorSubmitter,
|
||||
run_sync: UpdateOperationRunner,
|
||||
mark_restart: RestartMarker,
|
||||
clear_restart_marker: RestartMarkerClearer,
|
||||
poll_interval_seconds: float = _poll_interval_seconds,
|
||||
@@ -85,6 +87,7 @@ class SystemUpdateInteractionHandler:
|
||||
self._renderer = _SystemUpdateRenderer(messenger=messenger, actions=actions)
|
||||
self._progress_monitor = _SystemUpdateProgressMonitor(
|
||||
actions=actions, renderer=self._renderer, submit_monitor=submit_monitor,
|
||||
run_sync=run_sync,
|
||||
poll_interval_seconds=poll_interval_seconds,
|
||||
)
|
||||
|
||||
@@ -505,12 +508,14 @@ class _SystemUpdateProgressMonitor:
|
||||
def __init__(
|
||||
self, *, actions: SystemUpdateInteractionActions,
|
||||
renderer: _SystemUpdateRenderer, submit_monitor: UpdateMonitorSubmitter,
|
||||
run_sync: UpdateOperationRunner,
|
||||
poll_interval_seconds: float,
|
||||
) -> None:
|
||||
"""注入状态读取、消息渲染和后台任务提交能力。"""
|
||||
self._actions = actions
|
||||
self._renderer = renderer
|
||||
self._submit_monitor = submit_monitor
|
||||
self._run_sync = run_sync
|
||||
self._poll_interval_seconds = max(0.0, poll_interval_seconds)
|
||||
|
||||
def schedule(
|
||||
@@ -567,7 +572,7 @@ class _SystemUpdateProgressMonitor:
|
||||
request = update_interaction_manager.get_by_id(request_id, userid)
|
||||
if request is None:
|
||||
return
|
||||
status = await asyncio.to_thread(self._actions.update_status)
|
||||
status = await self._run_sync(self._actions.update_status)
|
||||
item = self._renderer.application_item(status)
|
||||
fingerprint = self._renderer.item_fingerprint(item)
|
||||
if fingerprint != last_fingerprint:
|
||||
@@ -578,7 +583,7 @@ class _SystemUpdateProgressMonitor:
|
||||
channel=channel,
|
||||
)
|
||||
if original_message_id and original_chat_id and ChannelCapabilityManager.supports_editing(channel):
|
||||
edited = await asyncio.to_thread(
|
||||
edited = await self._run_sync(
|
||||
self._renderer.edit_view,
|
||||
view=view,
|
||||
channel=channel,
|
||||
@@ -588,7 +593,7 @@ class _SystemUpdateProgressMonitor:
|
||||
original_chat_id=original_chat_id,
|
||||
)
|
||||
if not edited and (not edit_fallback_sent or item.state in self._terminal_download_states):
|
||||
await asyncio.to_thread(
|
||||
await self._run_sync(
|
||||
self._renderer.post_view,
|
||||
view=view,
|
||||
channel=channel,
|
||||
@@ -602,7 +607,7 @@ class _SystemUpdateProgressMonitor:
|
||||
else:
|
||||
progress_bucket = item.progress // 10
|
||||
if progress_bucket != last_progress_bucket or item.state in self._terminal_download_states:
|
||||
await asyncio.to_thread(
|
||||
await self._run_sync(
|
||||
self._renderer.post_view,
|
||||
view=view,
|
||||
channel=channel,
|
||||
@@ -621,7 +626,7 @@ class _SystemUpdateProgressMonitor:
|
||||
logger.warning(f"监视 MoviePilot 更新下载进度失败:{error}")
|
||||
request = update_interaction_manager.get_by_id(request_id, userid)
|
||||
if request is not None:
|
||||
await asyncio.to_thread(
|
||||
await self._run_sync(
|
||||
self._renderer.render_operation_failure,
|
||||
request=request,
|
||||
channel=channel,
|
||||
|
||||
@@ -27,7 +27,6 @@ from app.application.messaging.session import MessageSessionService
|
||||
from app.application.messaging.site import site_interaction_manager
|
||||
from app.application.messaging.skill import SkillInteractionHandler, skill_interaction_manager
|
||||
from app.application.messaging.subscribe import subscribe_interaction_manager
|
||||
from app.application.messaging.update import update_interaction_manager
|
||||
from app.chain.base import ChainBase
|
||||
from app.chain.interaction import MediaInteractionChain as _MediaInteractionChain
|
||||
from app.chain.site import SiteChain
|
||||
@@ -706,6 +705,8 @@ class MessageChain(ChainBase):
|
||||
|
||||
def _interaction_router(self) -> interaction_router.InteractionRouter:
|
||||
"""构造交互路由器,文本会话按创建时间选择,回调路由注册顺序即优先级。"""
|
||||
from app.application.messaging.update import update_interaction_manager
|
||||
|
||||
session_routes = [
|
||||
interaction_router.SessionRoute(
|
||||
name="sites",
|
||||
|
||||
+26
-4
@@ -1,15 +1,15 @@
|
||||
import asyncio
|
||||
import errno
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import threading
|
||||
import uuid
|
||||
from collections.abc import Coroutine, Mapping
|
||||
from collections.abc import Callable, Coroutine, Mapping
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Protocol, Union
|
||||
from typing import TYPE_CHECKING, Any, Optional, Protocol, Union
|
||||
|
||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||
from app.application.messaging.update import SystemUpdateInteractionHandler
|
||||
from app.chain.base import ChainBase
|
||||
from app.runtime import version as runtime_version
|
||||
from app.runtime.log import logger
|
||||
@@ -19,6 +19,9 @@ from app.runtime.tasks import get_task_registry
|
||||
from app.schemas.message import Message
|
||||
from app.schemas.notification import NotificationChannel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from app.application.messaging.update import SystemUpdateInteractionHandler
|
||||
|
||||
|
||||
class SystemResponsePort(Protocol):
|
||||
"""系统链查询发布版本所需的最小同步 HTTP 响应契约。"""
|
||||
@@ -102,19 +105,38 @@ def _close_system_response(response: SystemResponsePort) -> None:
|
||||
logger.debug(f"释放版本响应失败:{str(err)}")
|
||||
|
||||
|
||||
async def _run_update_operation(
|
||||
function: Callable[..., Any],
|
||||
/,
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""通过任务登记器在线程池执行同步更新操作并等待真实终态。"""
|
||||
task = get_task_registry().create_sync(
|
||||
function,
|
||||
*args,
|
||||
owner="chain.system.update_progress.operation",
|
||||
**kwargs,
|
||||
)
|
||||
return await asyncio.shield(task)
|
||||
|
||||
|
||||
class _SystemUpdateChain(ChainBase):
|
||||
"""提供通知渠道主程序升级交互的 Chain 入口。"""
|
||||
|
||||
_update_restart_file = "__system_update_restart__"
|
||||
|
||||
def _update_interaction_handler(self) -> SystemUpdateInteractionHandler:
|
||||
def _update_interaction_handler(self) -> "SystemUpdateInteractionHandler":
|
||||
"""构造复用当前消息网关和系统应用服务的更新交互控制器。"""
|
||||
from app.application.messaging.update import SystemUpdateInteractionHandler
|
||||
|
||||
if self.system_service is None:
|
||||
raise RuntimeError("系统更新服务尚未由启动组合根装配")
|
||||
return SystemUpdateInteractionHandler(
|
||||
messenger=self,
|
||||
actions=self.system_service,
|
||||
submit_monitor=self._submit_update_monitor,
|
||||
run_sync=_run_update_operation,
|
||||
mark_restart=self._mark_update_restart,
|
||||
clear_restart_marker=self._clear_update_restart_marker,
|
||||
)
|
||||
|
||||
@@ -103,7 +103,7 @@ ARCH-201 至 ARCH-204 均达到实现、验证、提交、推送和远端门禁
|
||||
| Python 源码量 | 305,884 行 | 排除 `app/plugins/**`;61 个文件超过 1,000 行,11 个超过 2,000 行 |
|
||||
| 长方法 | 290 个超过 80 行 | AST 统计排除 `app/plugins/**`;65 个超过 150 行,21 个超过 250 行 |
|
||||
| 全量 mypy 历史债务 | 9,508 / 513 文件 | Agent API 重构后的现状基线;canonical Facade 与 endpoint 类型边界已补齐,低水位只允许继续下降 |
|
||||
| Ruff 历史诊断 | 547 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
|
||||
| Ruff 历史诊断 | 546 | 低水位门禁通过,但规则集只覆盖 `E4/E7/E9/F/I` |
|
||||
| 覆盖率固定基线 | Application 80.00%,Domain 80.00% | Chain、Runtime、Agent、Adapter、Startup 未进入包级覆盖率门禁 |
|
||||
|
||||
### 3.3 热点文件
|
||||
|
||||
@@ -364,9 +364,6 @@
|
||||
"F541": 1,
|
||||
"I001": 1
|
||||
},
|
||||
"app/modules/slack/slack.py": {
|
||||
"I001": 1
|
||||
},
|
||||
"app/modules/subtitle/__init__.py": {
|
||||
"I001": 1
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Coroutine
|
||||
from collections.abc import Callable, Coroutine
|
||||
from typing import Any
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
@@ -145,12 +145,22 @@ def _handler(
|
||||
messenger=messenger,
|
||||
actions=actions,
|
||||
submit_monitor=submitted.append,
|
||||
run_sync=_run_sync,
|
||||
mark_restart=mark_restart or Mock(),
|
||||
clear_restart_marker=clear_restart_marker or Mock(),
|
||||
poll_interval_seconds=0,
|
||||
)
|
||||
|
||||
|
||||
async def _run_sync(
|
||||
function: Callable[..., Any],
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> Any:
|
||||
"""在测试事件循环内直接执行同步更新操作。"""
|
||||
return function(*args, **kwargs)
|
||||
|
||||
|
||||
def test_update_command_prompts_for_download_when_release_is_available() -> None:
|
||||
"""检测到新版本后应显示版本信息和确认升级按钮。"""
|
||||
messenger = _Messenger()
|
||||
|
||||
Reference in New Issue
Block a user