mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 08:26:53 +08:00
refactor: strengthen architecture CI gates and runtime contracts
This commit is contained in:
@@ -65,6 +65,9 @@ jobs:
|
|||||||
- name: Check process runtime service locators
|
- name: Check process runtime service locators
|
||||||
run: uv run --locked --no-sync python scripts/architecture/service_locator.py
|
run: uv run --locked --no-sync python scripts/architecture/service_locator.py
|
||||||
|
|
||||||
|
- name: Check Ruff diagnostic ratchet
|
||||||
|
run: uv run --locked --no-sync python scripts/architecture/ruff_ratchet.py
|
||||||
|
|
||||||
- name: Check mypy error ratchet
|
- name: Check mypy error ratchet
|
||||||
run: uv run --locked --no-sync python scripts/architecture/mypy_ratchet.py
|
run: uv run --locked --no-sync python scripts/architecture/mypy_ratchet.py
|
||||||
|
|
||||||
@@ -108,7 +111,6 @@ jobs:
|
|||||||
run: uv run --locked --no-sync python tests/run.py --shard "${{ matrix.shard }}"
|
run: uv run --locked --no-sync python tests/run.py --shard "${{ matrix.shard }}"
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
if: github.event_name == 'workflow_dispatch'
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: Coverage Report
|
name: Coverage Report
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
@@ -138,6 +140,7 @@ jobs:
|
|||||||
uv run --locked --no-sync python -m coverage report
|
uv run --locked --no-sync python -m coverage report
|
||||||
uv run --locked --no-sync python -m coverage json
|
uv run --locked --no-sync python -m coverage json
|
||||||
uv run --locked --no-sync python -m coverage xml
|
uv run --locked --no-sync python -m coverage xml
|
||||||
|
uv run --locked --no-sync python scripts/architecture/coverage_ratchet.py
|
||||||
|
|
||||||
- name: Upload coverage report
|
- name: Upload coverage report
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
|
|||||||
+3
-3
@@ -5,24 +5,24 @@ from typing import cast
|
|||||||
|
|
||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
|
|
||||||
from app.application.messaging.chat import AsyncAgentChatRepository, AsyncUnitOfWork
|
|
||||||
from app.application.outbox import AsyncOutboxTransaction
|
|
||||||
from app.application.configuration import (
|
from app.application.configuration import (
|
||||||
ApiRuntimeConfig,
|
ApiRuntimeConfig,
|
||||||
get_api_runtime_config_snapshot,
|
get_api_runtime_config_snapshot,
|
||||||
)
|
)
|
||||||
|
from app.application.messaging.chat import AsyncAgentChatRepository, AsyncUnitOfWork
|
||||||
|
from app.application.outbox import AsyncOutboxTransaction
|
||||||
from app.application.subscription.delete import SubscribeDeletionRepository
|
from app.application.subscription.delete import SubscribeDeletionRepository
|
||||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
||||||
from app.application.subscription.mutation import (
|
from app.application.subscription.mutation import (
|
||||||
SubscriptionHistoryMutationRepository,
|
SubscriptionHistoryMutationRepository,
|
||||||
SubscriptionMutationRepository,
|
SubscriptionMutationRepository,
|
||||||
)
|
)
|
||||||
|
from app.runtime.tasks import TaskRegistry, get_task_registry
|
||||||
from app.startup.composition.context import (
|
from app.startup.composition.context import (
|
||||||
AgentChatRuntime,
|
AgentChatRuntime,
|
||||||
HostRuntime,
|
HostRuntime,
|
||||||
SubscriptionRuntime,
|
SubscriptionRuntime,
|
||||||
)
|
)
|
||||||
from app.runtime.tasks import TaskRegistry, get_task_registry
|
|
||||||
|
|
||||||
|
|
||||||
def get_host_runtime(request: Request) -> HostRuntime:
|
def get_host_runtime(request: Request) -> HostRuntime:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|||||||
from collections.abc import AsyncGenerator, Callable, Generator
|
from collections.abc import AsyncGenerator, Callable, Generator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
SessionProvider = Callable[[], Generator[Any, None, None]]
|
SessionProvider = Callable[[], Generator[Any, None, None]]
|
||||||
AsyncSessionProvider = Callable[[], AsyncGenerator[Any, None]]
|
AsyncSessionProvider = Callable[[], AsyncGenerator[Any, None]]
|
||||||
RepositoryFactory = Callable[[Any], Any]
|
RepositoryFactory = Callable[[Any], Any]
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ from fastapi import Depends
|
|||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.api.context import (
|
from app.api.context import (
|
||||||
get_agent_chat_runtime,
|
|
||||||
get_agent_chat_repository,
|
get_agent_chat_repository,
|
||||||
|
get_agent_chat_runtime,
|
||||||
get_agent_chat_transaction,
|
get_agent_chat_transaction,
|
||||||
get_async_session,
|
get_async_session,
|
||||||
get_host_runtime,
|
get_host_runtime,
|
||||||
)
|
)
|
||||||
from app.application.messaging.chat import (
|
from app.application.messaging.chat import (
|
||||||
AgentChatService,
|
|
||||||
AgentChatPersistenceService,
|
AgentChatPersistenceService,
|
||||||
|
AgentChatService,
|
||||||
AsyncAgentChatRepository,
|
AsyncAgentChatRepository,
|
||||||
AsyncUnitOfWork,
|
AsyncUnitOfWork,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,25 +9,33 @@ from sqlalchemy.orm import Session
|
|||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.api.context import (
|
from app.api.context import (
|
||||||
get_async_session,
|
get_async_session,
|
||||||
|
get_background_task_registry,
|
||||||
get_host_runtime,
|
get_host_runtime,
|
||||||
get_subscription_history_repository,
|
get_subscription_history_repository,
|
||||||
get_subscription_outbox,
|
get_subscription_outbox,
|
||||||
get_subscription_repository,
|
get_subscription_repository,
|
||||||
get_subscription_transaction,
|
get_subscription_transaction,
|
||||||
get_sync_session,
|
get_sync_session,
|
||||||
|
resolve_background_task_registry,
|
||||||
)
|
)
|
||||||
from app.application.outbox import AsyncOutboxTransaction
|
from app.application.outbox import AsyncOutboxTransaction
|
||||||
from app.application.scheduling import start_scheduler_job
|
from app.application.scheduling import start_scheduler_job
|
||||||
from app.application.servarr import ServarrSubscriptionService
|
from app.application.servarr import ServarrSubscriptionService
|
||||||
from app.application.subscription.delete import (
|
from app.application.subscription.delete import (
|
||||||
AsyncUnitOfWork as DeleteUnitOfWork,
|
AsyncUnitOfWork as DeleteUnitOfWork,
|
||||||
|
)
|
||||||
|
from app.application.subscription.delete import (
|
||||||
DeleteSubscribeCommand,
|
DeleteSubscribeCommand,
|
||||||
SubscribeDeletionRepository,
|
SubscribeDeletionRepository,
|
||||||
)
|
)
|
||||||
from app.application.subscription.identity import DeleteSubscriptionsByIdentityCommand
|
from app.application.subscription.identity import (
|
||||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
DeleteSubscriptionsByIdentityCommand,
|
||||||
|
SubscribeIdentityDeletionRepository,
|
||||||
|
)
|
||||||
from app.application.subscription.mutation import (
|
from app.application.subscription.mutation import (
|
||||||
AsyncUnitOfWork as MutationUnitOfWork,
|
AsyncUnitOfWork as MutationUnitOfWork,
|
||||||
|
)
|
||||||
|
from app.application.subscription.mutation import (
|
||||||
SubscriptionHistoryMutationRepository,
|
SubscriptionHistoryMutationRepository,
|
||||||
SubscriptionMutationRepository,
|
SubscriptionMutationRepository,
|
||||||
SubscriptionMutationService,
|
SubscriptionMutationService,
|
||||||
@@ -36,10 +44,9 @@ from app.application.subscription.query import SubscriptionQueryService
|
|||||||
from app.application.subscription.search import SearchSubscriptionsCommand
|
from app.application.subscription.search import SearchSubscriptionsCommand
|
||||||
from app.runtime.events import eventmanager
|
from app.runtime.events import eventmanager
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
from app.schemas.types import EventType
|
from app.schemas.types import EventType
|
||||||
from app.startup.composition.context import HostRuntime
|
from app.startup.composition.context import HostRuntime
|
||||||
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
|
||||||
|
|
||||||
|
|
||||||
async def _publish_subscribe_deleted(
|
async def _publish_subscribe_deleted(
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from app.application.workflow import (
|
|||||||
WorkflowQueryService,
|
WorkflowQueryService,
|
||||||
get_workflow_manager,
|
get_workflow_manager,
|
||||||
)
|
)
|
||||||
from app.runtime.config import global_vars
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.startup.composition.context import HostRuntime
|
from app.startup.composition.context import HostRuntime
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ def get_workflow_mutation_command(
|
|||||||
load_event=workflow_manager.load_workflow_events,
|
load_event=workflow_manager.load_workflow_events,
|
||||||
remove_event=workflow_manager.remove_workflow_event,
|
remove_event=workflow_manager.remove_workflow_event,
|
||||||
refresh_event=workflow_manager.update_workflow_event,
|
refresh_event=workflow_manager.update_workflow_event,
|
||||||
stop_running=global_vars.stop_workflow,
|
stop_running=runtime_stop_state.stop_workflow,
|
||||||
delete_cache=lambda workflow_id: system_config.delete(
|
delete_cache=lambda workflow_id: system_config.delete(
|
||||||
f"WorkflowCache-{workflow_id}"
|
f"WorkflowCache-{workflow_id}"
|
||||||
),
|
),
|
||||||
@@ -52,7 +52,7 @@ def get_workflow_definition_command(
|
|||||||
return WorkflowDefinitionCommand(
|
return WorkflowDefinitionCommand(
|
||||||
repository=runtime.workflow.repository(db),
|
repository=runtime.workflow.repository(db),
|
||||||
unit_of_work=runtime.persistence.async_transaction(db),
|
unit_of_work=runtime.persistence.async_transaction(db),
|
||||||
stop_running=global_vars.stop_workflow,
|
stop_running=runtime_stop_state.stop_workflow,
|
||||||
async_delete_cache=lambda workflow_id: system_config.async_delete(
|
async_delete_cache=lambda workflow_id: system_config.async_delete(
|
||||||
f"WorkflowCache-{workflow_id}"
|
f"WorkflowCache-{workflow_id}"
|
||||||
),
|
),
|
||||||
|
|||||||
+47
-49
@@ -7,8 +7,8 @@ import shutil
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from queue import Empty, Queue
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from queue import Empty, Queue
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, AsyncIterator, Awaitable, Callable, Optional, Union
|
from typing import Any, AsyncIterator, Awaitable, Callable, Optional, Union
|
||||||
|
|
||||||
@@ -16,15 +16,58 @@ import aiofiles
|
|||||||
from fastapi import Depends, File, Form, HTTPException, Request, UploadFile, status
|
from fastapi import Depends, File, Form, HTTPException, Request, UploadFile, status
|
||||||
from fastapi.responses import FileResponse, StreamingResponse
|
from fastapi.responses import FileResponse, StreamingResponse
|
||||||
|
|
||||||
|
from app.agent.contracts import ReplyMode, build_display_message
|
||||||
|
from app.agent.mcp import agent_mcp_manager
|
||||||
|
from app.agent.runtime_loader import get_moviepilot_agent_type
|
||||||
|
from app.api.dependencies.agent import (
|
||||||
|
get_agent_chat_persistence,
|
||||||
|
get_agent_chat_service,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.auth import get_current_active_user
|
||||||
|
from app.api.presentation.sse import build_sse_error_response, build_sse_response
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.agent import (
|
||||||
|
get_running_agent_manager,
|
||||||
|
is_audio_input_available,
|
||||||
|
transcribe_audio,
|
||||||
|
)
|
||||||
|
from app.application.commands import get_command, get_commands
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.application.messaging.agent import (
|
||||||
|
agent_interaction_manager,
|
||||||
|
attach_web_agent_edit_queue,
|
||||||
|
attach_web_agent_message_queue,
|
||||||
|
build_agent_choice_button_rows,
|
||||||
|
create_web_agent_background_task,
|
||||||
|
detach_web_agent_edit_queue,
|
||||||
|
detach_web_agent_message_queue,
|
||||||
|
is_web_agent_message_for_user,
|
||||||
|
normalize_web_agent_button_rows,
|
||||||
|
parse_agent_choice_callback,
|
||||||
|
)
|
||||||
|
from app.application.messaging.chat import (
|
||||||
|
AgentChatPersistenceService,
|
||||||
|
AgentChatRecord,
|
||||||
|
AgentChatService,
|
||||||
|
get_configured_agent_chat_persistence,
|
||||||
|
get_configured_agent_chat_service,
|
||||||
|
)
|
||||||
|
from app.application.messaging.router import has_pending_interaction
|
||||||
|
from app.application.security.user import get_configured_user_id_lookup
|
||||||
|
from app.chain.message import MessageChain
|
||||||
from app.runtime.execution import run_in_threadpool
|
from app.runtime.execution import run_in_threadpool
|
||||||
|
from app.runtime.localization import LocaleHelper
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.agent import AgentChatDisplaySaveRequest as _SchemaAgentChatDisplaySaveRequest
|
from app.schemas.agent import AgentChatDisplaySaveRequest as _SchemaAgentChatDisplaySaveRequest
|
||||||
from app.schemas.agent import AgentChatSessionDetail as _SchemaAgentChatSessionDetail
|
from app.schemas.agent import AgentChatSessionDetail as _SchemaAgentChatSessionDetail
|
||||||
from app.schemas.agent import AgentChatSessionSummary as _SchemaAgentChatSessionSummary
|
from app.schemas.agent import AgentChatSessionSummary as _SchemaAgentChatSessionSummary
|
||||||
from app.schemas.agent import AgentChatUploadAttachment as _SchemaAgentChatUploadAttachment
|
from app.schemas.agent import AgentChatUploadAttachment as _SchemaAgentChatUploadAttachment
|
||||||
from app.schemas.agent import AgentMcpServerListData as _SchemaAgentMcpServerListData
|
from app.schemas.agent import AgentMcpServerListData as _SchemaAgentMcpServerListData
|
||||||
|
from app.schemas.agent import AgentMcpServersSaveRequest as _SchemaAgentMcpServersSaveRequest
|
||||||
from app.schemas.agent import AgentMcpServerTestRequest as _SchemaAgentMcpServerTestRequest
|
from app.schemas.agent import AgentMcpServerTestRequest as _SchemaAgentMcpServerTestRequest
|
||||||
from app.schemas.agent import AgentMcpServerTestResult as _SchemaAgentMcpServerTestResult
|
from app.schemas.agent import AgentMcpServerTestResult as _SchemaAgentMcpServerTestResult
|
||||||
from app.schemas.agent import AgentMcpServersSaveRequest as _SchemaAgentMcpServersSaveRequest
|
|
||||||
from app.schemas.agent import AgentSessionStopData as _SchemaAgentSessionStopData
|
from app.schemas.agent import AgentSessionStopData as _SchemaAgentSessionStopData
|
||||||
from app.schemas.agent import AgentWebCallbackData as _SchemaAgentWebCallbackData
|
from app.schemas.agent import AgentWebCallbackData as _SchemaAgentWebCallbackData
|
||||||
from app.schemas.agent import AgentWebCommandInfo as _SchemaAgentWebCommandInfo
|
from app.schemas.agent import AgentWebCommandInfo as _SchemaAgentWebCommandInfo
|
||||||
@@ -32,52 +75,7 @@ from app.schemas.message import AgentWebChatRequest as _SchemaAgentWebChatReques
|
|||||||
from app.schemas.message import AgentWebChoiceRequest as _SchemaAgentWebChoiceRequest
|
from app.schemas.message import AgentWebChoiceRequest as _SchemaAgentWebChoiceRequest
|
||||||
from app.schemas.message import Message as _SchemaMessage
|
from app.schemas.message import Message as _SchemaMessage
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.schemas.types import NotificationChannel
|
||||||
from app.api.presentation.sse import build_sse_error_response, build_sse_response
|
|
||||||
from app.agent.contracts import ReplyMode, build_display_message
|
|
||||||
from app.agent.mcp import agent_mcp_manager
|
|
||||||
from app.agent.runtime_loader import get_moviepilot_agent_type
|
|
||||||
from app.application.agent import (
|
|
||||||
get_running_agent_manager,
|
|
||||||
is_audio_input_available,
|
|
||||||
transcribe_audio,
|
|
||||||
)
|
|
||||||
from app.chain.message import MessageChain
|
|
||||||
from app.application.commands import get_command, get_commands
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.api.dependencies.agent import (
|
|
||||||
get_agent_chat_persistence,
|
|
||||||
get_agent_chat_service,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.auth import get_current_active_user
|
|
||||||
from app.application.messaging.chat import (
|
|
||||||
AgentChatRecord,
|
|
||||||
AgentChatPersistenceService,
|
|
||||||
AgentChatService,
|
|
||||||
get_configured_agent_chat_service,
|
|
||||||
get_configured_agent_chat_persistence,
|
|
||||||
)
|
|
||||||
from app.application.security.user import get_configured_user_id_lookup
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.application.messaging.agent import (
|
|
||||||
attach_web_agent_message_queue,
|
|
||||||
attach_web_agent_edit_queue,
|
|
||||||
create_web_agent_background_task,
|
|
||||||
detach_web_agent_message_queue,
|
|
||||||
detach_web_agent_edit_queue,
|
|
||||||
is_web_agent_message_for_user,
|
|
||||||
)
|
|
||||||
from app.application.messaging.agent import agent_interaction_manager
|
|
||||||
from app.application.messaging.agent import (
|
|
||||||
build_agent_choice_button_rows,
|
|
||||||
normalize_web_agent_button_rows,
|
|
||||||
parse_agent_choice_callback,
|
|
||||||
)
|
|
||||||
from app.application.messaging.router import has_pending_interaction
|
|
||||||
from app.runtime.localization import LocaleHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.types import EventType, NotificationChannel
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
@@ -2274,7 +2272,7 @@ async def _web_agent_stream_impl(
|
|||||||
{"session_id": session_id},
|
{"session_id": session_id},
|
||||||
locale=locale,
|
locale=locale,
|
||||||
)
|
)
|
||||||
while not global_vars.is_system_stopped:
|
while not runtime_stop_state.is_system_stopped:
|
||||||
if await request.is_disconnected():
|
if await request.is_disconnected():
|
||||||
disconnected = True
|
disconnected = True
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ from typing import Annotated, Optional
|
|||||||
|
|
||||||
from fastapi import Depends, Query
|
from fastapi import Depends, Query
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.anilist import AniListChain
|
from app.chain.anilist import AniListChain
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -5,15 +5,15 @@ from typing import AsyncIterator, List, Optional
|
|||||||
from fastapi import APIRouter, Depends, Header, Security
|
from fastapi import APIRouter, Depends, Header, Security
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
from app.schemas.openai import AnthropicErrorDetail as _SchemaAnthropicErrorDetail
|
from app.adapters.web.security.access import anthropic_api_key_header
|
||||||
from app.schemas.openai import AnthropicErrorResponse as _SchemaAnthropicErrorResponse
|
from app.api.context import (
|
||||||
from app.schemas.openai import AnthropicMessagesRequest as _SchemaAnthropicMessagesRequest
|
get_background_task_registry_compat,
|
||||||
from app.schemas.openai import AnthropicMessagesResponse as _SchemaAnthropicMessagesResponse
|
resolve_background_task_registry,
|
||||||
from app.schemas.openai import AnthropicTextBlock as _SchemaAnthropicTextBlock
|
)
|
||||||
from app.api.endpoints.openai import (
|
from app.api.endpoints.openai import (
|
||||||
MODEL_ID,
|
MODEL_ID,
|
||||||
_is_manager_unavailable,
|
|
||||||
_is_manager_queue_full,
|
_is_manager_queue_full,
|
||||||
|
_is_manager_unavailable,
|
||||||
_run_managed_agent,
|
_run_managed_agent,
|
||||||
)
|
)
|
||||||
from app.api.openai_utils import (
|
from app.api.openai_utils import (
|
||||||
@@ -24,12 +24,12 @@ from app.api.openai_utils import (
|
|||||||
from app.api.presentation.sse import build_sse_response, encode_named_event
|
from app.api.presentation.sse import build_sse_response, encode_named_event
|
||||||
from app.application.agent import get_running_agent_manager
|
from app.application.agent import get_running_agent_manager
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
from app.adapters.web.security.access import anthropic_api_key_header
|
|
||||||
from app.api.context import (
|
|
||||||
get_background_task_registry_compat,
|
|
||||||
resolve_background_task_registry,
|
|
||||||
)
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
from app.schemas.openai import AnthropicErrorDetail as _SchemaAnthropicErrorDetail
|
||||||
|
from app.schemas.openai import AnthropicErrorResponse as _SchemaAnthropicErrorResponse
|
||||||
|
from app.schemas.openai import AnthropicMessagesRequest as _SchemaAnthropicMessagesRequest
|
||||||
|
from app.schemas.openai import AnthropicMessagesResponse as _SchemaAnthropicMessagesResponse
|
||||||
|
from app.schemas.openai import AnthropicTextBlock as _SchemaAnthropicTextBlock
|
||||||
|
|
||||||
ANTHROPIC_ERROR_RESPONSES = {
|
ANTHROPIC_ERROR_RESPONSES = {
|
||||||
400: {"model": _SchemaAnthropicErrorResponse, "description": "请求格式错误"},
|
400: {"model": _SchemaAnthropicErrorResponse, "description": "请求格式错误"},
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ from typing import Any
|
|||||||
from fastapi import Depends, HTTPException
|
from fastapi import Depends, HTTPException
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from app.api.dependencies.auth import get_auth_service
|
||||||
|
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
||||||
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
|
from app.application.security.auth import AuthService, consume_plugin_auth_ticket
|
||||||
from app.schemas.token import Token as _SchemaToken
|
from app.schemas.token import Token as _SchemaToken
|
||||||
from app.schemas.user import AuthProviderInfo as _SchemaAuthProviderInfo
|
from app.schemas.user import AuthProviderInfo as _SchemaAuthProviderInfo
|
||||||
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
|
||||||
from app.application.security.auth import AuthService, consume_plugin_auth_ticket
|
|
||||||
from app.application.plugin.runtime import get_plugin_manager
|
|
||||||
from app.api.dependencies.auth import get_auth_service
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
from typing import List, Any, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.bangumi import BangumiChain
|
from app.chain.bangumi import BangumiChain
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,20 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, List, Optional, Annotated
|
from typing import Annotated, Any, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.adapters.web.security.access import verify_apitoken
|
||||||
|
from app.api.context import get_api_runtime_config, resolve_api_runtime_config
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
|
from app.api.dependencies.history import get_dashboard_query_service
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import ApiRuntimeConfig
|
||||||
|
from app.application.dashboard import DashboardQueryService
|
||||||
|
from app.application.directory import DirectoryHelper
|
||||||
|
from app.application.scheduling import get_scheduler
|
||||||
|
from app.chain.dashboard import DashboardChain
|
||||||
|
from app.chain.storage import StorageChain
|
||||||
from app.runtime.execution import run_in_threadpool
|
from app.runtime.execution import run_in_threadpool
|
||||||
from app.schemas.dashboard import DashboardMemoryInfo as _SchemaDashboardMemoryInfo
|
from app.schemas.dashboard import DashboardMemoryInfo as _SchemaDashboardMemoryInfo
|
||||||
from app.schemas.dashboard import DashboardSystemInfo as _SchemaDashboardSystemInfo
|
from app.schemas.dashboard import DashboardSystemInfo as _SchemaDashboardSystemInfo
|
||||||
@@ -13,19 +25,7 @@ from app.schemas.dashboard import ScheduleProgress as _SchemaScheduleProgress
|
|||||||
from app.schemas.dashboard import Statistic as _SchemaStatistic
|
from app.schemas.dashboard import Statistic as _SchemaStatistic
|
||||||
from app.schemas.dashboard import Storage as _SchemaStorage
|
from app.schemas.dashboard import Storage as _SchemaStorage
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.dashboard import DashboardChain
|
|
||||||
from app.chain.storage import StorageChain
|
|
||||||
from app.api.context import get_api_runtime_config, resolve_api_runtime_config
|
|
||||||
from app.application.configuration import ApiRuntimeConfig
|
|
||||||
from app.adapters.web.security.access import verify_apitoken
|
|
||||||
from app.api.dependencies.auth import get_current_active_superuser
|
|
||||||
from app.api.dependencies.history import get_dashboard_query_service
|
|
||||||
from app.application.dashboard import DashboardQueryService
|
|
||||||
from app.schemas.types import StorageAction
|
from app.schemas.types import StorageAction
|
||||||
from app.application.directory import DirectoryHelper
|
|
||||||
from app.application.scheduling import get_scheduler
|
|
||||||
from app.adapters.system.host import SystemUtils
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ from typing import Any, List, Optional
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.event import DiscoverMediaSource as _SchemaDiscoverMediaSource
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.bangumi import BangumiChain
|
from app.chain.bangumi import BangumiChain
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.runtime.events import eventmanager
|
from app.runtime.events import eventmanager
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.schemas.event import DiscoverMediaSource as _SchemaDiscoverMediaSource
|
||||||
from app.schemas.event import DiscoverSourceEventData
|
from app.schemas.event import DiscoverSourceEventData
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.types import ChainEventType, MediaType
|
from app.schemas.types import ChainEventType, MediaType
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ from typing import Any, List, Optional
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,26 @@
|
|||||||
from typing import Any, List, Annotated, Optional, Union
|
from typing import Annotated, Any, List, Optional, Union
|
||||||
|
|
||||||
from fastapi import Depends, Body
|
from fastapi import Body, Depends
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_token
|
||||||
|
from app.api.dependencies.auth import get_current_active_user
|
||||||
|
from app.api.dependencies.site import get_site_sync_query_service
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
|
from app.application.directory import DirectoryHelper
|
||||||
|
from app.application.security.url import SecurityUtils
|
||||||
|
from app.application.site.query import (
|
||||||
|
SiteQueryService,
|
||||||
|
get_configured_site_query_service,
|
||||||
|
)
|
||||||
|
from app.chain.download import DownloadChain
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.domain.context import Context, MediaInfo, MusicInfo, SubtitleInfo, TorrentInfo
|
||||||
|
from app.domain.media import is_music_media_source, normalize_music_type
|
||||||
|
from app.domain.meta.metabase import MetaBase
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.schemas.common import ServiceClientInfo as _SchemaServiceClientInfo
|
from app.schemas.common import ServiceClientInfo as _SchemaServiceClientInfo
|
||||||
from app.schemas.download import DownloadAddedData as _SchemaDownloadAddedData
|
from app.schemas.download import DownloadAddedData as _SchemaDownloadAddedData
|
||||||
from app.schemas.download import DownloadDirectory as _SchemaDownloadDirectory
|
from app.schemas.download import DownloadDirectory as _SchemaDownloadDirectory
|
||||||
@@ -13,24 +32,6 @@ from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
|||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.transfer import DownloaderTorrent as _SchemaDownloaderTorrent
|
from app.schemas.transfer import DownloaderTorrent as _SchemaDownloaderTorrent
|
||||||
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.download import DownloadChain
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.domain.context import Context, MediaInfo, MusicInfo, SubtitleInfo, TorrentInfo
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.adapters.web.security.access import verify_token
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.application.site.query import (
|
|
||||||
SiteQueryService,
|
|
||||||
get_configured_site_query_service,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.auth import get_current_active_user
|
|
||||||
from app.api.dependencies.site import get_site_sync_query_service
|
|
||||||
from app.application.directory import DirectoryHelper
|
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
MediaSource,
|
MediaSource,
|
||||||
@@ -38,8 +39,7 @@ from app.schemas.types import (
|
|||||||
MusicTargetEntityType,
|
MusicTargetEntityType,
|
||||||
SystemConfigKey,
|
SystemConfigKey,
|
||||||
)
|
)
|
||||||
from app.domain.media import is_music_media_source, normalize_music_type
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
from app.application.security.url import SecurityUtils
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,21 @@
|
|||||||
import time
|
import time
|
||||||
from collections.abc import Coroutine
|
from collections.abc import Coroutine
|
||||||
from typing import List, Any, Callable, Optional
|
from typing import Any, Callable, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.common import BatchProgressKeyData as _SchemaBatchProgressKeyData
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.common import ProgressKeyData as _SchemaProgressKeyData
|
|
||||||
from app.schemas.history import BatchTransferHistoryRedoRequest as _SchemaBatchTransferHistoryRedoRequest
|
|
||||||
from app.schemas.history import TransferHistory as _SchemaTransferHistory
|
|
||||||
from app.schemas.history import TransferHistoryPage as _SchemaTransferHistoryPage
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.history import DownloadHistory as _SchemaDownloadHistory
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.agent.contracts import ReplyMode
|
from app.agent.contracts import ReplyMode
|
||||||
from app.application.agent import get_running_agent_manager
|
|
||||||
from app.agent.prompt.transfer_redo import (
|
from app.agent.prompt.transfer_redo import (
|
||||||
build_batch_manual_redo_prompt,
|
build_batch_manual_redo_prompt,
|
||||||
build_manual_redo_prompt,
|
build_manual_redo_prompt,
|
||||||
)
|
)
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.api.context import (
|
from app.api.context import (
|
||||||
get_api_runtime_config,
|
get_api_runtime_config,
|
||||||
get_background_task_registry,
|
get_background_task_registry,
|
||||||
resolve_api_runtime_config,
|
resolve_api_runtime_config,
|
||||||
resolve_background_task_registry,
|
resolve_background_task_registry,
|
||||||
)
|
)
|
||||||
from app.application.configuration import ApiRuntimeConfig
|
|
||||||
from app.adapters.web.security.access import verify_token
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
@@ -37,14 +25,26 @@ from app.api.dependencies.history import (
|
|||||||
get_history_query_service,
|
get_history_query_service,
|
||||||
get_transfer_history_mutation_command,
|
get_transfer_history_mutation_command,
|
||||||
)
|
)
|
||||||
from app.runtime.progress import AsyncProgressHelper
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.agent import get_running_agent_manager
|
||||||
|
from app.application.configuration import ApiRuntimeConfig
|
||||||
from app.application.history import (
|
from app.application.history import (
|
||||||
DownloadHistoryMutationCommand,
|
DownloadHistoryMutationCommand,
|
||||||
HistoryQueryService,
|
HistoryQueryService,
|
||||||
TransferHistoryMutationCommand,
|
TransferHistoryMutationCommand,
|
||||||
)
|
)
|
||||||
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.progress import AsyncProgressHelper
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
from app.schemas.common import BatchProgressKeyData as _SchemaBatchProgressKeyData
|
||||||
|
from app.schemas.common import ProgressKeyData as _SchemaProgressKeyData
|
||||||
|
from app.schemas.history import BatchTransferHistoryRedoRequest as _SchemaBatchTransferHistoryRedoRequest
|
||||||
|
from app.schemas.history import DownloadHistory as _SchemaDownloadHistory
|
||||||
|
from app.schemas.history import TransferHistory as _SchemaTransferHistory
|
||||||
|
from app.schemas.history import TransferHistoryPage as _SchemaTransferHistoryPage
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ from typing import Any, Dict, List, Optional, Union
|
|||||||
from fastapi import Depends, Request, Response
|
from fastapi import Depends, Request, Response
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
|
from app.agent.llm.gateway import resolve_llm_provider_runtime
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.api.dependencies.auth import get_current_active_superuser_async
|
|
||||||
from app.agent.llm.gateway import resolve_llm_provider_runtime
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -1,22 +1,22 @@
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any, List, Annotated
|
from typing import Annotated, Any, List
|
||||||
|
|
||||||
from fastapi import Depends, Form, HTTPException, Request, Response
|
from fastapi import Depends, Form, HTTPException, Request, Response
|
||||||
from fastapi.security import OAuth2PasswordRequestForm
|
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
|
from fastapi.security import OAuth2PasswordRequestForm
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import set_or_refresh_resource_token_cookie
|
||||||
|
from app.api.context import get_api_runtime_config, resolve_api_runtime_config
|
||||||
|
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
||||||
|
from app.application.configuration import ApiRuntimeConfig, get_configured_system_config
|
||||||
|
from app.application.image import WallpaperHelper
|
||||||
|
from app.application.security.token import create_access_token
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
|
from app.chain.user import MfaRequired, UserChain
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import MfaChallenge as _SchemaMfaChallenge
|
from app.schemas.token import MfaChallenge as _SchemaMfaChallenge
|
||||||
from app.schemas.token import Token as _SchemaToken
|
from app.schemas.token import Token as _SchemaToken
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
|
||||||
from app.chain.user import MfaRequired, UserChain
|
|
||||||
from app.adapters.web.security.access import set_or_refresh_resource_token_cookie
|
|
||||||
from app.application.security.token import create_access_token
|
|
||||||
from app.api.context import get_api_runtime_config, resolve_api_runtime_config
|
|
||||||
from app.application.configuration import ApiRuntimeConfig, get_configured_system_config
|
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
|
||||||
from app.application.image import WallpaperHelper
|
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
from typing import List, Any, Dict, Annotated, Union
|
from typing import Annotated, Any, Dict, List, Union
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException, Request
|
from fastapi import Depends, HTTPException, Request
|
||||||
from fastapi.responses import JSONResponse, Response
|
from fastapi.responses import JSONResponse, Response
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_apikey
|
||||||
|
from app.agent.tools.manager import moviepilot_tool_manager
|
||||||
|
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.version import get_app_version
|
||||||
from app.schemas.mcp import MCP_JSONRPC_REQUEST_SCHEMA as _SchemaMCP_JSONRPC_REQUEST_SCHEMA
|
from app.schemas.mcp import MCP_JSONRPC_REQUEST_SCHEMA as _SchemaMCP_JSONRPC_REQUEST_SCHEMA
|
||||||
from app.schemas.mcp import McpJsonRpcError as _SchemaMcpJsonRpcError
|
from app.schemas.mcp import McpJsonRpcError as _SchemaMcpJsonRpcError
|
||||||
from app.schemas.mcp import McpJsonRpcResponse as _SchemaMcpJsonRpcResponse
|
from app.schemas.mcp import McpJsonRpcResponse as _SchemaMcpJsonRpcResponse
|
||||||
@@ -11,11 +16,6 @@ from app.schemas.mcp import McpToolInfo as _SchemaMcpToolInfo
|
|||||||
from app.schemas.mcp import ToolCallData as _SchemaToolCallData
|
from app.schemas.mcp import ToolCallData as _SchemaToolCallData
|
||||||
from app.schemas.mcp import ToolCallRequest as _SchemaToolCallRequest
|
from app.schemas.mcp import ToolCallRequest as _SchemaToolCallRequest
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
|
||||||
from app.agent.tools.manager import moviepilot_tool_manager
|
|
||||||
from app.adapters.web.security.access import verify_apikey
|
|
||||||
from app.runtime.version import get_app_version
|
|
||||||
from app.runtime.log import logger
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+19
-19
@@ -5,36 +5,36 @@ from uuid import UUID
|
|||||||
from fastapi import Depends, Query
|
from fastapi import Depends, Query
|
||||||
from pydantic import BeforeValidator
|
from pydantic import BeforeValidator
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_apitoken, verify_token
|
||||||
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_user,
|
||||||
|
)
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.scraping import ScrapingChain
|
||||||
|
from app.chain.tmdb import TmdbChain
|
||||||
|
from app.domain.context import Context, MusicInfo
|
||||||
|
from app.domain.media import is_music_media_source, normalize_music_type, parse_media_source_selection
|
||||||
|
from app.domain.meta.metabase import MetaBase
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
||||||
|
from app.schemas.category import CategoryConfig
|
||||||
from app.schemas.category import CategoryConfig as _SchemaCategoryConfig
|
from app.schemas.category import CategoryConfig as _SchemaCategoryConfig
|
||||||
from app.schemas.category import MediaCategoryMap as _SchemaMediaCategoryMap
|
from app.schemas.category import MediaCategoryMap as _SchemaMediaCategoryMap
|
||||||
from app.schemas.context import MediaEpisodeGroup as _SchemaMediaEpisodeGroup
|
from app.schemas.context import MediaEpisodeGroup as _SchemaMediaEpisodeGroup
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
from app.schemas.context import MediaSearchResults as _SchemaMediaSearchResults
|
from app.schemas.context import MediaSearchResults as _SchemaMediaSearchResults
|
||||||
from app.schemas.context import MediaSeason as _SchemaMediaSeason
|
from app.schemas.context import MediaSeason as _SchemaMediaSeason
|
||||||
|
from app.schemas.event import MediaSourceInfo as _SchemaMediaSourceInfo
|
||||||
|
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource, MediaType
|
||||||
from app.schemas.workflow import Context as _SchemaContext
|
from app.schemas.workflow import Context as _SchemaContext
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.scraping import ScrapingChain
|
|
||||||
from app.chain.tmdb import TmdbChain
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.domain.context import Context, MusicInfo
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
|
||||||
from app.api.dependencies.auth import (
|
|
||||||
get_current_active_superuser,
|
|
||||||
get_current_active_user,
|
|
||||||
)
|
|
||||||
from app.schemas.category import CategoryConfig
|
|
||||||
from app.schemas.event import MediaSourceInfo as _SchemaMediaSourceInfo
|
|
||||||
from app.schemas.types import MUSIC_ENTITY_RECORDING, MediaSource, MediaType
|
|
||||||
from app.domain.media import is_music_media_source, normalize_music_type, parse_media_source_selection
|
|
||||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -2,32 +2,32 @@ from typing import Any, List, Optional
|
|||||||
|
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_token
|
||||||
|
from app.api.dependencies.history import get_mediaserver_query_service
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
|
from app.application.mediaserver import (
|
||||||
|
MediaServerQueryService,
|
||||||
|
get_mediaserver_configs,
|
||||||
|
)
|
||||||
|
from app.chain.download import DownloadChain
|
||||||
|
from app.chain.mediaserver import MediaServerChain
|
||||||
|
from app.domain.context import MediaInfo
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.schemas.common import ServiceClientInfo as _SchemaServiceClientInfo
|
from app.schemas.common import ServiceClientInfo as _SchemaServiceClientInfo
|
||||||
|
from app.schemas.media import build_media_key, resolve_media_identity
|
||||||
from app.schemas.mediaserver import ExistMediaInfo as _SchemaExistMediaInfo
|
from app.schemas.mediaserver import ExistMediaInfo as _SchemaExistMediaInfo
|
||||||
from app.schemas.mediaserver import MediaServerExistingEpisodes as _SchemaMediaServerExistingEpisodes
|
from app.schemas.mediaserver import MediaServerExistingEpisodes as _SchemaMediaServerExistingEpisodes
|
||||||
from app.schemas.mediaserver import MediaServerExistsData as _SchemaMediaServerExistsData
|
from app.schemas.mediaserver import MediaServerExistsData as _SchemaMediaServerExistsData
|
||||||
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
|
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
|
||||||
from app.schemas.mediaserver import MediaServerPlayData as _SchemaMediaServerPlayData
|
from app.schemas.mediaserver import MediaServerPlayData as _SchemaMediaServerPlayData
|
||||||
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
|
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
|
||||||
|
from app.schemas.mediaserver import NotExistMediaInfo
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo as _SchemaNotExistMediaInfo
|
from app.schemas.mediaserver import NotExistMediaInfo as _SchemaNotExistMediaInfo
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.download import DownloadChain
|
|
||||||
from app.chain.mediaserver import MediaServerChain
|
|
||||||
from app.domain.context import MediaInfo
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.adapters.web.security.access import verify_token
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.application.mediaserver import (
|
|
||||||
MediaServerQueryService,
|
|
||||||
get_mediaserver_configs,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.history import get_mediaserver_query_service
|
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo
|
|
||||||
from app.schemas.types import MediaSource, MediaType, SystemConfigKey
|
from app.schemas.types import MediaSource, MediaType, SystemConfigKey
|
||||||
from app.schemas.media import build_media_key, resolve_media_identity
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,23 @@ from typing import Annotated, Any, List, Optional, Protocol, Union
|
|||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
from starlette.responses import PlainTextResponse
|
from starlette.responses import PlainTextResponse
|
||||||
|
|
||||||
|
from app.adapters.external.wechat_crypt import WXBizMsgCrypt
|
||||||
|
from app.adapters.web.security.access import verify_apitoken, verify_token
|
||||||
|
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
||||||
|
from app.api.dependencies.agent import get_message_query_service
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import (
|
||||||
|
get_api_runtime_config_snapshot,
|
||||||
|
get_configured_system_config,
|
||||||
|
)
|
||||||
|
from app.application.messaging.message import MessageQueryService
|
||||||
|
from app.application.notification import get_notification_configs
|
||||||
|
from app.chain.message import MessageChain
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
from app.schemas.message import MessageClearBefore as _SchemaMessageClearBefore
|
from app.schemas.message import MessageClearBefore as _SchemaMessageClearBefore
|
||||||
from app.schemas.message import MessageClearData as _SchemaMessageClearData
|
from app.schemas.message import MessageClearData as _SchemaMessageClearData
|
||||||
from app.schemas.message import MessageClearScope as _SchemaMessageClearScope
|
from app.schemas.message import MessageClearScope as _SchemaMessageClearScope
|
||||||
@@ -16,24 +33,7 @@ from app.schemas.message import SubscriptionMessage as _SchemaSubscriptionMessag
|
|||||||
from app.schemas.message import WebMessageItem as _SchemaWebMessageItem
|
from app.schemas.message import WebMessageItem as _SchemaWebMessageItem
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.message import MessageChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.application.configuration import (
|
|
||||||
get_api_runtime_config_snapshot,
|
|
||||||
get_configured_system_config,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.agent import get_message_query_service
|
|
||||||
from app.api.dependencies.auth import get_current_active_superuser
|
|
||||||
from app.application.messaging.message import MessageQueryService
|
|
||||||
from app.application.notification import get_notification_configs
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.adapters.external.wechat_crypt import WXBizMsgCrypt
|
|
||||||
from app.schemas.types import NotificationChannel, SystemConfigKey
|
from app.schemas.types import NotificationChannel, SystemConfigKey
|
||||||
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+29
-29
@@ -4,10 +4,37 @@ MFA (Multi-Factor Authentication) API 端点
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from typing import Any, Annotated, Optional
|
from typing import Annotated, Any, Optional
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException, Body, Request, Response
|
from fastapi import Body, Depends, HTTPException, Request, Response
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import set_or_refresh_resource_token_cookie
|
||||||
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_user,
|
||||||
|
get_current_active_user_async,
|
||||||
|
get_passkey_service,
|
||||||
|
get_user_service,
|
||||||
|
)
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
||||||
|
from app.application.security.auth import get_configured_auth_service
|
||||||
|
from app.application.security.otp import OtpUtils
|
||||||
|
from app.application.security.passkey import (
|
||||||
|
PasskeyChallengeStore,
|
||||||
|
PassKeyHelper,
|
||||||
|
PassKeyRegistrationOriginMismatchError,
|
||||||
|
PassKeyRegistrationVerificationError,
|
||||||
|
)
|
||||||
|
from app.application.security.passkeys import (
|
||||||
|
PasskeyService,
|
||||||
|
)
|
||||||
|
from app.application.security.token import verify_password
|
||||||
|
from app.application.security.user import (
|
||||||
|
UserService,
|
||||||
|
get_configured_user_id_lookup,
|
||||||
|
get_configured_user_name_lookup,
|
||||||
|
)
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.mcp import BaseModel as _SchemaBaseModel
|
from app.schemas.mcp import BaseModel as _SchemaBaseModel
|
||||||
from app.schemas.mcp import JsonData as _SchemaJsonData
|
from app.schemas.mcp import JsonData as _SchemaJsonData
|
||||||
from app.schemas.mfa import MfaStatusData as _SchemaMfaStatusData
|
from app.schemas.mfa import MfaStatusData as _SchemaMfaStatusData
|
||||||
@@ -17,33 +44,6 @@ from app.schemas.mfa import PasskeyStartData as _SchemaPasskeyStartData
|
|||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import Token as _SchemaToken
|
from app.schemas.token import Token as _SchemaToken
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.api.response import RAW_RESPONSE_OPENAPI_KEY, ResponseAPIRouter
|
|
||||||
from app.adapters.web.security.access import set_or_refresh_resource_token_cookie
|
|
||||||
from app.application.security.token import verify_password
|
|
||||||
from app.application.security.auth import get_configured_auth_service
|
|
||||||
from app.application.security.user import UserService
|
|
||||||
from app.application.security.user import (
|
|
||||||
get_configured_user_id_lookup,
|
|
||||||
get_configured_user_name_lookup,
|
|
||||||
)
|
|
||||||
from app.application.security.passkeys import (
|
|
||||||
PasskeyService,
|
|
||||||
)
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.api.dependencies.auth import (
|
|
||||||
get_current_active_user,
|
|
||||||
get_current_active_user_async,
|
|
||||||
get_user_service,
|
|
||||||
get_passkey_service,
|
|
||||||
)
|
|
||||||
from app.application.security.passkey import (
|
|
||||||
PassKeyHelper,
|
|
||||||
PassKeyRegistrationOriginMismatchError,
|
|
||||||
PassKeyRegistrationVerificationError,
|
|
||||||
PasskeyChallengeStore,
|
|
||||||
)
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.application.security.otp import OtpUtils
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -2,6 +2,18 @@ from typing import Annotated, Optional
|
|||||||
|
|
||||||
from fastapi import Depends, HTTPException, Query
|
from fastapi import Depends, HTTPException, Query
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_token
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.chain.listenbrainz import (
|
||||||
|
LISTENBRAINZ_CHART_RANGES,
|
||||||
|
LISTENBRAINZ_FRESH_MAX_DAYS,
|
||||||
|
LISTENBRAINZ_FRESH_SORTS,
|
||||||
|
)
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.musicbrainz import MusicBrainzChain
|
||||||
|
from app.chain.recommend import RecommendChain
|
||||||
|
from app.domain.context import MusicAlbumInfo, MusicArtistInfo, MusicInfo
|
||||||
from app.schemas.music import MusicAlbumInfo as _SchemaMusicAlbumInfo
|
from app.schemas.music import MusicAlbumInfo as _SchemaMusicAlbumInfo
|
||||||
from app.schemas.music import MusicArtistInfo as _SchemaMusicArtistInfo
|
from app.schemas.music import MusicArtistInfo as _SchemaMusicArtistInfo
|
||||||
from app.schemas.music import MusicRecognitionCacheData as _SchemaMusicRecognitionCacheData
|
from app.schemas.music import MusicRecognitionCacheData as _SchemaMusicRecognitionCacheData
|
||||||
@@ -9,19 +21,7 @@ from app.schemas.music import MusicRecognizeRequest as _SchemaMusicRecognizeRequ
|
|||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.recommend import RecommendChain
|
|
||||||
from app.schemas.types import MediaSource, MediaType
|
from app.schemas.types import MediaSource, MediaType
|
||||||
from app.domain.context import MusicAlbumInfo, MusicArtistInfo, MusicInfo
|
|
||||||
from app.adapters.web.security.access import verify_token
|
|
||||||
from app.api.dependencies.auth import get_current_active_superuser_async
|
|
||||||
from app.chain.listenbrainz import (
|
|
||||||
LISTENBRAINZ_CHART_RANGES,
|
|
||||||
LISTENBRAINZ_FRESH_MAX_DAYS,
|
|
||||||
LISTENBRAINZ_FRESH_SORTS,
|
|
||||||
)
|
|
||||||
from app.chain.musicbrainz import MusicBrainzChain
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ from typing import Any, Dict
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
from app.api.dependencies.auth import get_current_active_superuser
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.notification import NotificationChain
|
from app.chain.notification import NotificationChain
|
||||||
from app.api.dependencies.auth import get_current_active_superuser
|
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+17
-17
@@ -8,6 +8,23 @@ from fastapi import APIRouter, Depends, Request, Security
|
|||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.security import HTTPAuthorizationCredentials
|
from fastapi.security import HTTPAuthorizationCredentials
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import openai_bearer_scheme
|
||||||
|
from app.agent.contracts import ReplyMode
|
||||||
|
from app.agent.runtime_loader import get_moviepilot_agent_type
|
||||||
|
from app.api.context import (
|
||||||
|
get_background_task_registry_compat,
|
||||||
|
resolve_background_task_registry,
|
||||||
|
)
|
||||||
|
from app.api.openai_utils import (
|
||||||
|
build_completion_payload,
|
||||||
|
build_prompt,
|
||||||
|
build_responses_input,
|
||||||
|
build_session_id,
|
||||||
|
)
|
||||||
|
from app.api.presentation.sse import build_sse_response, encode_data_event
|
||||||
|
from app.application.agent import get_running_agent_manager
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
from app.schemas.openai import OpenAIChatCompletionResponse as _SchemaOpenAIChatCompletionResponse
|
from app.schemas.openai import OpenAIChatCompletionResponse as _SchemaOpenAIChatCompletionResponse
|
||||||
from app.schemas.openai import OpenAIChatCompletionsRequest as _SchemaOpenAIChatCompletionsRequest
|
from app.schemas.openai import OpenAIChatCompletionsRequest as _SchemaOpenAIChatCompletionsRequest
|
||||||
from app.schemas.openai import OpenAIErrorDetail as _SchemaOpenAIErrorDetail
|
from app.schemas.openai import OpenAIErrorDetail as _SchemaOpenAIErrorDetail
|
||||||
@@ -19,24 +36,7 @@ from app.schemas.openai import OpenAIResponsesOutputText as _SchemaOpenAIRespons
|
|||||||
from app.schemas.openai import OpenAIResponsesRequest as _SchemaOpenAIResponsesRequest
|
from app.schemas.openai import OpenAIResponsesRequest as _SchemaOpenAIResponsesRequest
|
||||||
from app.schemas.openai import OpenAIResponsesResponse as _SchemaOpenAIResponsesResponse
|
from app.schemas.openai import OpenAIResponsesResponse as _SchemaOpenAIResponsesResponse
|
||||||
from app.schemas.openai import OpenAIUsage as _SchemaOpenAIUsage
|
from app.schemas.openai import OpenAIUsage as _SchemaOpenAIUsage
|
||||||
from app.api.openai_utils import (
|
|
||||||
build_completion_payload,
|
|
||||||
build_prompt,
|
|
||||||
build_responses_input,
|
|
||||||
build_session_id,
|
|
||||||
)
|
|
||||||
from app.api.presentation.sse import build_sse_response, encode_data_event
|
|
||||||
from app.agent.runtime_loader import get_moviepilot_agent_type
|
|
||||||
from app.application.agent import get_running_agent_manager
|
|
||||||
from app.agent.contracts import ReplyMode
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.adapters.web.security.access import openai_bearer_scheme
|
|
||||||
from app.schemas.types import NotificationChannel
|
from app.schemas.types import NotificationChannel
|
||||||
from app.api.context import (
|
|
||||||
get_background_task_registry_compat,
|
|
||||||
resolve_background_task_registry,
|
|
||||||
)
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
|
||||||
|
|
||||||
OPENAI_ERROR_RESPONSES = {
|
OPENAI_ERROR_RESPONSES = {
|
||||||
400: {"model": _SchemaOpenAIErrorResponse, "description": "请求格式错误"},
|
400: {"model": _SchemaOpenAIErrorResponse, "description": "请求格式错误"},
|
||||||
|
|||||||
+38
-39
@@ -9,11 +9,48 @@ from fastapi import Depends, Header, HTTPException, Security
|
|||||||
from starlette import status
|
from starlette import status
|
||||||
from starlette.responses import StreamingResponse
|
from starlette.responses import StreamingResponse
|
||||||
|
|
||||||
|
from app.adapters.external.market import PluginHelper
|
||||||
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.adapters.system.plugin.package import PluginPackageManager
|
||||||
|
from app.adapters.web.security.access import (
|
||||||
|
resource_token_cookie,
|
||||||
|
verify_resource_token,
|
||||||
|
verify_token,
|
||||||
|
)
|
||||||
|
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
||||||
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_superuser_async,
|
||||||
|
)
|
||||||
|
from app.api.dependencies.plugin import (
|
||||||
|
get_plugin_config_command,
|
||||||
|
)
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.commands import init_commands
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot, get_configured_system_config
|
||||||
|
from app.application.plugin.config import PluginConfigCommand
|
||||||
|
from app.application.plugin.folders import remove_plugin_from_folders
|
||||||
|
from app.application.plugin.install import PluginInstallCommand
|
||||||
|
from app.application.plugin.routes import register_plugin_api, remove_plugin_api
|
||||||
|
from app.application.plugin.runtime import PluginRuntime, get_plugin_manager
|
||||||
|
from app.application.scheduling import remove_plugin_job, update_plugin_job
|
||||||
|
from app.runtime.cache import async_fresh
|
||||||
from app.runtime.execution import run_in_threadpool
|
from app.runtime.execution import run_in_threadpool
|
||||||
|
from app.runtime.extensions.plugin.contracts import (
|
||||||
|
PluginDashboardError,
|
||||||
|
PluginNotFoundError,
|
||||||
|
)
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
from app.schemas.common import JsonObject as _SchemaJsonObject
|
from app.schemas.common import JsonObject as _SchemaJsonObject
|
||||||
|
from app.schemas.exception import (
|
||||||
|
PersistenceUnavailableError,
|
||||||
|
PluginMutationRejectedError,
|
||||||
|
)
|
||||||
from app.schemas.plugin import Plugin as _SchemaPlugin
|
from app.schemas.plugin import Plugin as _SchemaPlugin
|
||||||
from app.schemas.plugin import PluginDashboard as _SchemaPluginDashboard
|
|
||||||
from app.schemas.plugin import PluginCloneRequest as _SchemaPluginCloneRequest
|
from app.schemas.plugin import PluginCloneRequest as _SchemaPluginCloneRequest
|
||||||
|
from app.schemas.plugin import PluginDashboard as _SchemaPluginDashboard
|
||||||
from app.schemas.plugin import PluginDashboardMetaItem as _SchemaPluginDashboardMetaItem
|
from app.schemas.plugin import PluginDashboardMetaItem as _SchemaPluginDashboardMetaItem
|
||||||
from app.schemas.plugin import PluginFoldersData as _SchemaPluginFoldersData
|
from app.schemas.plugin import PluginFoldersData as _SchemaPluginFoldersData
|
||||||
from app.schemas.plugin import PluginRating as _SchemaPluginRating
|
from app.schemas.plugin import PluginRating as _SchemaPluginRating
|
||||||
@@ -26,45 +63,7 @@ from app.schemas.plugin import PluginRuntimeSummary as _SchemaPluginRuntimeSumma
|
|||||||
from app.schemas.plugin import PluginSidebarNavItem as _SchemaPluginSidebarNavItem
|
from app.schemas.plugin import PluginSidebarNavItem as _SchemaPluginSidebarNavItem
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.application.plugin.folders import remove_plugin_from_folders
|
|
||||||
from app.application.plugin.routes import register_plugin_api, remove_plugin_api
|
|
||||||
from app.application.plugin.install import PluginInstallCommand
|
|
||||||
from app.application.plugin.config import PluginConfigCommand
|
|
||||||
from app.application.commands import init_commands
|
|
||||||
from app.application.scheduling import remove_plugin_job, update_plugin_job
|
|
||||||
from app.runtime.cache import async_fresh
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.application.plugin.runtime import PluginRuntime, get_plugin_manager
|
|
||||||
from app.runtime.extensions.plugin.contracts import (
|
|
||||||
PluginDashboardError,
|
|
||||||
PluginNotFoundError,
|
|
||||||
)
|
|
||||||
from app.adapters.web.security.access import (
|
|
||||||
resource_token_cookie,
|
|
||||||
verify_resource_token,
|
|
||||||
verify_token,
|
|
||||||
)
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.api.dependencies.auth import (
|
|
||||||
get_current_active_superuser,
|
|
||||||
get_current_active_superuser_async,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.plugin import (
|
|
||||||
get_plugin_config_command,
|
|
||||||
)
|
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
|
||||||
from app.adapters.external.market import PluginHelper
|
|
||||||
from app.adapters.system.plugin.package import PluginPackageManager
|
|
||||||
from app.schemas.exception import (
|
|
||||||
PersistenceUnavailableError,
|
|
||||||
PluginMutationRejectedError,
|
|
||||||
)
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
_plugin_release_refresh_tasks: set[asyncio.Task] = set()
|
_plugin_release_refresh_tasks: set[asyncio.Task] = set()
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ from typing import Any, Awaitable, List, Optional
|
|||||||
|
|
||||||
from fastapi import Depends, HTTPException, status
|
from fastapi import Depends, HTTPException, status
|
||||||
|
|
||||||
from app.schemas.event import RecommendMediaSource as _SchemaRecommendMediaSource
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.chain.recommend import RecommendChain
|
from app.chain.recommend import RecommendChain
|
||||||
from app.runtime.events import eventmanager
|
from app.runtime.events import eventmanager
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.schemas.event import RecommendMediaSource as _SchemaRecommendMediaSource
|
||||||
from app.schemas.exception import TMDbException
|
|
||||||
from app.schemas.event import RecommendSourceEventData
|
from app.schemas.event import RecommendSourceEventData
|
||||||
|
from app.schemas.exception import TMDbException
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
from app.schemas.transfer import MusicInfo as _SchemaMusicInfo
|
||||||
from app.schemas.types import ChainEventType
|
from app.schemas.types import ChainEventType
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+10
-10
@@ -4,25 +4,25 @@ import time
|
|||||||
from typing import Any, AsyncIterator, Iterator, List, Optional
|
from typing import Any, AsyncIterator, Iterator, List, Optional
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from fastapi import Depends, Body, Request
|
from fastapi import Body, Depends, Request
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_resource_token, verify_token
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.security.url import SecurityUtils
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.domain.media import normalize_music_type
|
||||||
|
from app.runtime.localization import LocaleHelper
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.media import resolve_media_identity
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.search import SearchLastContextData as _SchemaSearchLastContextData
|
from app.schemas.search import SearchLastContextData as _SchemaSearchLastContextData
|
||||||
from app.schemas.search import SearchRecommendStatusData as _SchemaSearchRecommendStatusData
|
from app.schemas.search import SearchRecommendStatusData as _SchemaSearchRecommendStatusData
|
||||||
from app.schemas.search import SubtitleInfo as _SchemaSubtitleInfo
|
from app.schemas.search import SubtitleInfo as _SchemaSubtitleInfo
|
||||||
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.workflow import Context as _SchemaContext
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.search import SearchChain
|
|
||||||
from app.adapters.web.security.access import verify_resource_token, verify_token
|
|
||||||
from app.runtime.localization import LocaleHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.types import MediaSource, MediaType
|
from app.schemas.types import MediaSource, MediaType
|
||||||
from app.domain.media import normalize_music_type
|
from app.schemas.workflow import Context as _SchemaContext
|
||||||
from app.schemas.media import resolve_media_identity
|
|
||||||
from app.application.security.url import SecurityUtils
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+27
-28
@@ -1,31 +1,9 @@
|
|||||||
from typing import List, Any, Dict, Optional
|
from typing import Annotated, Any, Dict, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends, HTTPException
|
from fastapi import Depends, HTTPException
|
||||||
from typing import Annotated
|
|
||||||
|
|
||||||
from app.schemas.common import JsonObject as _SchemaJsonObject
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.schemas.site import SiteAuth as _SchemaSiteAuth
|
|
||||||
from app.schemas.site import SiteCategory as _SchemaSiteCategory
|
|
||||||
from app.schemas.site import SiteCookieUpdate as _SchemaSiteCookieUpdate
|
|
||||||
from app.schemas.site import SiteIconData as _SchemaSiteIconData
|
|
||||||
from app.schemas.site import SiteMappingData as _SchemaSiteMappingData
|
|
||||||
from app.schemas.site import SiteStatistic as _SchemaSiteStatistic
|
|
||||||
from app.schemas.site import SiteUserData as _SchemaSiteUserData
|
|
||||||
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import Site as _SchemaSite
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.application.site.mutation import SiteMutationCommand
|
|
||||||
from app.application.site.query import SiteQueryService
|
|
||||||
from app.api.endpoints.plugin import register_plugin_api
|
|
||||||
from app.chain.site import SiteChain
|
|
||||||
from app.chain.torrents import TorrentsChain
|
|
||||||
from app.application.commands import init_commands
|
|
||||||
from app.application.plugin.runtime import get_plugin_manager
|
|
||||||
from app.adapters.web.security.access import verify_token
|
from app.adapters.web.security.access import verify_token
|
||||||
from app.api.principal import ApiPrincipal
|
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_manage_user_async,
|
get_current_active_manage_user_async,
|
||||||
@@ -37,13 +15,34 @@ from app.api.dependencies.site import (
|
|||||||
get_site_query_service,
|
get_site_query_service,
|
||||||
get_site_sync_query_service,
|
get_site_sync_query_service,
|
||||||
)
|
)
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
from app.api.endpoints.plugin import register_plugin_api
|
||||||
from app.runtime.log import logger
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.commands import init_commands
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
from app.application.scheduling import get_scheduler
|
from app.application.scheduling import get_scheduler
|
||||||
from app.schemas.types import SystemConfigKey, MediaType
|
from app.application.site.mutation import SiteMutationCommand
|
||||||
|
from app.application.site.query import SiteQueryService
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
|
from app.chain.site import SiteChain
|
||||||
|
from app.chain.torrents import TorrentsChain
|
||||||
from app.domain import site as site_rules
|
from app.domain import site as site_rules
|
||||||
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
from app.runtime.log import logger
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
from app.schemas.common import JsonObject as _SchemaJsonObject
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.site import SiteAuth as _SchemaSiteAuth
|
||||||
|
from app.schemas.site import SiteCategory as _SchemaSiteCategory
|
||||||
|
from app.schemas.site import SiteCookieUpdate as _SchemaSiteCookieUpdate
|
||||||
|
from app.schemas.site import SiteIconData as _SchemaSiteIconData
|
||||||
|
from app.schemas.site import SiteMappingData as _SchemaSiteMappingData
|
||||||
|
from app.schemas.site import SiteStatistic as _SchemaSiteStatistic
|
||||||
|
from app.schemas.site import SiteUserData as _SchemaSiteUserData
|
||||||
|
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
|
from app.schemas.types import MediaType, SystemConfigKey
|
||||||
|
from app.schemas.workflow import Site as _SchemaSite
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ from typing import Any, Dict, List, Optional
|
|||||||
from fastapi import Depends, HTTPException
|
from fastapi import Depends, HTTPException
|
||||||
from starlette.responses import FileResponse, Response
|
from starlette.responses import FileResponse, Response
|
||||||
|
|
||||||
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.storage import StorageChain
|
|
||||||
from app.chain.transfer import TransferChain
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
)
|
)
|
||||||
from app.runtime.progress import ProgressHelper
|
from app.api.principal import ApiPrincipal
|
||||||
from app.schemas.types import ProgressKey
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.storage import StorageChain
|
||||||
|
from app.chain.transfer import TransferChain
|
||||||
from app.foundation import text as text_tools
|
from app.foundation import text as text_tools
|
||||||
|
from app.runtime.progress import ProgressHelper
|
||||||
|
from app.schemas.common import ManageRequest as _SchemaManageRequest
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.types import ProgressKey
|
||||||
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,14 @@
|
|||||||
from typing import List, Any, Annotated, Optional
|
from typing import Annotated, Any, List, Optional
|
||||||
|
|
||||||
import cn2an
|
import cn2an
|
||||||
from fastapi import Request, Depends, HTTPException, Header
|
from fastapi import Depends, Header, HTTPException, Request
|
||||||
|
|
||||||
from app.schemas.common import IdData as _SchemaIdData
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.adapters.web.security.access import verify_apitoken, verify_token
|
||||||
from app.schemas.subscribe import SubscrbieInfo as _SchemaSubscrbieInfo
|
|
||||||
from app.schemas.subscribe import SubscribeShare as _SchemaSubscribeShare
|
|
||||||
from app.schemas.subscribe import SubscribeShareStatistics as _SchemaSubscribeShareStatistics
|
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.schemas.workflow import Subscribe as _SchemaSubscribe
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.api.context import (
|
from app.api.context import (
|
||||||
get_background_task_registry,
|
get_background_task_registry,
|
||||||
resolve_background_task_registry,
|
resolve_background_task_registry,
|
||||||
)
|
)
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.runtime.events import eventmanager
|
|
||||||
from app.domain.context import MediaInfo
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
|
||||||
from app.application.subscription.delete import (
|
|
||||||
DeleteSubscribeCommand,
|
|
||||||
SubscribeDeletionActor,
|
|
||||||
)
|
|
||||||
from app.application.subscription.identity import (
|
|
||||||
DeleteSubscriptionsByIdentityCommand,
|
|
||||||
)
|
|
||||||
from app.application.subscription.search import (
|
|
||||||
SearchSubscriptionsCommand,
|
|
||||||
SubscribeSearchActor,
|
|
||||||
)
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.application.subscription.query import SubscriptionQueryService
|
|
||||||
from app.application.subscription.mutation import (
|
|
||||||
SubscriptionActor,
|
|
||||||
SubscriptionMutationService,
|
|
||||||
)
|
|
||||||
from app.application.configuration import (
|
|
||||||
get_api_runtime_config_snapshot,
|
|
||||||
get_configured_system_config,
|
|
||||||
)
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_user,
|
get_current_active_user,
|
||||||
get_current_active_user_async,
|
get_current_active_user_async,
|
||||||
@@ -50,23 +17,56 @@ from app.api.dependencies.subscription import (
|
|||||||
get_delete_subscribe_command,
|
get_delete_subscribe_command,
|
||||||
get_delete_subscriptions_by_identity_command,
|
get_delete_subscriptions_by_identity_command,
|
||||||
get_search_subscriptions_command,
|
get_search_subscriptions_command,
|
||||||
get_subscription_query_service,
|
|
||||||
get_subscription_mutation_service,
|
get_subscription_mutation_service,
|
||||||
|
get_subscription_query_service,
|
||||||
get_subscription_sync_mutation_service,
|
get_subscription_sync_mutation_service,
|
||||||
)
|
)
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import (
|
||||||
|
get_api_runtime_config_snapshot,
|
||||||
|
get_configured_system_config,
|
||||||
|
)
|
||||||
from app.application.scheduling import get_scheduler
|
from app.application.scheduling import get_scheduler
|
||||||
|
from app.application.subscription.delete import (
|
||||||
|
DeleteSubscribeCommand,
|
||||||
|
SubscribeDeletionActor,
|
||||||
|
)
|
||||||
|
from app.application.subscription.identity import (
|
||||||
|
DeleteSubscriptionsByIdentityCommand,
|
||||||
|
)
|
||||||
|
from app.application.subscription.mutation import (
|
||||||
|
SubscriptionActor,
|
||||||
|
SubscriptionMutationService,
|
||||||
|
)
|
||||||
|
from app.application.subscription.query import SubscriptionQueryService
|
||||||
|
from app.application.subscription.search import (
|
||||||
|
SearchSubscriptionsCommand,
|
||||||
|
SubscribeSearchActor,
|
||||||
|
)
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
|
from app.domain.context import MediaInfo
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
from app.schemas.common import IdData as _SchemaIdData
|
||||||
from app.schemas.event import SubscribeModifiedEventData
|
from app.schemas.event import SubscribeModifiedEventData
|
||||||
|
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.subscribe import SubscrbieInfo as _SchemaSubscrbieInfo
|
||||||
|
from app.schemas.subscribe import SubscribeShare as _SchemaSubscribeShare
|
||||||
|
from app.schemas.subscribe import SubscribeShareStatistics as _SchemaSubscribeShareStatistics
|
||||||
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_ALBUM,
|
MUSIC_ENTITY_ALBUM,
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
|
EventType,
|
||||||
MediaSource,
|
MediaSource,
|
||||||
MediaType,
|
MediaType,
|
||||||
EventType,
|
|
||||||
SystemConfigKey,
|
SystemConfigKey,
|
||||||
)
|
)
|
||||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
from app.schemas.workflow import Subscribe as _SchemaSubscribe
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
+61
-61
@@ -6,25 +6,76 @@ import zipfile
|
|||||||
from collections import deque
|
from collections import deque
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional, Union, Annotated
|
from typing import Annotated, Any, Optional, Union
|
||||||
from urllib.parse import urljoin, urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
import anyio
|
import anyio
|
||||||
import pillow_avif # noqa: F401 # pylint: disable=unused-import # AVIF 注册副作用
|
import pillow_avif # noqa: F401 # pylint: disable=unused-import # AVIF 注册副作用
|
||||||
from anyio import Path as AsyncPath
|
from anyio import Path as AsyncPath
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
from fastapi import Body, Depends, Header, HTTPException, Request, Response
|
||||||
from fastapi import Body, Depends, HTTPException, Header, Request, Response
|
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
from app.adapters.external.market import (
|
||||||
|
PLUGIN_MARKET_WIKI_URL,
|
||||||
|
extract_plugin_market_repos_from_wiki,
|
||||||
|
merge_plugin_market_repos,
|
||||||
|
split_plugin_market_repo_urls,
|
||||||
|
)
|
||||||
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.adapters.network.http import AsyncRequestUtils, RequestUtils
|
||||||
|
from app.adapters.system import rust as rust_accel
|
||||||
|
from app.adapters.system.update import system_update_manager
|
||||||
|
from app.adapters.web.security.access import verify_apitoken, verify_resource_token, verify_token
|
||||||
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser,
|
||||||
|
get_current_active_superuser_async,
|
||||||
|
get_current_active_user_async,
|
||||||
|
)
|
||||||
|
from app.api.principal import ApiPrincipal
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.backup import DatabaseBackupInProgressError
|
||||||
|
from app.application.configuration import (
|
||||||
|
get_configured_system_config,
|
||||||
|
get_runtime_settings,
|
||||||
|
)
|
||||||
|
from app.application.database import get_database_governance
|
||||||
|
from app.application.image import ImageHelper
|
||||||
|
from app.application.messaging.message import MessageHelper
|
||||||
|
from app.application.module import get_module_manager
|
||||||
|
from app.application.network import NetworkTestService
|
||||||
|
from app.application.plugin.runtime import plugin_system_config_mutation
|
||||||
|
from app.application.rules import RuleHelper
|
||||||
|
from app.application.scheduling import get_scheduler
|
||||||
|
from app.application.security.url import SecurityUtils
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.mediaserver import MediaServerChain
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
|
from app.foundation.crypto import HashUtils
|
||||||
|
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
|
||||||
|
from app.foundation.url import UrlUtils
|
||||||
|
from app.runtime.events import eventmanager
|
||||||
|
from app.runtime.execution import run_in_threadpool_to_completion
|
||||||
|
from app.runtime.localization import LocaleHelper
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.progress import AsyncProgressHelper
|
||||||
|
from app.runtime.scheduling import TimerUtils
|
||||||
|
from app.runtime.state import SystemHelper
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.runtime.version import get_app_version, get_frontend_version
|
||||||
from app.schemas.common import JsonObject as _SchemaJsonObject
|
from app.schemas.common import JsonObject as _SchemaJsonObject
|
||||||
from app.schemas.common import JsonObjectList as _SchemaJsonObjectList
|
from app.schemas.common import JsonObjectList as _SchemaJsonObjectList
|
||||||
from app.schemas.common import TimeData as _SchemaTimeData
|
from app.schemas.common import TimeData as _SchemaTimeData
|
||||||
from app.schemas.common import ValueData as _SchemaValueData
|
from app.schemas.common import ValueData as _SchemaValueData
|
||||||
|
from app.schemas.event import ConfigChangeEventData
|
||||||
|
from app.schemas.exception import PluginMutationRejectedError
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.system import NetTestTarget as _SchemaNetTestTarget
|
|
||||||
from app.schemas.system import DatabaseBackupArtifactData as _SchemaDatabaseBackupArtifactData
|
from app.schemas.system import DatabaseBackupArtifactData as _SchemaDatabaseBackupArtifactData
|
||||||
from app.schemas.system import DatabaseBackupVerificationData as _SchemaDatabaseBackupVerificationData
|
from app.schemas.system import DatabaseBackupVerificationData as _SchemaDatabaseBackupVerificationData
|
||||||
|
from app.schemas.system import NetTestTarget as _SchemaNetTestTarget
|
||||||
from app.schemas.system import PluginMarketSyncData as _SchemaPluginMarketSyncData
|
from app.schemas.system import PluginMarketSyncData as _SchemaPluginMarketSyncData
|
||||||
from app.schemas.system import PluginMarketSyncRequest as _SchemaPluginMarketSyncRequest
|
from app.schemas.system import PluginMarketSyncRequest as _SchemaPluginMarketSyncRequest
|
||||||
from app.schemas.system import RuleTestData as _SchemaRuleTestData
|
from app.schemas.system import RuleTestData as _SchemaRuleTestData
|
||||||
@@ -33,58 +84,7 @@ from app.schemas.system import SystemModuleListData as _SchemaSystemModuleListDa
|
|||||||
from app.schemas.system import SystemUpdateStatus as _SchemaSystemUpdateStatus
|
from app.schemas.system import SystemUpdateStatus as _SchemaSystemUpdateStatus
|
||||||
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
from app.schemas.system import TorrentInfo as _SchemaTorrentInfo
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.api.response import ResponseAPIRouter
|
from app.schemas.types import EventType, SystemConfigKey
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.mediaserver import MediaServerChain
|
|
||||||
from app.chain.search import SearchChain
|
|
||||||
from app.chain.system import SystemChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import eventmanager
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.application.module import get_module_manager
|
|
||||||
from app.adapters.web.security.access import verify_apitoken, verify_resource_token, verify_token
|
|
||||||
from app.api.principal import ApiPrincipal
|
|
||||||
from app.application.configuration import (
|
|
||||||
get_configured_system_config,
|
|
||||||
get_runtime_settings,
|
|
||||||
)
|
|
||||||
from app.application.backup import DatabaseBackupInProgressError
|
|
||||||
from app.application.database import get_database_governance
|
|
||||||
from app.application.plugin.runtime import plugin_system_config_mutation
|
|
||||||
from app.api.dependencies.auth import (
|
|
||||||
get_current_active_superuser,
|
|
||||||
get_current_active_superuser_async,
|
|
||||||
get_current_active_user_async,
|
|
||||||
)
|
|
||||||
from app.application.image import ImageHelper
|
|
||||||
from app.runtime.localization import LocaleHelper
|
|
||||||
from app.adapters.external.market import (
|
|
||||||
PLUGIN_MARKET_WIKI_URL,
|
|
||||||
extract_plugin_market_repos_from_wiki,
|
|
||||||
merge_plugin_market_repos,
|
|
||||||
split_plugin_market_repo_urls,
|
|
||||||
)
|
|
||||||
from app.application.messaging.message import MessageHelper
|
|
||||||
from app.runtime.progress import AsyncProgressHelper
|
|
||||||
from app.runtime.scheduling import TimerUtils
|
|
||||||
from app.application.rules import RuleHelper
|
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
|
||||||
from app.runtime.state import SystemHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.runtime.execution import run_in_threadpool_to_completion
|
|
||||||
from app.application.scheduling import get_scheduler
|
|
||||||
from app.schemas.event import ConfigChangeEventData
|
|
||||||
from app.schemas.exception import PluginMutationRejectedError
|
|
||||||
from app.schemas.types import SystemConfigKey, EventType
|
|
||||||
from app.foundation.crypto import HashUtils
|
|
||||||
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
|
|
||||||
from app.adapters.network.http import RequestUtils, AsyncRequestUtils
|
|
||||||
from app.adapters.system import rust as rust_accel
|
|
||||||
from app.adapters.system.update import system_update_manager
|
|
||||||
from app.application.security.url import SecurityUtils
|
|
||||||
from app.application.network import NetworkTestService
|
|
||||||
from app.foundation.url import UrlUtils
|
|
||||||
from app.runtime.version import get_app_version, get_frontend_version
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
@@ -1042,7 +1042,7 @@ async def get_progress(
|
|||||||
|
|
||||||
async def event_generator():
|
async def event_generator():
|
||||||
try:
|
try:
|
||||||
while not global_vars.is_system_stopped:
|
while not runtime_stop_state.is_system_stopped:
|
||||||
if await request.is_disconnected():
|
if await request.is_disconnected():
|
||||||
break
|
break
|
||||||
detail = await progress.get(locale=locale)
|
detail = await progress.get(locale=locale)
|
||||||
@@ -1233,7 +1233,7 @@ async def get_message(
|
|||||||
|
|
||||||
async def event_generator():
|
async def event_generator():
|
||||||
try:
|
try:
|
||||||
while not global_vars.is_system_stopped:
|
while not runtime_stop_state.is_system_stopped:
|
||||||
if await request.is_disconnected():
|
if await request.is_disconnected():
|
||||||
break
|
break
|
||||||
detail = message.get(role)
|
detail = message.get(role)
|
||||||
@@ -1314,7 +1314,7 @@ async def _get_logging_impl(
|
|||||||
initial_stat = await log_path.stat()
|
initial_stat = await log_path.stat()
|
||||||
initial_size = initial_stat.st_size
|
initial_size = initial_stat.st_size
|
||||||
# 实时监听新日志,使用更短的轮询间隔
|
# 实时监听新日志,使用更短的轮询间隔
|
||||||
while not global_vars.is_system_stopped:
|
while not runtime_stop_state.is_system_stopped:
|
||||||
if await request.is_disconnected():
|
if await request.is_disconnected():
|
||||||
break
|
break
|
||||||
# 检查文件是否有新内容
|
# 检查文件是否有新内容
|
||||||
@@ -1413,7 +1413,7 @@ async def latest_version(_: _SchemaTokenPayload = Depends(verify_token)):
|
|||||||
version_res = await AsyncRequestUtils(
|
version_res = await AsyncRequestUtils(
|
||||||
proxies=get_runtime_settings().get("PROXY"),
|
proxies=get_runtime_settings().get("PROXY"),
|
||||||
headers=get_runtime_settings().get("GITHUB_HEADERS"),
|
headers=get_runtime_settings().get("GITHUB_HEADERS"),
|
||||||
).get_res(f"https://api.github.com/repos/jxxghp/MoviePilot/releases")
|
).get_res("https://api.github.com/repos/jxxghp/MoviePilot/releases")
|
||||||
if version_res is not None and version_res.status_code == 200:
|
if version_res is not None and version_res.status_code == 200:
|
||||||
ver_json = version_res.json()
|
ver_json = version_res.json()
|
||||||
if ver_json:
|
if ver_json:
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
from typing import List, Any, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_token
|
||||||
|
from app.api.dependencies.auth import get_current_active_superuser_async
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot, get_configured_system_config
|
||||||
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.tmdb import TmdbEpisode as _SchemaTmdbEpisode
|
||||||
from app.schemas.tmdb import TmdbRecognitionCacheData as _SchemaTmdbRecognitionCacheData
|
from app.schemas.tmdb import TmdbRecognitionCacheData as _SchemaTmdbRecognitionCacheData
|
||||||
from app.schemas.tmdb import TmdbSeason as _SchemaTmdbSeason
|
from app.schemas.tmdb import TmdbSeason as _SchemaTmdbSeason
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.tmdb import TmdbEpisode as _SchemaTmdbEpisode
|
|
||||||
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.tmdb import TmdbChain
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.adapters.web.security.access import verify_token
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.api.dependencies.auth import get_current_active_superuser_async
|
|
||||||
from app.schemas.types import MediaType, SystemConfigKey
|
from app.schemas.types import MediaType, SystemConfigKey
|
||||||
|
from app.schemas.workflow import MediaInfo as _SchemaMediaInfo
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -2,24 +2,24 @@ from typing import Optional
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.cache import TorrentCacheData as _SchemaTorrentCacheData
|
|
||||||
from app.schemas.cache import TorrentReidentifyData as _SchemaTorrentReidentifyData
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.torrents import TorrentsChain
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_superuser,
|
get_current_active_superuser,
|
||||||
get_current_active_superuser_async,
|
get_current_active_superuser_async,
|
||||||
)
|
)
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.application.torrent_cache import TorrentCacheRecognitionService
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.torrents import TorrentsChain
|
||||||
|
from app.foundation.crypto import HashUtils
|
||||||
|
from app.schemas.cache import TorrentCacheData as _SchemaTorrentCacheData
|
||||||
|
from app.schemas.cache import TorrentReidentifyData as _SchemaTorrentReidentifyData
|
||||||
|
from app.schemas.media import resolve_media_identity
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MediaSource,
|
MediaSource,
|
||||||
MusicTargetEntityType,
|
MusicTargetEntityType,
|
||||||
)
|
)
|
||||||
from app.foundation.crypto import HashUtils
|
|
||||||
from app.schemas.media import resolve_media_identity
|
|
||||||
from app.application.torrent_cache import TorrentCacheRecognitionService
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,34 +1,33 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, List, Annotated, Optional
|
from typing import Annotated, Any, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_apitoken, verify_token
|
||||||
|
from app.api.dependencies.auth import get_current_active_manage_user
|
||||||
|
from app.api.dependencies.history import get_transfer_history_lookup_service
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.application.directory import DirectoryHelper
|
||||||
|
from app.application.history import TransferHistoryLookupService
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.transfer import TransferChain
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.common import NameData as _SchemaNameData
|
from app.schemas.common import NameData as _SchemaNameData
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.system import TransferDirectoryConf as _SchemaTransferDirectoryConf
|
||||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||||
from app.schemas.transfer import EpisodeFormat as _SchemaEpisodeFormat
|
from app.schemas.transfer import EpisodeFormat as _SchemaEpisodeFormat
|
||||||
from app.schemas.transfer import EpisodeFormatRecommendData as _SchemaEpisodeFormatRecommendData
|
from app.schemas.transfer import EpisodeFormatRecommendData as _SchemaEpisodeFormatRecommendData
|
||||||
|
from app.schemas.transfer import EpisodeFormatRecommendItem, ManualTransferItem
|
||||||
from app.schemas.transfer import ManualTransferHistoryInfo as _SchemaManualTransferHistoryInfo
|
from app.schemas.transfer import ManualTransferHistoryInfo as _SchemaManualTransferHistoryInfo
|
||||||
from app.schemas.transfer import ManualTransferResultData as _SchemaManualTransferResultData
|
from app.schemas.transfer import ManualTransferResultData as _SchemaManualTransferResultData
|
||||||
from app.schemas.transfer import ManualTransferTargetPath as _SchemaManualTransferTargetPath
|
from app.schemas.transfer import ManualTransferTargetPath as _SchemaManualTransferTargetPath
|
||||||
from app.schemas.system import TransferDirectoryConf as _SchemaTransferDirectoryConf
|
|
||||||
from app.schemas.transfer import TransferJob as _SchemaTransferJob
|
from app.schemas.transfer import TransferJob as _SchemaTransferJob
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.transfer import TransferChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.adapters.web.security.access import verify_token, verify_apitoken
|
|
||||||
from app.api.dependencies.auth import get_current_active_manage_user
|
|
||||||
from app.api.dependencies.history import get_transfer_history_lookup_service
|
|
||||||
from app.application.directory import DirectoryHelper
|
|
||||||
from app.application.history import TransferHistoryLookupService
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
from app.schemas.workflow import FileItem
|
from app.schemas.workflow import FileItem
|
||||||
from app.schemas.transfer import ManualTransferItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.schemas.transfer import EpisodeFormatRecommendItem
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ async def remove_queue(
|
|||||||
"""
|
"""
|
||||||
TransferChain().remove_from_queue(fileitem)
|
TransferChain().remove_from_queue(fileitem)
|
||||||
# 取消整理
|
# 取消整理
|
||||||
global_vars.stop_transfer(fileitem.path)
|
runtime_stop_state.stop_transfer(fileitem.path)
|
||||||
return _SchemaResponse(success=True)
|
return _SchemaResponse(success=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -398,7 +397,7 @@ def _execute_manual_transfer(
|
|||||||
elif transer_item.fileitem:
|
elif transer_item.fileitem:
|
||||||
src_fileitems = [transer_item.fileitem]
|
src_fileitems = [transer_item.fileitem]
|
||||||
else:
|
else:
|
||||||
return _SchemaResponse(success=False, message=f"缺少参数")
|
return _SchemaResponse(success=False, message="缺少参数")
|
||||||
|
|
||||||
dedup_fileitems: List[FileItem] = []
|
dedup_fileitems: List[FileItem] = []
|
||||||
seen_paths = set()
|
seen_paths = set()
|
||||||
|
|||||||
+10
-10
@@ -2,23 +2,23 @@ import base64
|
|||||||
import re
|
import re
|
||||||
from typing import Annotated, Any, List, Union
|
from typing import Annotated, Any, List, Union
|
||||||
|
|
||||||
from fastapi import Body, Depends, HTTPException, UploadFile, File
|
from fastapi import Body, Depends, File, HTTPException, UploadFile
|
||||||
|
|
||||||
|
from app.api.dependencies.auth import (
|
||||||
|
get_current_active_superuser_async,
|
||||||
|
get_current_active_user_async,
|
||||||
|
get_user_service,
|
||||||
|
)
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.application.security.token import PasswordTooLongError, get_password_hash
|
||||||
|
from app.application.security.user import UserService
|
||||||
|
from app.application.security.userconfig import get_configured_user_configuration
|
||||||
from app.schemas.common import FileNameData as _SchemaFileNameData
|
from app.schemas.common import FileNameData as _SchemaFileNameData
|
||||||
from app.schemas.common import ValueData as _SchemaValueData
|
from app.schemas.common import ValueData as _SchemaValueData
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
from app.schemas.user import User as _SchemaUser
|
from app.schemas.user import User as _SchemaUser
|
||||||
from app.schemas.user import UserCreate as _SchemaUserCreate
|
from app.schemas.user import UserCreate as _SchemaUserCreate
|
||||||
from app.schemas.user import UserUpdate as _SchemaUserUpdate
|
from app.schemas.user import UserUpdate as _SchemaUserUpdate
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.application.security.token import PasswordTooLongError, get_password_hash
|
|
||||||
from app.application.security.user import UserService
|
|
||||||
from app.api.dependencies.auth import (
|
|
||||||
get_current_active_superuser_async,
|
|
||||||
get_current_active_user_async,
|
|
||||||
get_user_service,
|
|
||||||
)
|
|
||||||
from app.application.security.userconfig import get_configured_user_configuration
|
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
from typing import Any, Annotated
|
from typing import Annotated, Any
|
||||||
|
|
||||||
from fastapi import Depends, Request
|
from fastapi import Depends, Request
|
||||||
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.chain.webhook import WebhookChain
|
|
||||||
from app.adapters.web.security.access import verify_apitoken
|
from app.adapters.web.security.access import verify_apitoken
|
||||||
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
from app.api.context import get_background_task_registry, resolve_background_task_registry
|
||||||
|
from app.api.response import ResponseAPIRouter
|
||||||
|
from app.chain.webhook import WebhookChain
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,8 @@
|
|||||||
from typing import List, Any, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
|
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.schemas.workflow import NameValueOption as _SchemaNameValueOption
|
|
||||||
from app.schemas.workflow import PluginWorkflowActionGroup as _SchemaPluginWorkflowActionGroup
|
|
||||||
from app.schemas.workflow import Workflow as _SchemaWorkflow
|
|
||||||
from app.schemas.workflow import WorkflowActionDefinition as _SchemaWorkflowActionDefinition
|
|
||||||
from app.schemas.workflow import WorkflowShare as _SchemaWorkflowShare
|
|
||||||
from app.api.response import ResponseAPIRouter
|
|
||||||
from app.application.workflow import (
|
|
||||||
WorkflowDefinitionCommand,
|
|
||||||
WorkflowMutationCommand,
|
|
||||||
WorkflowQueryService,
|
|
||||||
get_workflow_manager,
|
|
||||||
)
|
|
||||||
from app.chain.workflow import WorkflowChain
|
|
||||||
from app.application.plugin.runtime import get_plugin_manager
|
|
||||||
from app.api.dependencies.auth import (
|
from app.api.dependencies.auth import (
|
||||||
get_current_active_manage_user,
|
get_current_active_manage_user,
|
||||||
get_current_active_manage_user_async,
|
get_current_active_manage_user_async,
|
||||||
@@ -26,8 +12,22 @@ from app.api.dependencies.workflow import (
|
|||||||
get_workflow_mutation_command,
|
get_workflow_mutation_command,
|
||||||
get_workflow_query_service,
|
get_workflow_query_service,
|
||||||
)
|
)
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.api.response import ResponseAPIRouter
|
||||||
from app.schemas.types import EventType, EVENT_TYPE_NAMES
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
|
from app.application.workflow import (
|
||||||
|
WorkflowDefinitionCommand,
|
||||||
|
WorkflowMutationCommand,
|
||||||
|
WorkflowQueryService,
|
||||||
|
get_workflow_manager,
|
||||||
|
)
|
||||||
|
from app.chain.workflow import WorkflowChain
|
||||||
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.types import EVENT_TYPE_NAMES, EventType
|
||||||
|
from app.schemas.workflow import NameValueOption as _SchemaNameValueOption
|
||||||
|
from app.schemas.workflow import PluginWorkflowActionGroup as _SchemaPluginWorkflowActionGroup
|
||||||
|
from app.schemas.workflow import Workflow as _SchemaWorkflow
|
||||||
|
from app.schemas.workflow import WorkflowActionDefinition as _SchemaWorkflowActionDefinition
|
||||||
|
from app.schemas.workflow import WorkflowShare as _SchemaWorkflowShare
|
||||||
|
|
||||||
router = ResponseAPIRouter()
|
router = ResponseAPIRouter()
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from typing import Any
|
|||||||
|
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
|
||||||
|
|
||||||
SSE_HEADERS = {
|
SSE_HEADERS = {
|
||||||
"Cache-Control": "no-cache, no-transform",
|
"Cache-Control": "no-cache, no-transform",
|
||||||
"Connection": "keep-alive",
|
"Connection": "keep-alive",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from starlette.responses import Response as StarletteResponse
|
|||||||
from app.schemas.common import JsonData
|
from app.schemas.common import JsonData
|
||||||
from app.schemas.response import Response, ValidationIssue
|
from app.schemas.response import Response, ValidationIssue
|
||||||
|
|
||||||
|
|
||||||
ERROR_RESPONSES: dict[int, dict[str, Any]] = {
|
ERROR_RESPONSES: dict[int, dict[str, Any]] = {
|
||||||
400: {"model": Response[None], "description": "请求错误"},
|
400: {"model": Response[None], "description": "请求错误"},
|
||||||
401: {"model": Response[None], "description": "未认证"},
|
401: {"model": Response[None], "description": "未认证"},
|
||||||
|
|||||||
+13
-14
@@ -1,8 +1,19 @@
|
|||||||
from typing import List, Optional, Annotated
|
from typing import Annotated, List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Depends
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
|
||||||
|
from app.adapters.web.security.access import verify_apikey
|
||||||
|
from app.api.dependencies.subscription import get_servarr_subscription_service
|
||||||
|
from app.api.response import ERROR_RESPONSES
|
||||||
|
from app.application.servarr import ServarrSubscription, ServarrSubscriptionService
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
|
from app.chain.tvdb import TvdbChain
|
||||||
|
from app.domain.context import MediaInfo
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
|
from app.runtime.version import get_app_version
|
||||||
from app.schemas.response import Response as _SchemaResponse
|
from app.schemas.response import Response as _SchemaResponse
|
||||||
|
from app.schemas.servarr import RadarrMovie, SonarrSeries
|
||||||
from app.schemas.servarr import RadarrMovie as _SchemaRadarrMovie
|
from app.schemas.servarr import RadarrMovie as _SchemaRadarrMovie
|
||||||
from app.schemas.servarr import ServarrIdResponse as _SchemaServarrIdResponse
|
from app.schemas.servarr import ServarrIdResponse as _SchemaServarrIdResponse
|
||||||
from app.schemas.servarr import ServarrLanguageProfile as _SchemaServarrLanguageProfile
|
from app.schemas.servarr import ServarrLanguageProfile as _SchemaServarrLanguageProfile
|
||||||
@@ -11,19 +22,7 @@ from app.schemas.servarr import ServarrRootFolder as _SchemaServarrRootFolder
|
|||||||
from app.schemas.servarr import ServarrSystemStatus as _SchemaServarrSystemStatus
|
from app.schemas.servarr import ServarrSystemStatus as _SchemaServarrSystemStatus
|
||||||
from app.schemas.servarr import ServarrTag as _SchemaServarrTag
|
from app.schemas.servarr import ServarrTag as _SchemaServarrTag
|
||||||
from app.schemas.servarr import SonarrSeries as _SchemaSonarrSeries
|
from app.schemas.servarr import SonarrSeries as _SchemaSonarrSeries
|
||||||
from app.api.response import ERROR_RESPONSES
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.chain.tvdb import TvdbChain
|
|
||||||
from app.domain.context import MediaInfo
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.application.servarr import ServarrSubscription, ServarrSubscriptionService
|
|
||||||
from app.adapters.web.security.access import verify_apikey
|
|
||||||
from app.api.dependencies.subscription import get_servarr_subscription_service
|
|
||||||
from app.schemas.servarr import RadarrMovie
|
|
||||||
from app.schemas.servarr import SonarrSeries
|
|
||||||
from app.schemas.types import MediaSource, MediaType
|
from app.schemas.types import MediaSource, MediaType
|
||||||
from app.runtime.version import get_app_version
|
|
||||||
|
|
||||||
arr_router = APIRouter(tags=["servarr"], responses=ERROR_RESPONSES)
|
arr_router = APIRouter(tags=["servarr"], responses=ERROR_RESPONSES)
|
||||||
|
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ from fastapi import APIRouter, Body, Depends, Header, HTTPException, Path, Reque
|
|||||||
from fastapi.responses import PlainTextResponse
|
from fastapi.responses import PlainTextResponse
|
||||||
from fastapi.routing import APIRoute
|
from fastapi.routing import APIRoute
|
||||||
|
|
||||||
|
from app.api.response import ERROR_RESPONSES
|
||||||
|
from app.application.configuration import get_api_runtime_config_snapshot
|
||||||
|
from app.foundation.crypto import CryptoJsUtils, HashUtils
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.servcookie import CookieActionResponse as _SchemaCookieActionResponse
|
from app.schemas.servcookie import CookieActionResponse as _SchemaCookieActionResponse
|
||||||
from app.schemas.servcookie import CookieData as _SchemaCookieData
|
from app.schemas.servcookie import CookieData as _SchemaCookieData
|
||||||
from app.schemas.servcookie import CookieDecryptedPayload as _SchemaCookieDecryptedPayload
|
from app.schemas.servcookie import CookieDecryptedPayload as _SchemaCookieDecryptedPayload
|
||||||
from app.schemas.servcookie import CookieEncryptedPayload as _SchemaCookieEncryptedPayload
|
from app.schemas.servcookie import CookieEncryptedPayload as _SchemaCookieEncryptedPayload
|
||||||
from app.schemas.servcookie import CookiePassword as _SchemaCookiePassword
|
from app.schemas.servcookie import CookiePassword as _SchemaCookiePassword
|
||||||
from app.api.response import ERROR_RESPONSES
|
|
||||||
from app.application.configuration import get_api_runtime_config_snapshot
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.foundation.crypto import CryptoJsUtils, HashUtils
|
|
||||||
|
|
||||||
|
|
||||||
class GzipRequest(Request):
|
class GzipRequest(Request):
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import Any, Optional
|
|||||||
from app.application.chain.data import ChainDataPorts
|
from app.application.chain.data import ChainDataPorts
|
||||||
from app.application.chain.durable_events import ChainDurableEventWriter
|
from app.application.chain.durable_events import ChainDurableEventWriter
|
||||||
from app.application.configuration import ChainRuntimeConfig
|
from app.application.configuration import ChainRuntimeConfig
|
||||||
|
from app.runtime.stop import StopState, runtime_stop_state
|
||||||
|
|
||||||
|
|
||||||
MessageQueueFactory = Callable[[Callable[..., Any]], Any]
|
MessageQueueFactory = Callable[[Callable[..., Any]], Any]
|
||||||
@@ -34,6 +35,7 @@ class ChainRuntimeContext:
|
|||||||
configuration: ChainRuntimeConfig = field(
|
configuration: ChainRuntimeConfig = field(
|
||||||
default_factory=lambda: ChainRuntimeConfig(media_extensions=())
|
default_factory=lambda: ChainRuntimeConfig(media_extensions=())
|
||||||
)
|
)
|
||||||
|
stop_state: StopState = field(default_factory=lambda: runtime_stop_state)
|
||||||
|
|
||||||
|
|
||||||
def _unconfigured_chain_runtime_context() -> ChainRuntimeContext:
|
def _unconfigured_chain_runtime_context() -> ChainRuntimeContext:
|
||||||
|
|||||||
@@ -10,26 +10,24 @@ import time
|
|||||||
from contextvars import Context, copy_context
|
from contextvars import Context, copy_context
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, Literal, Optional, List, Dict, Protocol, Union
|
from typing import Any, Callable, Dict, List, Literal, Optional, Protocol, Union
|
||||||
from typing import Callable
|
|
||||||
|
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
|
|
||||||
from app.runtime.cache import TTLCache
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import MediaInfo, MusicInfo, TorrentInfo
|
from app.domain.context import MediaInfo, MusicInfo, TorrentInfo
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.foundation import size as size_tools
|
||||||
|
from app.foundation.crypto import HashUtils
|
||||||
|
from app.foundation.singleton import Singleton, SingletonClass
|
||||||
|
from app.runtime.cache import TTLCache
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.message import Message
|
from app.schemas.message import Message
|
||||||
from app.schemas.tmdb import TmdbEpisode
|
from app.schemas.tmdb import TmdbEpisode
|
||||||
from app.schemas.transfer import TransferInfo
|
from app.schemas.transfer import TransferInfo
|
||||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, SystemConfigKey
|
from app.schemas.types import MUSIC_ENTITY_ALBUM, SystemConfigKey
|
||||||
from app.foundation.singleton import Singleton, SingletonClass
|
|
||||||
from app.foundation import size as size_tools
|
|
||||||
from app.foundation.crypto import HashUtils
|
|
||||||
|
|
||||||
|
|
||||||
# 专辑名尾部的括号年份标记;重命名模板会独立追加 `({{year}})`,
|
# 专辑名尾部的括号年份标记;重命名模板会独立追加 `({{year}})`,
|
||||||
# 标签或目录名中自带的尾部年份若不剥离,会生成重复年份的目录名(issue #6355)
|
# 标签或目录名中自带的尾部年份若不剥离,会生成重复年份的目录名(issue #6355)
|
||||||
@@ -998,7 +996,7 @@ class MessageQueueManager(metaclass=SingletonClass):
|
|||||||
current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
if self._is_in_scheduled_time(current_time):
|
if self._is_in_scheduled_time(current_time):
|
||||||
while self._running and not self.queue.empty():
|
while self._running and not self.queue.empty():
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if not self._is_in_scheduled_time(datetime.now()):
|
if not self._is_in_scheduled_time(datetime.now()):
|
||||||
break
|
break
|
||||||
|
|||||||
+14
-15
@@ -5,7 +5,7 @@ import traceback
|
|||||||
from abc import ABCMeta
|
from abc import ABCMeta
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Any, Tuple, List, Set, Union, Dict
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
from app.application.chain.context import ChainRuntimeContext, get_chain_runtime_context
|
from app.application.chain.context import ChainRuntimeContext, get_chain_runtime_context
|
||||||
from app.application.chain.data import get_chain_data_ports
|
from app.application.chain.data import get_chain_data_ports
|
||||||
@@ -18,24 +18,22 @@ from app.chain._recognition import RecognitionMixin
|
|||||||
from app.domain.context import Context, MediaInfo, SubtitleInfo, TorrentInfo
|
from app.domain.context import Context, MediaInfo, SubtitleInfo, TorrentInfo
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.exception import RateLimitExceededException
|
|
||||||
from app.schemas.transfer import TransferInfo
|
|
||||||
from app.schemas.mediaserver import ExistMediaInfo
|
|
||||||
from app.schemas.transfer import DownloaderFile, DownloaderTorrent
|
|
||||||
from app.schemas.message import IncomingMessage
|
|
||||||
from app.schemas.mediaserver import WebhookEventInfo
|
|
||||||
from app.schemas.tmdb import TmdbEpisode
|
|
||||||
from app.schemas.context import MediaPerson
|
|
||||||
from app.schemas.workflow import FileItem
|
|
||||||
from app.schemas.system import TransferDirectoryConf
|
|
||||||
from app.schemas.category import CategoryConfig
|
from app.schemas.category import CategoryConfig
|
||||||
|
from app.schemas.context import MediaPerson
|
||||||
|
from app.schemas.exception import RateLimitExceededException
|
||||||
|
from app.schemas.mediaserver import ExistMediaInfo, WebhookEventInfo
|
||||||
|
from app.schemas.message import IncomingMessage
|
||||||
|
from app.schemas.system import TransferDirectoryConf
|
||||||
|
from app.schemas.tmdb import TmdbEpisode
|
||||||
|
from app.schemas.transfer import DownloaderFile, DownloaderTorrent, TransferInfo
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
TorrentStatus,
|
|
||||||
MediaType,
|
|
||||||
MediaSourceSelection,
|
|
||||||
MediaImageType,
|
|
||||||
EventType,
|
EventType,
|
||||||
|
MediaImageType,
|
||||||
|
MediaSourceSelection,
|
||||||
|
MediaType,
|
||||||
|
TorrentStatus,
|
||||||
)
|
)
|
||||||
|
from app.schemas.workflow import FileItem
|
||||||
|
|
||||||
|
|
||||||
class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin,
|
class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin,
|
||||||
@@ -57,6 +55,7 @@ class ChainBase(RecognitionMixin, MessageProcessingMixin, NotificationMixin,
|
|||||||
self.filecache = context.file_cache
|
self.filecache = context.file_cache
|
||||||
self.async_filecache = context.async_file_cache
|
self.async_filecache = context.async_file_cache
|
||||||
self.runtime_config = context.configuration
|
self.runtime_config = context.configuration
|
||||||
|
self.stop_state = context.stop_state
|
||||||
self.data_ports = context.data_ports or get_chain_data_ports()
|
self.data_ports = context.data_ports or get_chain_data_ports()
|
||||||
self.durable_event_writer = context.durable_event_writer
|
self.durable_event_writer = context.durable_event_writer
|
||||||
self._module_dispatcher = context.module_dispatcher_factory(
|
self._module_dispatcher = context.module_dispatcher_factory(
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
"""Chain mixin 对宿主能力的静态契约。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class ChainRuntimeMixinHost(Protocol):
|
||||||
|
"""识别与消息 mixin 共用的 Chain 运行能力。"""
|
||||||
|
|
||||||
|
runtime_config: Any
|
||||||
|
eventmanager: Any
|
||||||
|
messageoper: Any
|
||||||
|
messagequeue: Any
|
||||||
|
|
||||||
|
def run_module(self, method: str, **kwargs: Any) -> Any:
|
||||||
|
"""调用同步模块能力。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
async def async_run_module(self, method: str, **kwargs: Any) -> Any:
|
||||||
|
"""调用异步模块能力。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class MusicSubscribeMixinHost(Protocol):
|
||||||
|
"""音乐订阅 mixin 对 SubscribeChain 的最小要求。"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _music_media_chain(cls) -> Any:
|
||||||
|
"""构造媒体识别链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def _music_download_chain(self) -> Any:
|
||||||
|
"""构造下载链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def _music_search_chain(self) -> Any:
|
||||||
|
"""构造搜索链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def _music_site_keywords(self, mediainfo: Any) -> list[str]:
|
||||||
|
"""构造音乐站点搜索关键字。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def _matches_music_resource(self, mediainfo: Any, *texts: Any) -> bool:
|
||||||
|
"""判断站点资源文本是否匹配音乐目标。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_sub_sites(self, subscribe: Any) -> list[int]: ...
|
||||||
|
|
||||||
|
def get_params(self, subscribe: Any) -> Any: ...
|
||||||
|
|
||||||
|
def filter_torrents(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
def check_and_handle_existing_media(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
def finish_subscribe_or_not(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
def get_subscribe_source_keyword(self, subscribe: Any) -> str: ...
|
||||||
|
|
||||||
|
|
||||||
|
class InteractionMixinHost(Protocol):
|
||||||
|
"""交互委托 mixin 对业务 Chain 的最小要求。"""
|
||||||
|
|
||||||
|
_interaction_handler_type: type
|
||||||
|
|
||||||
|
def _interaction_handler(self) -> Any:
|
||||||
|
"""构造业务交互处理器。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class TransferMixinHost(ChainRuntimeMixinHost, Protocol):
|
||||||
|
"""整理辅助 mixin 对 TransferChain 的最小要求。"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_media_chain(cls) -> Any:
|
||||||
|
"""构造媒体识别链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_storage_chain(cls) -> Any:
|
||||||
|
"""构造存储链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_subscribe_chain(cls) -> Any:
|
||||||
|
"""构造订阅链。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def post_message(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
async def async_post_message(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
def obtain_images(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
|
|
||||||
|
def do_transfer(self, *args: Any, **kwargs: Any) -> Any: ...
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
from typing import Optional, Tuple, Union
|
from typing import Optional, Tuple, Union
|
||||||
|
|
||||||
|
from app.chain._contracts import InteractionMixinHost
|
||||||
from app.schemas.types import NotificationChannel
|
from app.schemas.types import NotificationChannel
|
||||||
|
|
||||||
|
|
||||||
class InteractionChainMixin:
|
class InteractionChainMixin:
|
||||||
|
__mixin_host_protocol__ = InteractionMixinHost
|
||||||
"""
|
"""
|
||||||
斜杠命令交互四件套委托:remote_list / parse_callback /
|
斜杠命令交互四件套委托:remote_list / parse_callback /
|
||||||
handle_callback_interaction / handle_text_interaction。
|
handle_callback_interaction / handle_text_interaction。
|
||||||
|
|||||||
@@ -9,20 +9,21 @@ from datetime import datetime
|
|||||||
from typing import Any, Dict, List, Optional, Union
|
from typing import Any, Dict, List, Optional, Union
|
||||||
|
|
||||||
from app.application.chain.data import get_chain_user_port
|
from app.application.chain.data import get_chain_user_port
|
||||||
|
from app.application.messaging.message import MessageTemplateHelper
|
||||||
|
from app.application.notification import get_notification_switch
|
||||||
|
from app.chain._contracts import ChainRuntimeMixinHost
|
||||||
from app.domain.context import Context, MediaInfo, MusicInfo, TorrentInfo
|
from app.domain.context import Context, MediaInfo, MusicInfo, TorrentInfo
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.foundation.identity import normalize_internal_user_id
|
from app.foundation.identity import normalize_internal_user_id
|
||||||
from app.application.messaging.message import MessageTemplateHelper
|
|
||||||
from app.application.notification import get_notification_switch
|
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.message import MessageResponse
|
from app.schemas.message import Message, MessageResponse
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.transfer import TransferInfo
|
|
||||||
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
|
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
|
||||||
|
from app.schemas.transfer import TransferInfo
|
||||||
from app.schemas.types import EventType, NotificationChannel
|
from app.schemas.types import EventType, NotificationChannel
|
||||||
|
|
||||||
|
|
||||||
class MessageProcessingMixin:
|
class MessageProcessingMixin:
|
||||||
|
__mixin_host_protocol__ = ChainRuntimeMixinHost
|
||||||
"""消息输入/处理状态机与通知派发规范化。"""
|
"""消息输入/处理状态机与通知派发规范化。"""
|
||||||
|
|
||||||
def start_message_processing_status(
|
def start_message_processing_status(
|
||||||
@@ -117,6 +118,7 @@ class MessageProcessingMixin:
|
|||||||
|
|
||||||
|
|
||||||
class NotificationMixin:
|
class NotificationMixin:
|
||||||
|
__mixin_host_protocol__ = ChainRuntimeMixinHost
|
||||||
"""通知消息发送域:渲染、隔离路由、队列发送与消息编辑。"""
|
"""通知消息发送域:渲染、隔离路由、队列发送与消息编辑。"""
|
||||||
|
|
||||||
def post_message(
|
def post_message(
|
||||||
|
|||||||
+10
-3
@@ -1,16 +1,22 @@
|
|||||||
import copy
|
import copy
|
||||||
from typing import Any, List, Optional, Tuple
|
from typing import Any, List, Optional, Tuple
|
||||||
|
|
||||||
from app.application.torrent import TorrentHelper
|
from app.application.chain.data import get_chain_subscribe_port
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.subscription.contract import (
|
from app.application.subscription.contract import (
|
||||||
build_subscribe_meta,
|
build_subscribe_meta,
|
||||||
subscribe_media_key,
|
subscribe_media_key,
|
||||||
)
|
)
|
||||||
|
from app.application.torrent import TorrentHelper
|
||||||
|
from app.chain._contracts import MusicSubscribeMixinHost
|
||||||
from app.chain.download import DownloadChain
|
from app.chain.download import DownloadChain
|
||||||
from app.chain.media import MediaChain
|
from app.chain.media import MediaChain
|
||||||
from app.chain.search import SearchChain
|
from app.chain.search import SearchChain
|
||||||
from app.application.chain.data import get_chain_subscribe_port
|
|
||||||
from app.application.configuration import get_configured_system_config
|
# 旧测试与插件补丁入口;正式依赖通过宿主工厂逐步收敛。
|
||||||
|
MediaChain = MediaChain
|
||||||
|
DownloadChain = DownloadChain
|
||||||
|
SearchChain = SearchChain
|
||||||
from app.domain.context import Context, MediaInfo, MusicInfo
|
from app.domain.context import Context, MediaInfo, MusicInfo
|
||||||
from app.domain.media import MUSIC_SUBSCRIBABLE_TYPES
|
from app.domain.media import MUSIC_SUBSCRIBABLE_TYPES
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
@@ -35,6 +41,7 @@ def _normalize_music_total_tracks(value: Any) -> Optional[int]:
|
|||||||
|
|
||||||
|
|
||||||
class MusicSubscribeMixin:
|
class MusicSubscribeMixin:
|
||||||
|
__mixin_host_protocol__ = MusicSubscribeMixinHost
|
||||||
"""
|
"""
|
||||||
音乐订阅功能域 mixin:单曲/专辑目标识别、实体快照同步、候选筛选、
|
音乐订阅功能域 mixin:单曲/专辑目标识别、实体快照同步、候选筛选、
|
||||||
择优下载与完成推进。
|
择优下载与完成推进。
|
||||||
|
|||||||
@@ -7,20 +7,22 @@
|
|||||||
import copy
|
import copy
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from app.runtime.execution import run_in_threadpool
|
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
|
from app.chain._contracts import ChainRuntimeMixinHost
|
||||||
from app.domain.context import MediaInfo, MusicInfo
|
from app.domain.context import MediaInfo, MusicInfo
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.runtime.cache import fresh, async_fresh
|
from app.runtime.cache import async_fresh, fresh
|
||||||
from app.runtime.events import Event
|
from app.runtime.events import Event
|
||||||
|
from app.runtime.execution import run_in_threadpool
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||||
from app.schemas.types import ChainEventType, MediaSource, MediaType, SystemConfigKey
|
from app.schemas.types import ChainEventType, MediaSource, MediaType, SystemConfigKey
|
||||||
|
|
||||||
|
|
||||||
class RecognitionMixin:
|
class RecognitionMixin:
|
||||||
|
__mixin_host_protocol__ = ChainRuntimeMixinHost
|
||||||
|
|
||||||
def _can_use_media_recognize_share(
|
def _can_use_media_recognize_share(
|
||||||
self,
|
self,
|
||||||
|
|||||||
+22
-10
@@ -7,21 +7,12 @@ TransferChain 中。mixin 方法运行时经 MRO 解析,共享 TransferChain
|
|||||||
注意:这里的方法均已去掉私有名前缀双下划线(__ -> _),因为 Python 的名字
|
注意:这里的方法均已去掉私有名前缀双下划线(__ -> _),因为 Python 的名字
|
||||||
改编按定义类生效,方法迁到 mixin 后 __ 前缀会改变改编目标,导致跨类调用失败。
|
改编按定义类生效,方法迁到 mixin 后 __ 前缀会改变改编目标,导致跨类调用失败。
|
||||||
"""
|
"""
|
||||||
import asyncio
|
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.schemas.history import DownloadHistory as _SchemaDownloadHistory
|
|
||||||
from app.schemas.transfer import EpisodeFormatRule as _SchemaEpisodeFormatRule
|
|
||||||
from app.adapters.system.host import SystemUtils
|
from app.adapters.system.host import SystemUtils
|
||||||
from app.application.agent import build_manual_redo_prompt, get_running_agent_manager
|
from app.application.agent import build_manual_redo_prompt, get_running_agent_manager
|
||||||
from app.application.formatting import EpisodeFormatRuleHelper
|
|
||||||
from app.application.history import clear_transfer_failures, resolve_history
|
|
||||||
from app.application.transfer import TransferTask, job_lock
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.storage import StorageChain
|
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.application.chain.data import (
|
from app.application.chain.data import (
|
||||||
get_chain_download_history_port,
|
get_chain_download_history_port,
|
||||||
get_chain_transfer_history_port,
|
get_chain_transfer_history_port,
|
||||||
@@ -30,6 +21,18 @@ from app.application.configuration import (
|
|||||||
get_chain_runtime_config_snapshot,
|
get_chain_runtime_config_snapshot,
|
||||||
get_configured_system_config,
|
get_configured_system_config,
|
||||||
)
|
)
|
||||||
|
from app.application.formatting import EpisodeFormatRuleHelper
|
||||||
|
from app.application.history import clear_transfer_failures, resolve_history
|
||||||
|
from app.application.transfer import TransferTask, job_lock
|
||||||
|
from app.chain._contracts import TransferMixinHost
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.storage import StorageChain
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
|
|
||||||
|
# 旧测试与插件补丁入口;正式依赖通过宿主工厂逐步收敛。
|
||||||
|
MediaChain = MediaChain
|
||||||
|
StorageChain = StorageChain
|
||||||
|
SubscribeChain = SubscribeChain
|
||||||
from app.domain.context import MediaInfo, MusicInfo
|
from app.domain.context import MediaInfo, MusicInfo
|
||||||
from app.domain.media import normalize_music_type
|
from app.domain.media import normalize_music_type
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
@@ -38,9 +41,10 @@ from app.foundation import text as text_tools
|
|||||||
from app.runtime.config import global_vars
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.runtime.tasks import get_task_registry
|
from app.runtime.tasks import get_task_registry
|
||||||
from app.schemas.workflow import FileItem
|
from app.schemas.history import DownloadHistory as _SchemaDownloadHistory
|
||||||
from app.schemas.message import Message
|
from app.schemas.message import Message
|
||||||
from app.schemas.tmdb import TmdbEpisode
|
from app.schemas.tmdb import TmdbEpisode
|
||||||
|
from app.schemas.transfer import EpisodeFormatRule as _SchemaEpisodeFormatRule
|
||||||
from app.schemas.transfer import TransferInfo
|
from app.schemas.transfer import TransferInfo
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_ALBUM,
|
MUSIC_ENTITY_ALBUM,
|
||||||
@@ -51,6 +55,7 @@ from app.schemas.types import (
|
|||||||
ReplyMode,
|
ReplyMode,
|
||||||
SystemConfigKey,
|
SystemConfigKey,
|
||||||
)
|
)
|
||||||
|
from app.schemas.workflow import FileItem
|
||||||
|
|
||||||
DownloadFiles = Any
|
DownloadFiles = Any
|
||||||
DownloadHistory = Any
|
DownloadHistory = Any
|
||||||
@@ -100,6 +105,7 @@ SUBTITLE_STEM_TAGS = {
|
|||||||
|
|
||||||
|
|
||||||
class FileFilterMixin:
|
class FileFilterMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _requires_automatic_category(task: TransferTask) -> bool:
|
def _requires_automatic_category(task: TransferTask) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -446,6 +452,7 @@ class FileFilterMixin:
|
|||||||
|
|
||||||
|
|
||||||
class ScrapeBatchMixin:
|
class ScrapeBatchMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
|
|
||||||
def _send_metadata_scrape_event(
|
def _send_metadata_scrape_event(
|
||||||
self, task: TransferTask, transferinfo: TransferInfo
|
self, task: TransferTask, transferinfo: TransferInfo
|
||||||
@@ -647,6 +654,7 @@ class ScrapeBatchMixin:
|
|||||||
|
|
||||||
|
|
||||||
class EpisodeFormatMixin:
|
class EpisodeFormatMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
|
|
||||||
def recommend_name(self, meta: MetaBase, mediainfo: MediaInfo) -> Optional[str]:
|
def recommend_name(self, meta: MetaBase, mediainfo: MediaInfo) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@@ -837,6 +845,7 @@ class EpisodeFormatMixin:
|
|||||||
|
|
||||||
|
|
||||||
class HistoryMatchMixin:
|
class HistoryMatchMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _match_download_file(
|
def _match_download_file(
|
||||||
download_file: DownloadFiles,
|
download_file: DownloadFiles,
|
||||||
@@ -1023,6 +1032,7 @@ class HistoryMatchMixin:
|
|||||||
|
|
||||||
|
|
||||||
class FileKeyMixin:
|
class FileKeyMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_file_key(fileitem: FileItem) -> Tuple[str, str]:
|
def _get_file_key(fileitem: FileItem) -> Tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
@@ -1110,6 +1120,7 @@ class FileKeyMixin:
|
|||||||
|
|
||||||
|
|
||||||
class ManualHistoryMixin:
|
class ManualHistoryMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_subscribe_custom_words(
|
def _get_subscribe_custom_words(
|
||||||
history_record: Optional[DownloadHistory],
|
history_record: Optional[DownloadHistory],
|
||||||
@@ -1234,6 +1245,7 @@ class ManualHistoryMixin:
|
|||||||
|
|
||||||
|
|
||||||
class FailedRetryMixin:
|
class FailedRetryMixin:
|
||||||
|
__mixin_host_protocol__ = TransferMixinHost
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def build_failed_transfer_buttons(
|
def build_failed_transfer_buttons(
|
||||||
history_id: Optional[int],
|
history_id: Optional[int],
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
|
||||||
|
|
||||||
class AniListChain(ChainBase):
|
class AniListChain(ChainBase):
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
|
||||||
|
|
||||||
class BangumiChain(ChainBase):
|
class BangumiChain(ChainBase):
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from app.chain import ChainBase
|
||||||
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
|
from app.schemas.dashboard import DownloaderInfo as _SchemaDownloaderInfo
|
||||||
from app.schemas.dashboard import Statistic as _SchemaStatistic
|
from app.schemas.dashboard import Statistic as _SchemaStatistic
|
||||||
from app.chain import ChainBase
|
|
||||||
|
|
||||||
|
|
||||||
class DashboardChain(ChainBase):
|
class DashboardChain(ChainBase):
|
||||||
|
|||||||
+1
-1
@@ -1,9 +1,9 @@
|
|||||||
from typing import Any, List, Optional
|
from typing import Any, List, Optional
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.domain.context import MediaInfo, MusicAlbumInfo, MusicInfo
|
from app.domain.context import MediaInfo, MusicAlbumInfo, MusicInfo
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType
|
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+44
-37
@@ -6,18 +6,25 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, List, Optional, Tuple, Set, Dict, Union
|
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple, Union
|
||||||
from urllib.parse import parse_qs, urlencode, urljoin, urlparse
|
from urllib.parse import parse_qs, urlencode, urljoin, urlparse
|
||||||
|
|
||||||
from app.schemas.transfer import DownloaderTorrent as _SchemaDownloaderTorrent
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.schemas.system import TransferDirectoryConf as _SchemaTransferDirectoryConf
|
from app.adapters.system.host import SystemUtils
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.application.chain.data import (
|
||||||
|
get_chain_download_failure_port,
|
||||||
|
get_chain_download_history_port,
|
||||||
|
get_chain_media_server_port,
|
||||||
|
)
|
||||||
|
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||||
|
from app.application.directory import DirectoryHelper, validate_download_save_path
|
||||||
|
from app.application.download import selection as _selection
|
||||||
|
from app.application.download.tasks import DownloadTaskService
|
||||||
|
from app.application.torrent import TorrentHelper
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.media import MediaChain
|
from app.chain.media import MediaChain
|
||||||
from app.chain.storage import StorageChain
|
from app.chain.storage import StorageChain
|
||||||
from app.runtime.cache import FileCache
|
from app.domain import episode as episode_rules
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
|
||||||
from app.domain.context import (
|
from app.domain.context import (
|
||||||
Context,
|
Context,
|
||||||
MediaInfo,
|
MediaInfo,
|
||||||
@@ -25,36 +32,36 @@ from app.domain.context import (
|
|||||||
SubtitleInfo,
|
SubtitleInfo,
|
||||||
TorrentInfo,
|
TorrentInfo,
|
||||||
)
|
)
|
||||||
from app.runtime.events import eventmanager, Event
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.domain.metainfo import MetaInfo
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.application.chain.data import (
|
|
||||||
get_chain_download_failure_port,
|
|
||||||
get_chain_download_history_port,
|
|
||||||
get_chain_media_server_port,
|
|
||||||
)
|
|
||||||
from app.application.directory import DirectoryHelper, validate_download_save_path
|
|
||||||
from app.application.download.tasks import DownloadTaskService
|
|
||||||
from app.application.download import selection as _selection
|
|
||||||
from app.runtime.thread import ThreadHelper
|
|
||||||
from app.application.torrent import TorrentHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.mediaserver import ExistMediaInfo
|
|
||||||
from app.schemas.file import FileURI
|
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo
|
|
||||||
from app.schemas.transfer import DownloaderTorrent
|
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.event import ResourceSelectionEventData
|
|
||||||
from app.schemas.event import ResourceDownloadEventData
|
|
||||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType, TorrentStatus, EventType, NotificationChannel, MessageType, ContentType, \
|
|
||||||
ChainEventType
|
|
||||||
from app.adapters.network.http import RequestUtils
|
|
||||||
from app.schemas.media import build_media_key, resolve_media_identity
|
|
||||||
from app.domain import episode as episode_rules
|
|
||||||
from app.foundation import size as size_tools
|
from app.foundation import size as size_tools
|
||||||
from app.foundation import text as text_tools
|
from app.foundation import text as text_tools
|
||||||
from app.adapters.system.host import SystemUtils
|
from app.runtime.cache import FileCache
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.runtime.thread import ThreadHelper
|
||||||
|
from app.schemas.event import ResourceDownloadEventData, ResourceSelectionEventData
|
||||||
|
from app.schemas.file import FileURI
|
||||||
|
from app.schemas.media import build_media_key, resolve_media_identity
|
||||||
|
from app.schemas.mediaserver import ExistMediaInfo, NotExistMediaInfo
|
||||||
|
from app.schemas.message import Message
|
||||||
|
from app.schemas.system import TransferDirectoryConf as _SchemaTransferDirectoryConf
|
||||||
|
from app.schemas.transfer import DownloaderTorrent
|
||||||
|
from app.schemas.transfer import DownloaderTorrent as _SchemaDownloaderTorrent
|
||||||
|
from app.schemas.types import (
|
||||||
|
MUSIC_ENTITY_ALBUM,
|
||||||
|
ChainEventType,
|
||||||
|
ContentType,
|
||||||
|
EventType,
|
||||||
|
MediaSource,
|
||||||
|
MediaType,
|
||||||
|
MessageType,
|
||||||
|
NotificationChannel,
|
||||||
|
TorrentStatus,
|
||||||
|
)
|
||||||
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Any
|
from typing import Any
|
||||||
@@ -1000,7 +1007,7 @@ class DownloadChain(ChainBase):
|
|||||||
MediaType.MUSIC: set(),
|
MediaType.MUSIC: set(),
|
||||||
}
|
}
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
media_type = context.media_info.type
|
media_type = context.media_info.type
|
||||||
if media_type not in downloaded_keys:
|
if media_type not in downloaded_keys:
|
||||||
@@ -1657,7 +1664,7 @@ class DownloadChain(ChainBase):
|
|||||||
for need_mid, need_season in need_seasons.items():
|
for need_mid, need_season in need_seasons.items():
|
||||||
# 循环种子
|
# 循环种子
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
# 媒体信息
|
# 媒体信息
|
||||||
media = context.media_info
|
media = context.media_info
|
||||||
@@ -1801,7 +1808,7 @@ class DownloadChain(ChainBase):
|
|||||||
need_episodes = list(range(start_episode, total_episode + 1))
|
need_episodes = list(range(start_episode, total_episode + 1))
|
||||||
# 循环种子
|
# 循环种子
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
# 媒体信息
|
# 媒体信息
|
||||||
media = context.media_info
|
media = context.media_info
|
||||||
@@ -1889,7 +1896,7 @@ class DownloadChain(ChainBase):
|
|||||||
continue
|
continue
|
||||||
# 循环种子
|
# 循环种子
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
# 媒体信息
|
# 媒体信息
|
||||||
media = context.media_info
|
media = context.media_info
|
||||||
@@ -2040,7 +2047,7 @@ class DownloadChain(ChainBase):
|
|||||||
media_id=media_id,
|
media_id=media_id,
|
||||||
episode_group=mediainfo.episode_group)
|
episode_group=mediainfo.episode_group)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
logger.error(f"媒体信息识别失败!")
|
logger.error("媒体信息识别失败!")
|
||||||
return False, {}
|
return False, {}
|
||||||
if not mediainfo.seasons:
|
if not mediainfo.seasons:
|
||||||
logger.error(f"媒体信息中没有季集信息:{mediainfo.title_year}")
|
logger.error(f"媒体信息中没有季集信息:{mediainfo.title_year}")
|
||||||
|
|||||||
@@ -2,18 +2,18 @@ import math
|
|||||||
import re
|
import re
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
from app.application.chain.data import get_chain_user_port
|
||||||
from app.chain.download import DownloadChain
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.search import SearchChain
|
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
from app.application.messaging.media import (
|
from app.application.messaging.media import (
|
||||||
PendingMediaInteraction,
|
PendingMediaInteraction,
|
||||||
media_interaction_manager,
|
media_interaction_manager,
|
||||||
)
|
)
|
||||||
from app.application.torrent import TorrentHelper
|
from app.application.torrent import TorrentHelper
|
||||||
from app.application.chain.data import get_chain_user_port
|
from app.chain import ChainBase
|
||||||
|
from app.chain.download import DownloadChain
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.domain import episode as episode_rules
|
from app.domain import episode as episode_rules
|
||||||
from app.domain import title as title_rules
|
from app.domain import title as title_rules
|
||||||
from app.domain.context import Context, MediaInfo
|
from app.domain.context import Context, MediaInfo
|
||||||
@@ -22,9 +22,9 @@ from app.foundation import url as url_tools
|
|||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.download import DownloadDirectory
|
from app.schemas.download import DownloadDirectory
|
||||||
from app.schemas.file import FileURI
|
from app.schemas.file import FileURI
|
||||||
|
from app.schemas.media import build_media_key, resolve_media_identity
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo
|
from app.schemas.mediaserver import NotExistMediaInfo
|
||||||
from app.schemas.message import Message
|
from app.schemas.message import Message
|
||||||
from app.schemas.media import build_media_key, resolve_media_identity
|
|
||||||
from app.schemas.notification import ChannelCapabilityManager
|
from app.schemas.notification import ChannelCapabilityManager
|
||||||
from app.schemas.system import TransferDirectoryConf
|
from app.schemas.system import TransferDirectoryConf
|
||||||
from app.schemas.types import MediaType, NotificationChannel
|
from app.schemas.types import MediaType, NotificationChannel
|
||||||
|
|||||||
+16
-16
@@ -3,15 +3,15 @@ from pathlib import Path
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Iterable, List, Optional, Tuple, Union
|
from typing import Any, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.runtime.execution import run_in_threadpool
|
from app.application.audio import AudioMetadataHelper
|
||||||
from app.schemas.event import MediaRecognizeConvertEventData as _SchemaMediaRecognizeConvertEventData
|
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||||
|
from app.application.music.catalog import MusicCatalogService
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.acoustid import AcoustIdChain
|
from app.chain.acoustid import AcoustIdChain
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.musicbrainz import MusicBrainzChain, _MusicMetadataSourceChain
|
from app.chain.musicbrainz import MusicBrainzChain, _MusicMetadataSourceChain
|
||||||
from app.chain.theaudiodb import TheAudioDbChain
|
from app.chain.theaudiodb import TheAudioDbChain
|
||||||
from app.runtime.cache import async_fresh, fresh
|
from app.domain import title as title_rules
|
||||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
|
||||||
from app.domain.context import (
|
from app.domain.context import (
|
||||||
Context,
|
Context,
|
||||||
MediaInfo,
|
MediaInfo,
|
||||||
@@ -19,13 +19,18 @@ from app.domain.context import (
|
|||||||
MusicArtistInfo,
|
MusicArtistInfo,
|
||||||
MusicInfo,
|
MusicInfo,
|
||||||
)
|
)
|
||||||
from app.runtime.events import Event
|
from app.domain.media import is_music_media_source
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.application.audio import AudioMetadataHelper
|
from app.foundation.singleton import Singleton
|
||||||
from app.application.music.catalog import MusicCatalogService
|
from app.foundation.text import convert as zhconv_convert
|
||||||
|
from app.runtime.cache import async_fresh, fresh
|
||||||
|
from app.runtime.events import Event
|
||||||
|
from app.runtime.execution import run_in_threadpool
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.event import MediaRecognizeConvertEventData as _SchemaMediaRecognizeConvertEventData
|
||||||
|
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
ChainEventType,
|
ChainEventType,
|
||||||
@@ -33,11 +38,6 @@ from app.schemas.types import (
|
|||||||
MediaSourceSelection,
|
MediaSourceSelection,
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from app.domain.media import is_music_media_source
|
|
||||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
|
||||||
from app.foundation.singleton import Singleton
|
|
||||||
from app.foundation.text import convert as zhconv_convert
|
|
||||||
from app.domain import title as title_rules
|
|
||||||
|
|
||||||
recognize_lock = Lock()
|
recognize_lock = Lock()
|
||||||
|
|
||||||
@@ -786,9 +786,9 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
year = None
|
year = None
|
||||||
# 结果赋值
|
# 结果赋值
|
||||||
if title == org_meta.name and year == org_meta.year:
|
if title == org_meta.name and year == org_meta.year:
|
||||||
logger.info(f"辅助识别与原始识别结果一致,无需重新识别媒体信息")
|
logger.info("辅助识别与原始识别结果一致,无需重新识别媒体信息")
|
||||||
return None
|
return None
|
||||||
logger.info(f"辅助识别结果与原始识别结果不一致,重新匹配媒体信息 ...")
|
logger.info("辅助识别结果与原始识别结果不一致,重新匹配媒体信息 ...")
|
||||||
org_meta.name = title
|
org_meta.name = title
|
||||||
org_meta.year = year
|
org_meta.year = year
|
||||||
org_meta.begin_season = season_number
|
org_meta.begin_season = season_number
|
||||||
@@ -1725,9 +1725,9 @@ class MediaChain(ChainBase, metaclass=Singleton):
|
|||||||
year = None
|
year = None
|
||||||
# 结果赋值
|
# 结果赋值
|
||||||
if title == org_meta.name and year == org_meta.year:
|
if title == org_meta.name and year == org_meta.year:
|
||||||
logger.info(f"辅助识别与原始识别结果一致,无需重新识别媒体信息")
|
logger.info("辅助识别与原始识别结果一致,无需重新识别媒体信息")
|
||||||
return None
|
return None
|
||||||
logger.info(f"辅助识别结果与原始识别结果不一致,重新匹配媒体信息 ...")
|
logger.info("辅助识别结果与原始识别结果不一致,重新匹配媒体信息 ...")
|
||||||
org_meta.name = title
|
org_meta.name = title
|
||||||
org_meta.year = year
|
org_meta.year = year
|
||||||
org_meta.begin_season = season_number
|
org_meta.begin_season = season_number
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Callable, Dict, List, Union, Optional, Generator, Any, Tuple
|
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.application.chain.data import get_chain_media_server_port
|
from app.application.chain.data import get_chain_media_server_port
|
||||||
from app.application.mediaserver import get_mediaserver_configs
|
from app.application.mediaserver import get_mediaserver_configs
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.mediaserver import MediaServerLibrary
|
|
||||||
from app.schemas.mediaserver import MediaServerItem
|
|
||||||
from app.schemas.mediaserver import MediaServerSeasonInfo
|
|
||||||
from app.schemas.mediaserver import MediaServerPlayItem
|
|
||||||
from app.schemas.types import MediaType
|
|
||||||
from app.application.security.url import SecurityUtils
|
from app.application.security.url import SecurityUtils
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.mediaserver import MediaServerItem, MediaServerLibrary, MediaServerPlayItem, MediaServerSeasonInfo
|
||||||
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
@@ -355,7 +352,7 @@ class MediaServerChain(ChainBase):
|
|||||||
library_media_total = library_media_counts.get(str(library.id))
|
library_media_total = library_media_counts.get(str(library.id))
|
||||||
library_count = 0
|
library_count = 0
|
||||||
for item in self.items(server=server_name, library_id=library.id):
|
for item in self.items(server=server_name, library_id=library.id):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
return total_count, global_media_finished
|
return total_count, global_media_finished
|
||||||
if not item or not item.item_id:
|
if not item or not item.item_id:
|
||||||
continue
|
continue
|
||||||
@@ -554,7 +551,7 @@ class MediaServerChain(ChainBase):
|
|||||||
global_media_total=global_media_total,
|
global_media_total=global_media_total,
|
||||||
global_media_finished=global_media_finished,
|
global_media_finished=global_media_finished,
|
||||||
)
|
)
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
return
|
return
|
||||||
total_count += server_count
|
total_count += server_count
|
||||||
logger.info(f"媒体服务器 {server_name} 数据同步完成,总同步数量:{total_count}")
|
logger.info(f"媒体服务器 {server_name} 数据同步完成,总同步数量:{total_count}")
|
||||||
|
|||||||
+10
-11
@@ -7,22 +7,16 @@ from concurrent.futures import CancelledError as FutureCancelledError
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional, Dict, Union, List, Tuple
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
from urllib.parse import unquote, urlparse
|
from urllib.parse import unquote, urlparse
|
||||||
|
|
||||||
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.application.agent import (
|
from app.application.agent import (
|
||||||
get_running_agent_manager,
|
get_running_agent_manager,
|
||||||
is_audio_input_available,
|
is_audio_input_available,
|
||||||
supports_image_input,
|
supports_image_input,
|
||||||
transcribe_audio,
|
transcribe_audio,
|
||||||
)
|
)
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain.site import SiteChain
|
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.chain.transfer import TransferChain
|
|
||||||
from app.chain.interaction import MediaInteractionChain as _MediaInteractionChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.tasks import get_task_registry
|
|
||||||
from app.application.messaging.agent import agent_interaction_manager, parse_agent_choice_callback
|
from app.application.messaging.agent import agent_interaction_manager, parse_agent_choice_callback
|
||||||
from app.application.messaging.interaction import InteractionContext, InteractionDispatch
|
from app.application.messaging.interaction import InteractionContext, InteractionDispatch
|
||||||
from app.application.messaging.media import media_interaction_manager
|
from app.application.messaging.media import media_interaction_manager
|
||||||
@@ -32,12 +26,17 @@ from app.application.messaging.session import MessageSessionService
|
|||||||
from app.application.messaging.site import site_interaction_manager
|
from app.application.messaging.site import site_interaction_manager
|
||||||
from app.application.messaging.skill import SkillInteractionHandler, skill_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.subscribe import subscribe_interaction_manager
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.chain.interaction import MediaInteractionChain as _MediaInteractionChain
|
||||||
|
from app.chain.site import SiteChain
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
|
from app.chain.transfer import TransferChain
|
||||||
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.message import IncomingMessage
|
from app.runtime.tasks import get_task_registry
|
||||||
from app.schemas.message import Message
|
from app.schemas.message import IncomingMessage, Message
|
||||||
from app.schemas.notification import ChannelCapabilityManager
|
from app.schemas.notification import ChannelCapabilityManager
|
||||||
from app.schemas.types import EventType, NotificationChannel
|
from app.schemas.types import EventType, NotificationChannel
|
||||||
from app.adapters.network.http import RequestUtils
|
|
||||||
|
|
||||||
|
|
||||||
class MessageChain(ChainBase):
|
class MessageChain(ChainBase):
|
||||||
|
|||||||
@@ -2,25 +2,25 @@ from typing import Callable, List, Optional
|
|||||||
|
|
||||||
import pillow_avif # noqa: F401 # pylint: disable=unused-import # AVIF 注册副作用
|
import pillow_avif # noqa: F401 # pylint: disable=unused-import # AVIF 注册副作用
|
||||||
|
|
||||||
|
from app.application.image import ImageHelper
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.bangumi import BangumiChain
|
from app.chain.bangumi import BangumiChain
|
||||||
from app.chain.douban import DoubanChain
|
from app.chain.douban import DoubanChain
|
||||||
from app.chain.listenbrainz import ListenBrainzChain
|
from app.chain.listenbrainz import ListenBrainzChain
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
from app.runtime.cache import cached, fresh
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import MusicInfo
|
from app.domain.context import MusicInfo
|
||||||
from app.application.image import ImageHelper
|
from app.foundation.singleton import Singleton
|
||||||
|
from app.runtime.cache import cached, fresh
|
||||||
|
from app.runtime.execution import log_execution_time
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.media import normalize_media_source
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_ALBUM,
|
MUSIC_ENTITY_ALBUM,
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
MediaSource,
|
MediaSource,
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from app.runtime.execution import log_execution_time
|
|
||||||
from app.schemas.media import normalize_media_source
|
|
||||||
from app.foundation.singleton import Singleton
|
|
||||||
|
|
||||||
|
|
||||||
class RecommendChain(ChainBase, metaclass=Singleton):
|
class RecommendChain(ChainBase, metaclass=Singleton):
|
||||||
@@ -222,7 +222,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
|
|||||||
# 这里避免区间内连续调用相同来源,因此遍历方案为每页遍历所有推荐来源,再进行页数遍历
|
# 这里避免区间内连续调用相同来源,因此遍历方案为每页遍历所有推荐来源,再进行页数遍历
|
||||||
for page in range(1, self.cache_max_pages + 1):
|
for page in range(1, self.cache_max_pages + 1):
|
||||||
for method in recommend_methods:
|
for method in recommend_methods:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
return
|
return
|
||||||
if method in methods_finished:
|
if method in methods_finished:
|
||||||
continue
|
continue
|
||||||
@@ -277,7 +277,7 @@ class RecommendChain(ChainBase, metaclass=Singleton):
|
|||||||
|
|
||||||
total_num = len(datas)
|
total_num = len(datas)
|
||||||
for index, data in enumerate(datas, start=1):
|
for index, data in enumerate(datas, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
return
|
return
|
||||||
poster_path = data.get("poster_path")
|
poster_path = data.get("poster_path")
|
||||||
if poster_path:
|
if poster_path:
|
||||||
|
|||||||
+15
-18
@@ -7,47 +7,44 @@ from tempfile import NamedTemporaryFile, TemporaryDirectory
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import Any, Iterable, List, Optional, Tuple, Union
|
from typing import Any, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.adapters.network.http import RequestUtils
|
||||||
|
from app.application.audio import AudioMetadataHelper
|
||||||
|
from app.application.configuration import (
|
||||||
|
get_chain_runtime_config_snapshot,
|
||||||
|
get_configured_system_config,
|
||||||
|
)
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.lrclib import LrclibChain
|
from app.chain.lrclib import LrclibChain
|
||||||
|
from app.chain.media import MediaChain
|
||||||
from app.chain.storage import StorageChain
|
from app.chain.storage import StorageChain
|
||||||
from app.runtime.cache import cached
|
|
||||||
from app.domain.context import (
|
from app.domain.context import (
|
||||||
MediaInfo,
|
MediaInfo,
|
||||||
MusicAlbumInfo,
|
MusicAlbumInfo,
|
||||||
MusicInfo,
|
MusicInfo,
|
||||||
MusicLyrics,
|
MusicLyrics,
|
||||||
)
|
)
|
||||||
from app.runtime.events import eventmanager, Event
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
from app.domain.meta.metabase import MetaBase
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
from app.domain.metainfo import MetaInfo, MetaInfoPath
|
||||||
from app.application.configuration import (
|
from app.foundation.singleton import Singleton
|
||||||
get_chain_runtime_config_snapshot,
|
from app.runtime.cache import cached
|
||||||
get_configured_system_config,
|
from app.runtime.events import Event, eventmanager
|
||||||
)
|
|
||||||
from app.application.audio import AudioMetadataHelper
|
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.workflow import FileItem
|
from app.runtime.reload import ConfigReloadMixin
|
||||||
|
from app.schemas.media import resolve_media_identity
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_ALBUM,
|
MUSIC_ENTITY_ALBUM,
|
||||||
MUSIC_ENTITY_RECORDING,
|
MUSIC_ENTITY_RECORDING,
|
||||||
EventType,
|
EventType,
|
||||||
MediaSource,
|
MediaSource,
|
||||||
MediaType,
|
MediaType,
|
||||||
ScrapingTarget,
|
|
||||||
ScrapingMetadata,
|
ScrapingMetadata,
|
||||||
ScrapingPolicy,
|
ScrapingPolicy,
|
||||||
|
ScrapingTarget,
|
||||||
SystemConfigKey,
|
SystemConfigKey,
|
||||||
)
|
)
|
||||||
from app.adapters.network.http import RequestUtils
|
from app.schemas.workflow import FileItem
|
||||||
from app.schemas.media import resolve_media_identity
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.reload import ConfigReloadMixin
|
|
||||||
from app.foundation.singleton import Singleton
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
|
|
||||||
scraping_lock = Lock()
|
scraping_lock = Lock()
|
||||||
|
|
||||||
|
|||||||
+22
-25
@@ -7,34 +7,34 @@ import time
|
|||||||
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, as_completed, wait
|
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, as_completed, wait
|
||||||
from contextlib import aclosing
|
from contextlib import aclosing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import AsyncIterator, Any, Awaitable, Callable, Dict, Iterable, Tuple
|
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterable, List, Optional, Tuple
|
||||||
from typing import List, Optional
|
|
||||||
from unicodedata import normalize
|
from unicodedata import normalize
|
||||||
|
|
||||||
from app.runtime.execution import run_in_threadpool, submit_with_context
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import Context
|
|
||||||
from app.domain.context import MediaInfo, SubtitleInfo, TorrentInfo
|
|
||||||
from app.runtime.events import eventmanager, Event
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.domain.context import MusicInfo
|
|
||||||
from app.application.configuration import (
|
from app.application.configuration import (
|
||||||
get_chain_runtime_config_snapshot,
|
get_chain_runtime_config_snapshot,
|
||||||
get_configured_system_config,
|
get_configured_system_config,
|
||||||
)
|
)
|
||||||
from app.runtime.progress import AsyncProgressHelper, ProgressHelper
|
|
||||||
from app.runtime.tasks import get_task_registry
|
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
|
||||||
from app.application.search.state import (
|
from app.application.search.state import (
|
||||||
SearchStateService,
|
SearchStateService,
|
||||||
normalize_search_params,
|
normalize_search_params,
|
||||||
stringify_sites,
|
stringify_sites,
|
||||||
)
|
)
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
from app.application.torrent import TorrentHelper
|
from app.application.torrent import TorrentHelper
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.domain.context import Context, MediaInfo, MusicInfo, SubtitleInfo, TorrentInfo
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
|
from app.foundation import size as size_tools
|
||||||
|
from app.foundation.text import convert as zhconv_convert
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
|
from app.runtime.execution import run_in_threadpool, submit_with_context
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.progress import AsyncProgressHelper, ProgressHelper
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.runtime.tasks import get_task_registry
|
||||||
|
from app.schemas.media import build_media_key, resolve_media_identity
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo
|
from app.schemas.mediaserver import NotExistMediaInfo
|
||||||
from app.schemas.types import (
|
from app.schemas.types import (
|
||||||
MUSIC_ENTITY_ALBUM,
|
MUSIC_ENTITY_ALBUM,
|
||||||
@@ -44,9 +44,6 @@ from app.schemas.types import (
|
|||||||
ProgressKey,
|
ProgressKey,
|
||||||
SystemConfigKey,
|
SystemConfigKey,
|
||||||
)
|
)
|
||||||
from app.schemas.media import build_media_key, resolve_media_identity
|
|
||||||
from app.foundation import size as size_tools
|
|
||||||
from app.foundation.text import convert as zhconv_convert
|
|
||||||
|
|
||||||
|
|
||||||
class SearchChain(ChainBase):
|
class SearchChain(ChainBase):
|
||||||
@@ -1355,7 +1352,7 @@ class SearchChain(ChainBase):
|
|||||||
logger.info(f"开始匹配结果 标题:{mediainfo.title},原标题:{mediainfo.original_title},别名:{mediainfo.names}")
|
logger.info(f"开始匹配结果 标题:{mediainfo.title},原标题:{mediainfo.original_title},别名:{mediainfo.names}")
|
||||||
progress.update(value=51, text=f'开始匹配,总 {_total} 个资源 ...')
|
progress.update(value=51, text=f'开始匹配,总 {_total} 个资源 ...')
|
||||||
for torrent in torrents:
|
for torrent in torrents:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
_count += 1
|
_count += 1
|
||||||
progress.update(value=(_count / _total) * 96,
|
progress.update(value=(_count / _total) * 96,
|
||||||
@@ -1709,7 +1706,7 @@ class SearchChain(ChainBase):
|
|||||||
**self._media_recognize_kwargs(mediainfo),
|
**self._media_recognize_kwargs(mediainfo),
|
||||||
)
|
)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
logger.error(f'媒体信息识别失败!')
|
logger.error('媒体信息识别失败!')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# 准备搜索参数
|
# 准备搜索参数
|
||||||
@@ -1802,7 +1799,7 @@ class SearchChain(ChainBase):
|
|||||||
**self._media_recognize_kwargs(mediainfo),
|
**self._media_recognize_kwargs(mediainfo),
|
||||||
)
|
)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
logger.error(f'媒体信息识别失败!')
|
logger.error('媒体信息识别失败!')
|
||||||
return []
|
return []
|
||||||
|
|
||||||
# 准备搜索参数
|
# 准备搜索参数
|
||||||
@@ -1885,7 +1882,7 @@ class SearchChain(ChainBase):
|
|||||||
**self._media_recognize_kwargs(mediainfo),
|
**self._media_recognize_kwargs(mediainfo),
|
||||||
)
|
)
|
||||||
if not mediainfo:
|
if not mediainfo:
|
||||||
logger.error(f'媒体信息识别失败!')
|
logger.error('媒体信息识别失败!')
|
||||||
yield {
|
yield {
|
||||||
"type": "error",
|
"type": "error",
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -2071,7 +2068,7 @@ class SearchChain(ChainBase):
|
|||||||
match_subtitles = []
|
match_subtitles = []
|
||||||
logger.info(f"开始匹配字幕 标题:{mediainfo.title},原标题:{mediainfo.original_title},别名:{mediainfo.names}")
|
logger.info(f"开始匹配字幕 标题:{mediainfo.title},原标题:{mediainfo.original_title},别名:{mediainfo.names}")
|
||||||
for subtitle in subtitles:
|
for subtitle in subtitles:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
subtitle_names = self.__build_subtitle_names(subtitle)
|
subtitle_names = self.__build_subtitle_names(subtitle)
|
||||||
if not subtitle_names:
|
if not subtitle_names:
|
||||||
@@ -2384,7 +2381,7 @@ class SearchChain(ChainBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
while pending_tasks:
|
while pending_tasks:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
done_tasks, _ = wait(pending_tasks, return_when=FIRST_COMPLETED)
|
done_tasks, _ = wait(pending_tasks, return_when=FIRST_COMPLETED)
|
||||||
for future in done_tasks:
|
for future in done_tasks:
|
||||||
@@ -2460,7 +2457,7 @@ class SearchChain(ChainBase):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
while pending_tasks:
|
while pending_tasks:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
done_tasks, _ = await asyncio.wait(
|
done_tasks, _ = await asyncio.wait(
|
||||||
pending_tasks,
|
pending_tasks,
|
||||||
|
|||||||
+23
-23
@@ -1,35 +1,35 @@
|
|||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Callable, Optional, Tuple, Union, Dict
|
from typing import Any, Callable, Dict, Optional, Tuple, Union
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
from app.chain import ChainBase
|
from app.adapters.external.cookiecloud import CookieCloudHelper
|
||||||
from app.chain._interaction import InteractionChainMixin
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import Event, eventmanager
|
|
||||||
from app.application.chain.data import get_chain_site_port
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.adapters.network.browser import PlaywrightHelper
|
from app.adapters.network.browser import PlaywrightHelper
|
||||||
from app.adapters.network.cloudflare import under_challenge
|
from app.adapters.network.cloudflare import under_challenge
|
||||||
from app.application.security.cookie import CookieHelper
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.adapters.external.cookiecloud import CookieCloudHelper
|
from app.application.chain.data import get_chain_site_port
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.messaging.site import SiteInteractionHandler
|
from app.application.messaging.site import SiteInteractionHandler
|
||||||
from app.application.rss import RssHelper
|
from app.application.rss import RssHelper
|
||||||
from app.runtime.log import logger
|
from app.application.security.cookie import CookieHelper
|
||||||
from app.schemas.notification import NotificationChannel
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
from app.schemas.message import Message
|
from app.chain import ChainBase
|
||||||
from app.schemas.site import SiteUserData
|
from app.chain._interaction import InteractionChainMixin
|
||||||
from app.schemas.types import EventType, MessageType
|
|
||||||
from app.adapters.network.http import RequestUtils
|
|
||||||
from app.domain.site import SiteUtils
|
|
||||||
from app.domain import site as site_rules
|
from app.domain import site as site_rules
|
||||||
|
from app.domain.site import SiteUtils
|
||||||
from app.foundation import size as size_tools
|
from app.foundation import size as size_tools
|
||||||
from app.foundation import url as url_tools
|
from app.foundation import url as url_tools
|
||||||
from app.foundation.dom import DomUtils
|
from app.foundation.dom import DomUtils
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.message import Message
|
||||||
|
from app.schemas.notification import NotificationChannel
|
||||||
|
from app.schemas.site import SiteUserData
|
||||||
|
from app.schemas.types import EventType, MessageType
|
||||||
|
|
||||||
Site = Any
|
Site = Any
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ class SiteChain(InteractionChainMixin, ChainBase):
|
|||||||
re.search(r"(贵宾|VIP?)", userdata.user_level or "", re.IGNORECASE)):
|
re.search(r"(贵宾|VIP?)", userdata.user_level or "", re.IGNORECASE)):
|
||||||
self.post_message(Message(
|
self.post_message(Message(
|
||||||
mtype=MessageType.SiteMessage,
|
mtype=MessageType.SiteMessage,
|
||||||
title=f"【站点分享率低预警】",
|
title="【站点分享率低预警】",
|
||||||
text=f"站点 {site.get('name')} 分享率 {userdata.ratio},请注意!"
|
text=f"站点 {site.get('name')} 分享率 {userdata.ratio},请注意!"
|
||||||
))
|
))
|
||||||
return userdata
|
return userdata
|
||||||
@@ -140,7 +140,7 @@ class SiteChain(InteractionChainMixin, ChainBase):
|
|||||||
data={"total": total_num, "finished": 0},
|
data={"total": total_num, "finished": 0},
|
||||||
)
|
)
|
||||||
for index, site in enumerate(sites, start=1):
|
for index, site in enumerate(sites, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
return None
|
return None
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(
|
progress_callback(
|
||||||
@@ -429,7 +429,7 @@ class SiteChain(InteractionChainMixin, ChainBase):
|
|||||||
update_count = add_count = fail_count = 0
|
update_count = add_count = fail_count = 0
|
||||||
for index, (domain, cookie) in enumerate(cookies.items(), start=1):
|
for index, (domain, cookie) in enumerate(cookies.items(), start=1):
|
||||||
# 检查系统是否停止
|
# 检查系统是否停止
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
logger.info("系统正在停止,中断CookieCloud同步")
|
logger.info("系统正在停止,中断CookieCloud同步")
|
||||||
return False, "系统正在停止,同步被中断"
|
return False, "系统正在停止,同步被中断"
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
@@ -708,8 +708,8 @@ class SiteChain(InteractionChainMixin, ChainBase):
|
|||||||
timeout=timeout)
|
timeout=timeout)
|
||||||
if not public and not SiteUtils.is_logged_in(page_source):
|
if not public and not SiteUtils.is_logged_in(page_source):
|
||||||
if under_challenge(page_source):
|
if under_challenge(page_source):
|
||||||
return False, f"无法通过Cloudflare!"
|
return False, "无法通过Cloudflare!"
|
||||||
return False, f"仿真登录失败,Cookie已失效!"
|
return False, "仿真登录失败,Cookie已失效!"
|
||||||
else:
|
else:
|
||||||
res = RequestUtils(cookies=site_cookie,
|
res = RequestUtils(cookies=site_cookie,
|
||||||
ua=ua,
|
ua=ua,
|
||||||
@@ -731,7 +731,7 @@ class SiteChain(InteractionChainMixin, ChainBase):
|
|||||||
elif res is not None:
|
elif res is not None:
|
||||||
return False, f"错误:{res.status_code} {res.reason}!"
|
return False, f"错误:{res.status_code} {res.reason}!"
|
||||||
else:
|
else:
|
||||||
return False, f"无法打开网站!"
|
return False, "无法打开网站!"
|
||||||
return True, "连接成功"
|
return True, "连接成功"
|
||||||
|
|
||||||
def _interaction_handler(self) -> "SiteInteractionHandler":
|
def _interaction_handler(self) -> "SiteInteractionHandler":
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional, List, Dict
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
|
from app.chain import ChainBase
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
|
|
||||||
|
|
||||||
class StorageChain(ChainBase):
|
class StorageChain(ChainBase):
|
||||||
|
|||||||
+87
-55
@@ -5,35 +5,9 @@ import threading
|
|||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any, Callable, Dict, List, Optional, Union, Tuple
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.schemas.mediaserver import NotExistMediaInfo as _SchemaNotExistMediaInfo
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.schemas.message import Message as _SchemaMessage
|
|
||||||
from app.schemas.subscribe import SubscrbieInfo as _SchemaSubscrbieInfo
|
|
||||||
from app.schemas.subscribe import SubscribeDownloadFileInfo as _SchemaSubscribeDownloadFileInfo
|
|
||||||
from app.schemas.subscribe import SubscribeEpisodeInfo as _SchemaSubscribeEpisodeInfo
|
|
||||||
from app.schemas.subscribe import SubscribeLibraryFileInfo as _SchemaSubscribeLibraryFileInfo
|
|
||||||
from app.schemas.workflow import Subscribe as _SchemaSubscribe
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain._interaction import InteractionChainMixin
|
|
||||||
from app.chain._music import MusicSubscribeMixin
|
|
||||||
from app.chain.download import DownloadChain
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.mediaserver import MediaServerChain
|
|
||||||
from app.chain.search import SearchChain
|
|
||||||
from app.chain.tmdb import TmdbChain
|
|
||||||
from app.chain.torrents import TorrentsChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import (
|
|
||||||
Context,
|
|
||||||
MediaInfo,
|
|
||||||
TorrentInfo,
|
|
||||||
)
|
|
||||||
from app.runtime.events import eventmanager, Event
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.meta.words import WordsMatcher
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.application.chain.data import (
|
from app.application.chain.data import (
|
||||||
get_chain_download_history_port,
|
get_chain_download_history_port,
|
||||||
get_chain_site_port,
|
get_chain_site_port,
|
||||||
@@ -43,30 +17,66 @@ from app.application.configuration import (
|
|||||||
get_chain_runtime_config_snapshot,
|
get_chain_runtime_config_snapshot,
|
||||||
get_configured_system_config,
|
get_configured_system_config,
|
||||||
)
|
)
|
||||||
from app.application.messaging.subscribe import SubscribeInteractionHandler
|
|
||||||
from app.application.messaging.message import MessageTemplateHelper
|
|
||||||
from app.application.mediaserver import MediaServerHelper
|
from app.application.mediaserver import MediaServerHelper
|
||||||
from app.application.subscription.write import add_subscribe, async_add_subscribe
|
from app.application.messaging.message import MessageTemplateHelper
|
||||||
from app.application.subscription.complete import get_subscription_completion_scope
|
from app.application.messaging.subscribe import SubscribeInteractionHandler
|
||||||
from app.application.subscription import priority as _priority
|
from app.application.subscription import priority as _priority
|
||||||
|
from app.application.subscription.complete import get_subscription_completion_scope
|
||||||
|
from app.application.subscription.contract import (
|
||||||
|
build_subscribe_meta as _build_subscribe_meta,
|
||||||
|
)
|
||||||
|
from app.application.subscription.contract import (
|
||||||
|
subscribe_media_key,
|
||||||
|
subscribe_media_keys,
|
||||||
|
)
|
||||||
from app.application.subscription.delete import (
|
from app.application.subscription.delete import (
|
||||||
SubscribeDeletionActor,
|
SubscribeDeletionActor,
|
||||||
get_sync_delete_subscribe_scope,
|
get_sync_delete_subscribe_scope,
|
||||||
)
|
)
|
||||||
from app.application.subscription.contract import (
|
|
||||||
build_subscribe_meta as _build_subscribe_meta,
|
|
||||||
subscribe_media_key,
|
|
||||||
subscribe_media_keys,
|
|
||||||
)
|
|
||||||
from app.application.subscription.query import SubscriptionQueryService
|
from app.application.subscription.query import SubscriptionQueryService
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.application.subscription.write import add_subscribe, async_add_subscribe
|
||||||
from app.application.torrent import TorrentHelper
|
from app.application.torrent import TorrentHelper
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.chain._interaction import InteractionChainMixin
|
||||||
|
from app.chain._music import MusicSubscribeMixin
|
||||||
|
from app.chain.download import DownloadChain
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.mediaserver import MediaServerChain
|
||||||
|
from app.chain.search import SearchChain
|
||||||
|
from app.chain.tmdb import TmdbChain
|
||||||
|
from app.chain.torrents import TorrentsChain
|
||||||
|
from app.domain.context import (
|
||||||
|
Context,
|
||||||
|
MediaInfo,
|
||||||
|
TorrentInfo,
|
||||||
|
)
|
||||||
|
from app.domain.meta.metabase import MetaBase
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.meta.words import WordsMatcher
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.event import SubscribeEpisodesRefreshEventData
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.event import SubscribeCompletionCheckEventData
|
from app.schemas.event import SubscribeCompletionCheckEventData, SubscribeEpisodesRefreshEventData
|
||||||
from app.schemas.types import MUSIC_ENTITY_ALBUM, MediaSource, MediaType, SystemConfigKey, NotificationChannel, MessageType, EventType, ChainEventType, \
|
|
||||||
ContentType
|
|
||||||
from app.schemas.media import normalize_media_source, resolve_media_identity
|
from app.schemas.media import normalize_media_source, resolve_media_identity
|
||||||
|
from app.schemas.mediaserver import NotExistMediaInfo as _SchemaNotExistMediaInfo
|
||||||
|
from app.schemas.message import Message as _SchemaMessage
|
||||||
|
from app.schemas.subscribe import SubscrbieInfo as _SchemaSubscrbieInfo
|
||||||
|
from app.schemas.subscribe import SubscribeDownloadFileInfo as _SchemaSubscribeDownloadFileInfo
|
||||||
|
from app.schemas.subscribe import SubscribeEpisodeInfo as _SchemaSubscribeEpisodeInfo
|
||||||
|
from app.schemas.subscribe import SubscribeLibraryFileInfo as _SchemaSubscribeLibraryFileInfo
|
||||||
|
from app.schemas.types import (
|
||||||
|
MUSIC_ENTITY_ALBUM,
|
||||||
|
ChainEventType,
|
||||||
|
ContentType,
|
||||||
|
EventType,
|
||||||
|
MediaSource,
|
||||||
|
MediaType,
|
||||||
|
MessageType,
|
||||||
|
NotificationChannel,
|
||||||
|
SystemConfigKey,
|
||||||
|
)
|
||||||
|
from app.schemas.workflow import Subscribe as _SchemaSubscribe
|
||||||
|
|
||||||
if hasattr(_SchemaSubscribe, "model_fields"):
|
if hasattr(_SchemaSubscribe, "model_fields"):
|
||||||
Subscribe = _SchemaSubscribe
|
Subscribe = _SchemaSubscribe
|
||||||
@@ -191,6 +201,28 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
电影下载优先级 writer 单独维护。
|
电影下载优先级 writer 单独维护。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _music_media_chain(cls):
|
||||||
|
"""为音乐 mixin 提供可替换的媒体识别构造点。"""
|
||||||
|
from app.chain import _music as _music_mixin
|
||||||
|
return (_music_mixin.MediaChain or MediaChain)()
|
||||||
|
|
||||||
|
def _music_download_chain(self):
|
||||||
|
"""为音乐 mixin 提供可替换的下载构造点。"""
|
||||||
|
from app.chain import _music as _music_mixin
|
||||||
|
return (_music_mixin.DownloadChain or DownloadChain)()
|
||||||
|
|
||||||
|
def _music_search_chain(self):
|
||||||
|
"""为音乐 mixin 提供可替换的搜索构造点。"""
|
||||||
|
from app.chain import _music as _music_mixin
|
||||||
|
return (_music_mixin.SearchChain or SearchChain)()
|
||||||
|
|
||||||
|
def _music_site_keywords(self, mediainfo):
|
||||||
|
return SearchChain.music_site_keywords(mediainfo)
|
||||||
|
|
||||||
|
def _matches_music_resource(self, mediainfo, *texts):
|
||||||
|
return SearchChain.matches_music_resource(mediainfo, *texts)
|
||||||
|
|
||||||
# 交互处理器类注入,供 InteractionChainMixin 的 parse_callback 委托
|
# 交互处理器类注入,供 InteractionChainMixin 的 parse_callback 委托
|
||||||
_interaction_handler_type = SubscribeInteractionHandler
|
_interaction_handler_type = SubscribeInteractionHandler
|
||||||
|
|
||||||
@@ -1179,7 +1211,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
try:
|
try:
|
||||||
# 遍历订阅
|
# 遍历订阅
|
||||||
for index, subscribe in enumerate(subscribes, start=1):
|
for index, subscribe in enumerate(subscribes, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
processed_subscribes.append(subscribe)
|
processed_subscribes.append(subscribe)
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
@@ -1275,7 +1307,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
matched_contexts = []
|
matched_contexts = []
|
||||||
try:
|
try:
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
torrent_meta = context.meta_info
|
torrent_meta = context.meta_info
|
||||||
torrent_info = context.torrent_info
|
torrent_info = context.torrent_info
|
||||||
@@ -1592,11 +1624,11 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
"""预识别待匹配资源,并保留原上下文供后续订阅复用。"""
|
"""预识别待匹配资源,并保留原上下文供后续订阅复用。"""
|
||||||
processed_torrents: Dict[str, List[Context]] = {}
|
processed_torrents: Dict[str, List[Context]] = {}
|
||||||
for domain, contexts in torrents.items():
|
for domain, contexts in torrents.items():
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
processed_torrents[domain] = []
|
processed_torrents[domain] = []
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if context.torrent_info and getattr(context.torrent_info, "category", None) in (
|
if context.torrent_info and getattr(context.torrent_info, "category", None) in (
|
||||||
MediaType.MUSIC,
|
MediaType.MUSIC,
|
||||||
@@ -1699,7 +1731,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
for index, subscribe in enumerate(subscribes, start=1):
|
for index, subscribe in enumerate(subscribes, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(
|
progress_callback(
|
||||||
@@ -1772,13 +1804,13 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
systemconfig = _system_config()
|
systemconfig = _system_config()
|
||||||
wordsmatcher = WordsMatcher()
|
wordsmatcher = WordsMatcher()
|
||||||
for domain, contexts in processed_torrents.items():
|
for domain, contexts in processed_torrents.items():
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if domains and domain not in domains:
|
if domains and domain not in domains:
|
||||||
continue
|
continue
|
||||||
logger.debug(f'开始匹配站点:{domain},共缓存了 {len(contexts)} 个种子...')
|
logger.debug(f'开始匹配站点:{domain},共缓存了 {len(contexts)} 个种子...')
|
||||||
for context in contexts:
|
for context in contexts:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
# 提取信息
|
# 提取信息
|
||||||
_context = copy.copy(context)
|
_context = copy.copy(context)
|
||||||
@@ -2046,7 +2078,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
)
|
)
|
||||||
# 遍历订阅
|
# 遍历订阅
|
||||||
for index, subscribe in enumerate(subscribes, start=1):
|
for index, subscribe in enumerate(subscribes, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
logger.info(f'开始更新订阅元数据:{subscribe.name} ...')
|
logger.info(f'开始更新订阅元数据:{subscribe.name} ...')
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
@@ -2174,7 +2206,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(value=100, text="未配置 Follow 订阅用户,跳过刷新")
|
progress_callback(value=100, text="未配置 Follow 订阅用户,跳过刷新")
|
||||||
return
|
return
|
||||||
logger.info(f'开始刷新follow用户分享订阅 ...')
|
logger.info('开始刷新follow用户分享订阅 ...')
|
||||||
success_count = 0
|
success_count = 0
|
||||||
subscribeoper = get_chain_subscribe_port()
|
subscribeoper = get_chain_subscribe_port()
|
||||||
share_subscribes = MoviePilotServerHelper.get_subscribe_shares() or []
|
share_subscribes = MoviePilotServerHelper.get_subscribe_shares() or []
|
||||||
@@ -2186,7 +2218,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
data={"total": total_num, "finished": 0},
|
data={"total": total_num, "finished": 0},
|
||||||
)
|
)
|
||||||
for index, share_sub in enumerate(share_subscribes, start=1):
|
for index, share_sub in enumerate(share_subscribes, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(
|
progress_callback(
|
||||||
@@ -2276,7 +2308,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
|
|
||||||
:param progress_callback: 定时服务进度更新回调
|
:param progress_callback: 定时服务进度更新回调
|
||||||
"""
|
"""
|
||||||
logger.info(f'开始预缓存订阅日历 ...')
|
logger.info('开始预缓存订阅日历 ...')
|
||||||
subscribes = await get_chain_subscribe_port().async_list()
|
subscribes = await get_chain_subscribe_port().async_list()
|
||||||
total_num = len(subscribes)
|
total_num = len(subscribes)
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
@@ -2286,7 +2318,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
data={"total": total_num, "finished": 0},
|
data={"total": total_num, "finished": 0},
|
||||||
)
|
)
|
||||||
for index, subscribe in enumerate(subscribes, start=1):
|
for index, subscribe in enumerate(subscribes, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(
|
progress_callback(
|
||||||
@@ -2336,7 +2368,7 @@ class SubscribeChain(MusicSubscribeMixin, InteractionChainMixin, ChainBase):
|
|||||||
text=f"订阅日历({index}/{total_num})预缓存完成",
|
text=f"订阅日历({index}/{total_num})预缓存完成",
|
||||||
data={"total": total_num, "finished": index},
|
data={"total": total_num, "finished": index},
|
||||||
)
|
)
|
||||||
logger.info(f'订阅日历预缓存完成')
|
logger.info('订阅日历预缓存完成')
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(value=100, text="订阅日历预缓存完成")
|
progress_callback(value=100, text="订阅日历预缓存完成")
|
||||||
|
|
||||||
|
|||||||
+8
-8
@@ -4,17 +4,17 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import uuid
|
import uuid
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union, Optional
|
from typing import Optional, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
|
||||||
from app.runtime.state import SystemHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.notification import NotificationChannel
|
|
||||||
from app.adapters.network.http import RequestUtils
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.adapters.system.host import SystemUtils
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||||
|
from app.chain import ChainBase
|
||||||
from app.runtime import version as runtime_version
|
from app.runtime import version as runtime_version
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.state import SystemHelper
|
||||||
|
from app.schemas.message import Message
|
||||||
|
from app.schemas.notification import NotificationChannel
|
||||||
|
|
||||||
|
|
||||||
class SystemChain(ChainBase):
|
class SystemChain(ChainBase):
|
||||||
@@ -33,7 +33,7 @@ class SystemChain(ChainBase):
|
|||||||
self.post_message(Message(
|
self.post_message(Message(
|
||||||
channel=channel,
|
channel=channel,
|
||||||
source=source,
|
source=source,
|
||||||
title=f"缓存清理完成!",
|
title="缓存清理完成!",
|
||||||
userid=userid,
|
userid=userid,
|
||||||
save_history=False))
|
save_history=False))
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1,11 +1,11 @@
|
|||||||
import random
|
import random
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
|
||||||
from app.schemas.tmdb import TmdbSeason as _SchemaTmdbSeason
|
|
||||||
from app.schemas.tmdb import TmdbEpisode as _SchemaTmdbEpisode
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.domain.context import MediaInfo
|
from app.domain.context import MediaInfo
|
||||||
|
from app.schemas.context import MediaPerson as _SchemaMediaPerson
|
||||||
|
from app.schemas.tmdb import TmdbEpisode as _SchemaTmdbEpisode
|
||||||
|
from app.schemas.tmdb import TmdbSeason as _SchemaTmdbSeason
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+20
-22
@@ -1,27 +1,25 @@
|
|||||||
import copy
|
import copy
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
from typing import Callable, Dict, List, Union, Optional
|
from typing import Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import TorrentInfo, Context, MediaInfo
|
|
||||||
from app.domain.context import MusicInfo
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.metainfo import MetaInfo
|
|
||||||
from app.application.chain.data import get_chain_site_port
|
from app.application.chain.data import get_chain_site_port
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.rss import RssHelper
|
from app.application.rss import RssHelper
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
from app.application.torrent import TorrentHelper
|
from app.application.torrent import TorrentHelper
|
||||||
from app.runtime.log import logger
|
from app.chain import ChainBase
|
||||||
from app.schemas.message import Message
|
from app.chain.media import MediaChain
|
||||||
from app.schemas.types import SystemConfigKey, NotificationChannel, MessageType, MediaType
|
|
||||||
from app.schemas.media import resolve_media_identity
|
|
||||||
from app.domain import site as site_rules
|
from app.domain import site as site_rules
|
||||||
|
from app.domain.context import Context, MediaInfo, MusicInfo, TorrentInfo
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.foundation import text as text_tools
|
from app.foundation import text as text_tools
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.media import resolve_media_identity
|
||||||
|
from app.schemas.message import Message
|
||||||
|
from app.schemas.types import MediaType, MessageType, NotificationChannel, SystemConfigKey
|
||||||
|
|
||||||
|
|
||||||
class TorrentsChain(ChainBase):
|
class TorrentsChain(ChainBase):
|
||||||
@@ -50,13 +48,13 @@ class TorrentsChain(ChainBase):
|
|||||||
"""
|
"""
|
||||||
self.post_message(Message(
|
self.post_message(Message(
|
||||||
channel=channel,
|
channel=channel,
|
||||||
title=f"开始刷新种子 ...",
|
title="开始刷新种子 ...",
|
||||||
userid=userid,
|
userid=userid,
|
||||||
save_history=False))
|
save_history=False))
|
||||||
self.refresh()
|
self.refresh()
|
||||||
self.post_message(Message(
|
self.post_message(Message(
|
||||||
channel=channel,
|
channel=channel,
|
||||||
title=f"种子刷新完成!",
|
title="种子刷新完成!",
|
||||||
userid=userid,
|
userid=userid,
|
||||||
save_history=False))
|
save_history=False))
|
||||||
|
|
||||||
@@ -362,23 +360,23 @@ class TorrentsChain(ChainBase):
|
|||||||
"""
|
"""
|
||||||
清理种子缓存数据,包含音乐独立缓存
|
清理种子缓存数据,包含音乐独立缓存
|
||||||
"""
|
"""
|
||||||
logger.info(f'开始清理种子缓存数据 ...')
|
logger.info('开始清理种子缓存数据 ...')
|
||||||
self.remove_cache(self._spider_file)
|
self.remove_cache(self._spider_file)
|
||||||
self.remove_cache(self._rss_file)
|
self.remove_cache(self._rss_file)
|
||||||
self.remove_cache(self._music_spider_file)
|
self.remove_cache(self._music_spider_file)
|
||||||
self.remove_cache(self._music_rss_file)
|
self.remove_cache(self._music_rss_file)
|
||||||
logger.info(f'种子缓存数据清理完成')
|
logger.info('种子缓存数据清理完成')
|
||||||
|
|
||||||
async def async_clear_torrents(self):
|
async def async_clear_torrents(self):
|
||||||
"""
|
"""
|
||||||
异步清理种子缓存数据,包含音乐独立缓存
|
异步清理种子缓存数据,包含音乐独立缓存
|
||||||
"""
|
"""
|
||||||
logger.info(f'开始异步清理种子缓存数据 ...')
|
logger.info('开始异步清理种子缓存数据 ...')
|
||||||
await self.async_remove_cache(self._spider_file)
|
await self.async_remove_cache(self._spider_file)
|
||||||
await self.async_remove_cache(self._rss_file)
|
await self.async_remove_cache(self._rss_file)
|
||||||
await self.async_remove_cache(self._music_spider_file)
|
await self.async_remove_cache(self._music_spider_file)
|
||||||
await self.async_remove_cache(self._music_rss_file)
|
await self.async_remove_cache(self._music_rss_file)
|
||||||
logger.info(f'异步种子缓存数据清理完成')
|
logger.info('异步种子缓存数据清理完成')
|
||||||
|
|
||||||
def browse(self, domain: str, keyword: Optional[str] = None, cat: Optional[str] = None,
|
def browse(self, domain: str, keyword: Optional[str] = None, cat: Optional[str] = None,
|
||||||
page: Optional[int] = 0,
|
page: Optional[int] = 0,
|
||||||
@@ -585,7 +583,7 @@ class TorrentsChain(ChainBase):
|
|||||||
return domain
|
return domain
|
||||||
logger.info(f'{indexer.get("name")} 有 {len(torrents) + len(music_torrents)} 个新种子')
|
logger.info(f'{indexer.get("name")} 有 {len(torrents) + len(music_torrents)} 个新种子')
|
||||||
for torrent in torrents + music_torrents:
|
for torrent in torrents + music_torrents:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if not torrent.enclosure:
|
if not torrent.enclosure:
|
||||||
logger.warning(f"缺少种子链接,忽略处理: {torrent.title}")
|
logger.warning(f"缺少种子链接,忽略处理: {torrent.title}")
|
||||||
@@ -692,7 +690,7 @@ class TorrentsChain(ChainBase):
|
|||||||
)
|
)
|
||||||
# 遍历站点缓存资源
|
# 遍历站点缓存资源
|
||||||
for index, indexer in enumerate(indexers, start=1):
|
for index, indexer in enumerate(indexers, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
progress_callback(
|
progress_callback(
|
||||||
|
|||||||
+79
-51
@@ -10,55 +10,38 @@ from concurrent.futures import CancelledError as FutureCancelledError
|
|||||||
from concurrent.futures import Future
|
from concurrent.futures import Future
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Tuple, Union, Dict, Callable, Any
|
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain.media import MediaChain
|
|
||||||
from app.chain.storage import StorageChain
|
|
||||||
from app.chain.tmdb import TmdbChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import MediaInfo, MusicInfo, TorrentInfo
|
|
||||||
from app.domain.meta.metabase import MetaBase
|
|
||||||
from app.domain.meta.metamusic import MetaMusic
|
|
||||||
from app.domain.metainfo import MetaInfoPath
|
|
||||||
from app.application.chain.data import (
|
from app.application.chain.data import (
|
||||||
get_chain_download_history_port,
|
get_chain_download_history_port,
|
||||||
get_chain_transfer_history_port,
|
get_chain_transfer_history_port,
|
||||||
get_chain_transfer_pending_port,
|
get_chain_transfer_pending_port,
|
||||||
)
|
)
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.chain.media import MediaChain
|
||||||
|
from app.chain.storage import StorageChain
|
||||||
|
from app.chain.tmdb import TmdbChain
|
||||||
|
from app.domain.context import MediaInfo, MusicInfo, TorrentInfo
|
||||||
|
from app.domain.meta.metabase import MetaBase
|
||||||
|
from app.domain.meta.metamusic import MetaMusic
|
||||||
|
from app.domain.metainfo import MetaInfoPath
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
|
||||||
DownloadHistory = Any
|
DownloadHistory = Any
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.directory import DirectoryHelper
|
from app.application.directory import DirectoryHelper
|
||||||
from app.application.formatting import FormatParser
|
from app.application.formatting import FormatParser
|
||||||
from app.runtime.progress import ProgressHelper
|
from app.application.history import (
|
||||||
from app.application.history import (add_transfer_fail, add_transfer_success,
|
add_transfer_fail,
|
||||||
clear_transfer_failures, describe_history_gate,
|
add_transfer_success,
|
||||||
evaluate_history_gate, is_skip_action,
|
clear_transfer_failures,
|
||||||
record_transfer_failure)
|
describe_history_gate,
|
||||||
from app.application.outbox import TRANSFER_COMPLETED_TOPIC, TRANSFER_FAILED_TOPIC
|
evaluate_history_gate,
|
||||||
from app.runtime.log import logger
|
is_skip_action,
|
||||||
from app.schemas.event import StorageOperSelectionEventData
|
record_transfer_failure,
|
||||||
from app.schemas.transfer import TransferInfo
|
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.transfer import EpisodeFormat
|
|
||||||
from app.schemas.workflow import FileItem
|
|
||||||
from app.schemas.system import TransferDirectoryConf
|
|
||||||
from app.schemas.transfer import TransferJob
|
|
||||||
from app.schemas.tmdb import TmdbEpisode
|
|
||||||
from app.schemas.exception import OperationInterrupted
|
|
||||||
from app.schemas.types import (
|
|
||||||
TorrentStatus,
|
|
||||||
EventType,
|
|
||||||
MediaType,
|
|
||||||
ProgressKey,
|
|
||||||
MessageType,
|
|
||||||
NotificationChannel,
|
|
||||||
SystemConfigKey,
|
|
||||||
ChainEventType,
|
|
||||||
ContentType,
|
|
||||||
MediaSource,
|
|
||||||
)
|
)
|
||||||
from app.runtime.reload import ConfigReloadMixin
|
from app.application.outbox import TRANSFER_COMPLETED_TOPIC, TRANSFER_FAILED_TOPIC
|
||||||
from app.application.transfer import (
|
from app.application.transfer import (
|
||||||
FailedRetryScheduler,
|
FailedRetryScheduler,
|
||||||
JobManager,
|
JobManager,
|
||||||
@@ -70,13 +53,40 @@ from app.application.transfer import (
|
|||||||
build_transfer_failure_group_key,
|
build_transfer_failure_group_key,
|
||||||
job_lock,
|
job_lock,
|
||||||
)
|
)
|
||||||
from app.chain._transfer import (EpisodeFormatMixin, FailedRetryMixin,
|
from app.chain._transfer import (
|
||||||
FileFilterMixin, FileKeyMixin,
|
EpisodeFormatMixin,
|
||||||
HistoryMatchMixin, ManualHistoryMixin,
|
FailedRetryMixin,
|
||||||
ScrapeBatchMixin)
|
FileFilterMixin,
|
||||||
from app.schemas.media import resolve_media_identity
|
FileKeyMixin,
|
||||||
from app.foundation.singleton import Singleton
|
HistoryMatchMixin,
|
||||||
|
ManualHistoryMixin,
|
||||||
|
ScrapeBatchMixin,
|
||||||
|
)
|
||||||
from app.domain import episode as episode_rules
|
from app.domain import episode as episode_rules
|
||||||
|
from app.foundation.singleton import Singleton
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.progress import ProgressHelper
|
||||||
|
from app.runtime.reload import ConfigReloadMixin
|
||||||
|
from app.schemas.event import StorageOperSelectionEventData
|
||||||
|
from app.schemas.exception import OperationInterrupted
|
||||||
|
from app.schemas.media import resolve_media_identity
|
||||||
|
from app.schemas.message import Message
|
||||||
|
from app.schemas.system import TransferDirectoryConf
|
||||||
|
from app.schemas.tmdb import TmdbEpisode
|
||||||
|
from app.schemas.transfer import EpisodeFormat, TransferInfo, TransferJob
|
||||||
|
from app.schemas.types import (
|
||||||
|
ChainEventType,
|
||||||
|
ContentType,
|
||||||
|
EventType,
|
||||||
|
MediaSource,
|
||||||
|
MediaType,
|
||||||
|
MessageType,
|
||||||
|
NotificationChannel,
|
||||||
|
ProgressKey,
|
||||||
|
SystemConfigKey,
|
||||||
|
TorrentStatus,
|
||||||
|
)
|
||||||
|
from app.schemas.workflow import FileItem
|
||||||
|
|
||||||
# 下载器锁
|
# 下载器锁
|
||||||
downloader_lock = threading.Lock()
|
downloader_lock = threading.Lock()
|
||||||
@@ -90,6 +100,24 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
文件整理处理链
|
文件整理处理链
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_media_chain(cls):
|
||||||
|
"""为整理 mixin 提供可替换的媒体识别构造点。"""
|
||||||
|
from app.chain import _transfer as _transfer_mixin
|
||||||
|
return (_transfer_mixin.MediaChain or MediaChain)()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_storage_chain(cls):
|
||||||
|
"""为整理 mixin 提供可替换的存储构造点。"""
|
||||||
|
from app.chain import _transfer as _transfer_mixin
|
||||||
|
return (_transfer_mixin.StorageChain or StorageChain)()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _transfer_subscribe_chain(cls):
|
||||||
|
"""为整理 mixin 提供可替换的订阅构造点。"""
|
||||||
|
from app.chain.subscribe import SubscribeChain as _SubscribeChain
|
||||||
|
return _SubscribeChain()
|
||||||
|
|
||||||
# worker 在构造期启动;若中途失败,单例仍需先发布给 lifespan 清理入口。
|
# worker 在构造期启动;若中途失败,单例仍需先发布给 lifespan 清理入口。
|
||||||
_retain_failed_singleton = True
|
_retain_failed_singleton = True
|
||||||
|
|
||||||
@@ -1148,7 +1176,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
|
|
||||||
:param stop_event: 当前 worker 代专属停止信号,热更新后不会被重新清除
|
:param stop_event: 当前 worker 代专属停止信号,热更新后不会被重新清除
|
||||||
"""
|
"""
|
||||||
while not global_vars.is_system_stopped and not stop_event.is_set():
|
while not runtime_stop_state.is_system_stopped and not stop_event.is_set():
|
||||||
try:
|
try:
|
||||||
item: TransferQueue = self._queue.get(
|
item: TransferQueue = self._queue.get(
|
||||||
block=True, timeout=self._transfer_interval
|
block=True, timeout=self._transfer_interval
|
||||||
@@ -1156,10 +1184,10 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
if item is self._QUEUE_STOP_SENTINEL:
|
if item is self._QUEUE_STOP_SENTINEL:
|
||||||
self._queue.task_done()
|
self._queue.task_done()
|
||||||
self.__settle_transfer_progress_if_idle()
|
self.__settle_transfer_progress_if_idle()
|
||||||
if stop_event.is_set() or global_vars.is_system_stopped:
|
if stop_event.is_set() or runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
continue
|
continue
|
||||||
if stop_event.is_set() or global_vars.is_system_stopped:
|
if stop_event.is_set() or runtime_stop_state.is_system_stopped:
|
||||||
# 关闭信号与 queue.get 竞态时,把尚未处理的任务放回队列;其
|
# 关闭信号与 queue.get 竞态时,把尚未处理的任务放回队列;其
|
||||||
# TransferPending 登记保持不变,供同进程重启 worker 或下次启动回放。
|
# TransferPending 登记保持不变,供同进程重启 worker 或下次启动回放。
|
||||||
self._queue.put(item)
|
self._queue.put(item)
|
||||||
@@ -1606,7 +1634,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
try:
|
try:
|
||||||
total_num = len(torrents)
|
total_num = len(torrents)
|
||||||
for index, torrent in enumerate(torrents, start=1):
|
for index, torrent in enumerate(torrents, start=1):
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if progress_callback:
|
if progress_callback:
|
||||||
torrent_name = (
|
torrent_name = (
|
||||||
@@ -1732,7 +1760,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
若 `predicate` 为 `None`,则默认保留所有项
|
若 `predicate` 为 `None`,则默认保留所有项
|
||||||
:param verify_file_exists: 验证目录或文件是否存在,默认值为 `True`
|
:param verify_file_exists: 验证目录或文件是否存在,默认值为 `True`
|
||||||
"""
|
"""
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
raise OperationInterrupted()
|
raise OperationInterrupted()
|
||||||
|
|
||||||
storagechain = StorageChain()
|
storagechain = StorageChain()
|
||||||
@@ -2536,7 +2564,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
skipped_torrents = set()
|
skipped_torrents = set()
|
||||||
try:
|
try:
|
||||||
for file_item, bluray_dir in file_items:
|
for file_item, bluray_dir in file_items:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
raise OperationInterrupted()
|
raise OperationInterrupted()
|
||||||
if continue_callback and not continue_callback():
|
if continue_callback and not continue_callback():
|
||||||
raise OperationInterrupted()
|
raise OperationInterrupted()
|
||||||
@@ -2762,7 +2790,7 @@ class TransferChain(FileFilterMixin, ScrapeBatchMixin, EpisodeFormatMixin, Histo
|
|||||||
progress.update(value=0, text=__process_msg)
|
progress.update(value=0, text=__process_msg)
|
||||||
try:
|
try:
|
||||||
for transfer_task in transfer_tasks:
|
for transfer_task in transfer_tasks:
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
break
|
break
|
||||||
if continue_callback and not continue_callback():
|
if continue_callback and not continue_callback():
|
||||||
break
|
break
|
||||||
|
|||||||
+5
-6
@@ -2,14 +2,13 @@ import secrets
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, Literal, Optional, Tuple, Union
|
from typing import Any, Literal, Optional, Tuple, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.application.security.token import get_password_hash, verify_password
|
|
||||||
from app.application.chain.data import get_chain_user_port
|
from app.application.chain.data import get_chain_user_port
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.event import AuthCredentials
|
|
||||||
from app.schemas.event import AuthInterceptCredentials
|
|
||||||
from app.schemas.types import ChainEventType
|
|
||||||
from app.application.security.otp import OtpUtils
|
from app.application.security.otp import OtpUtils
|
||||||
|
from app.application.security.token import get_password_hash, verify_password
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.event import AuthCredentials, AuthInterceptCredentials
|
||||||
|
from app.schemas.types import ChainEventType
|
||||||
|
|
||||||
PASSWORD_INVALID_CREDENTIALS_MESSAGE = "用户名、密码或验证码错误"
|
PASSWORD_INVALID_CREDENTIALS_MESSAGE = "用户名、密码或验证码错误"
|
||||||
User = Any
|
User = Any
|
||||||
|
|||||||
+8
-12
@@ -13,19 +13,15 @@ from typing import Any, Callable, List, Optional, Tuple
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import Event, eventmanager
|
|
||||||
from app.application.workflow import get_workflow_manager
|
|
||||||
from app.application.chain.data import get_chain_workflow_port
|
from app.application.chain.data import get_chain_workflow_port
|
||||||
|
from app.application.workflow import get_workflow_manager
|
||||||
|
from app.chain import ChainBase
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
from app.runtime.execution import OwnedThreadPoolExecutor
|
from app.runtime.execution import OwnedThreadPoolExecutor
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.workflow import ActionContext
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.workflow import ActionFlow
|
|
||||||
from app.schemas.workflow import Action
|
|
||||||
from app.schemas.workflow import ActionExecution
|
|
||||||
from app.schemas.workflow import ActionResult
|
|
||||||
from app.schemas.types import EventType
|
from app.schemas.types import EventType
|
||||||
|
from app.schemas.workflow import Action, ActionContext, ActionExecution, ActionFlow, ActionResult
|
||||||
|
|
||||||
ARTIFACT_FIELDS = {"torrents", "medias", "fileitems", "downloads", "sites", "subscribes"}
|
ARTIFACT_FIELDS = {"torrents", "medias", "fileitems", "downloads", "sites", "subscribes"}
|
||||||
DEFAULT_WORKFLOW_MAX_WORKERS = 4
|
DEFAULT_WORKFLOW_MAX_WORKERS = 4
|
||||||
@@ -113,7 +109,7 @@ class WorkflowCancelToken:
|
|||||||
"""
|
"""
|
||||||
return bool(
|
return bool(
|
||||||
(self.stop_event and self.stop_event.is_set())
|
(self.stop_event and self.stop_event.is_set())
|
||||||
or global_vars.is_workflow_stopped(self.workflow_id)
|
or runtime_stop_state.is_workflow_stopped(self.workflow_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -236,7 +232,7 @@ class WorkflowExecutor:
|
|||||||
self._registered_execution = callable(register)
|
self._registered_execution = callable(register)
|
||||||
self._admission_state = "admitted"
|
self._admission_state = "admitted"
|
||||||
# 只有获得执行准入后才能清除历史单工作流停止标记。
|
# 只有获得执行准入后才能清除历史单工作流停止标记。
|
||||||
global_vars.workflow_resume(self.workflow.id)
|
runtime_stop_state.resume_workflow(self.workflow.id)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def request_stop(self) -> None:
|
def request_stop(self) -> None:
|
||||||
@@ -285,7 +281,7 @@ class WorkflowExecutor:
|
|||||||
"""判断本次执行或全局工作流是否已收到停止请求。"""
|
"""判断本次执行或全局工作流是否已收到停止请求。"""
|
||||||
return bool(
|
return bool(
|
||||||
self._stop_event.is_set()
|
self._stop_event.is_set()
|
||||||
or global_vars.is_workflow_stopped(self.workflow.id)
|
or runtime_stop_state.is_workflow_stopped(self.workflow.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_workflow_max_workers(self) -> int:
|
def get_workflow_max_workers(self) -> int:
|
||||||
|
|||||||
+7
-6
@@ -26,18 +26,18 @@ def _prepare_direct_execution_import_path() -> None:
|
|||||||
|
|
||||||
_prepare_direct_execution_import_path()
|
_prepare_direct_execution_import_path()
|
||||||
|
|
||||||
import setproctitle
|
|
||||||
import signal
|
import signal
|
||||||
import threading
|
import threading
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
import setproctitle
|
||||||
import uvicorn as uvicorn
|
import uvicorn as uvicorn
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from uvicorn import Config
|
from uvicorn import Config
|
||||||
|
|
||||||
from app.adapters.system.stdio import configure_rotating_stdio
|
|
||||||
from app.adapters.system.host import SystemUtils
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.adapters.system.stdio import configure_rotating_stdio
|
||||||
|
|
||||||
stdio_log_file = os.getenv("MOVIEPILOT_STDIO_LOG_FILE")
|
stdio_log_file = os.getenv("MOVIEPILOT_STDIO_LOG_FILE")
|
||||||
if stdio_log_file:
|
if stdio_log_file:
|
||||||
@@ -55,8 +55,9 @@ elif SystemUtils.is_frozen():
|
|||||||
sys.stderr = open(os.devnull, 'w')
|
sys.stderr = open(os.devnull, 'w')
|
||||||
|
|
||||||
from app.factory import app
|
from app.factory import app
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.topology import (
|
from app.runtime.topology import (
|
||||||
@@ -71,7 +72,7 @@ class MoviePilotServer(uvicorn.Server):
|
|||||||
"""在 Uvicorn 开始优雅退出前发布应用协作停止标志"""
|
"""在 Uvicorn 开始优雅退出前发布应用协作停止标志"""
|
||||||
|
|
||||||
def handle_exit(self, sig, frame) -> None:
|
def handle_exit(self, sig, frame) -> None:
|
||||||
global_vars.stop_system()
|
getattr(global_vars, "stop_system")()
|
||||||
super().handle_exit(sig, frame)
|
super().handle_exit(sig, frame)
|
||||||
|
|
||||||
|
|
||||||
@@ -92,7 +93,7 @@ def create_server() -> MoviePilotServer:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
# 数据库准备阶段收到的信号早于 Server 物化,创建后必须继承既有停止意图。
|
# 数据库准备阶段收到的信号早于 Server 物化,创建后必须继承既有停止意图。
|
||||||
if global_vars.is_system_stopped:
|
if runtime_stop_state.is_system_stopped:
|
||||||
server.should_exit = True
|
server.should_exit = True
|
||||||
return server
|
return server
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ def run_api_server() -> None:
|
|||||||
|
|
||||||
def request_shutdown() -> None:
|
def request_shutdown() -> None:
|
||||||
"""发布协作停止标志并请求 Uvicorn 退出"""
|
"""发布协作停止标志并请求 Uvicorn 退出"""
|
||||||
global_vars.stop_system()
|
getattr(global_vars, "stop_system")()
|
||||||
if Server is not None:
|
if Server is not None:
|
||||||
Server.should_exit = True
|
Server.should_exit = True
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from pathlib import Path, PurePosixPath
|
from pathlib import Path, PurePosixPath
|
||||||
from typing import Optional, List, Dict, Tuple, Callable, Union
|
from typing import Callable, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
from app.application.storage import StorageHelper
|
||||||
|
from app.foundation.crypto import HashUtils
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.progress import ProgressHelper
|
||||||
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.system import StorageConf as _SchemaStorageConf
|
from app.schemas.system import StorageConf as _SchemaStorageConf
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.progress import ProgressHelper
|
|
||||||
from app.application.storage import StorageHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.exception import StorageQueryError
|
|
||||||
from app.foundation.crypto import HashUtils
|
|
||||||
|
|
||||||
|
|
||||||
def transfer_process(path: str) -> Callable[[int | float], None]:
|
def transfer_process(path: str) -> Callable[[int | float], None]:
|
||||||
|
|||||||
@@ -8,19 +8,19 @@ from typing import List, Optional, Tuple, Union
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
from app.adapters.network.http import RequestUtils
|
||||||
|
from app.foundation import temporal as time_tools
|
||||||
|
from app.foundation.singleton import WeakSingleton
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.exception import StorageQueryError
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.adapters.network.http import RequestUtils
|
|
||||||
from app.foundation.singleton import WeakSingleton
|
|
||||||
from app.foundation import temporal as time_tools
|
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
@@ -645,7 +645,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
|||||||
uploaded_size = 0
|
uploaded_size = 0
|
||||||
with open(local_path, "rb") as f:
|
with open(local_path, "rb") as f:
|
||||||
for part_info in part_info_list:
|
for part_info in part_info_list:
|
||||||
if global_vars.is_transfer_stopped(local_path.as_posix()):
|
if runtime_stop_state.consume_transfer_stop(local_path.as_posix()):
|
||||||
logger.info(f"【阿里云盘】{target_name} 上传已取消!")
|
logger.info(f"【阿里云盘】{target_name} 上传已取消!")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -780,7 +780,7 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
|
|||||||
downloaded_size = 0
|
downloaded_size = 0
|
||||||
with open(local_path, "wb") as f:
|
with open(local_path, "wb") as f:
|
||||||
for chunk in r.iter_content(chunk_size=self.chunk_size):
|
for chunk in r.iter_content(chunk_size=self.chunk_size):
|
||||||
if global_vars.is_transfer_stopped(fileitem.path):
|
if runtime_stop_state.consume_transfer_stop(fileitem.path):
|
||||||
logger.info(f"【阿里云盘】{fileitem.path} 下载已取消!")
|
logger.info(f"【阿里云盘】{fileitem.path} 下载已取消!")
|
||||||
return None
|
return None
|
||||||
if chunk:
|
if chunk:
|
||||||
|
|||||||
@@ -3,23 +3,22 @@ import json
|
|||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
|
||||||
from app.runtime.cache import cached
|
from app.runtime.cache import cached
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
from app.runtime.config import global_vars
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
|
||||||
from app.schemas.exception import OperationInterrupted, StorageQueryError
|
|
||||||
from app.schemas.types import StorageSchema
|
|
||||||
from app.adapters.network.http import RequestUtils
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.foundation.singleton import WeakSingleton
|
from app.foundation.singleton import WeakSingleton
|
||||||
from app.foundation.url import UrlUtils
|
from app.foundation.url import UrlUtils
|
||||||
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.schemas.exception import OperationInterrupted, StorageQueryError
|
||||||
|
from app.schemas.types import StorageSchema
|
||||||
|
|
||||||
# OpenList/AList 在 per_page<=0 时会退回后端默认 200,显式指定最大页大小避免大目录被截断。
|
# OpenList/AList 在 per_page<=0 时会退回后端默认 200,显式指定最大页大小避免大目录被截断。
|
||||||
OPENLIST_MAX_LIST_PAGE_SIZE = 500
|
OPENLIST_MAX_LIST_PAGE_SIZE = 500
|
||||||
@@ -703,7 +702,7 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
|||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
with open(local_path, "wb") as f:
|
with open(local_path, "wb") as f:
|
||||||
for chunk in r.iter_content(chunk_size=8192):
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
if global_vars.is_transfer_stopped(fileitem.path):
|
if runtime_stop_state.consume_transfer_stop(fileitem.path):
|
||||||
logger.info(f"【OpenList】{fileitem.path} 下载已取消!")
|
logger.info(f"【OpenList】{fileitem.path} 下载已取消!")
|
||||||
return None
|
return None
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
@@ -760,7 +759,7 @@ class Alist(StorageBase, metaclass=WeakSingleton):
|
|||||||
return self.file_size
|
return self.file_size
|
||||||
|
|
||||||
def read(self, size=-1):
|
def read(self, size=-1):
|
||||||
if global_vars.is_transfer_stopped(path.as_posix()):
|
if runtime_stop_state.consume_transfer_stop(path.as_posix()):
|
||||||
logger.info(f"【OpenList】{path} 上传已取消!")
|
logger.info(f"【OpenList】{path} 上传已取消!")
|
||||||
raise OperationInterrupted(f"Upload cancelled: {path}")
|
raise OperationInterrupted(f"Upload cancelled: {path}")
|
||||||
chunk = self.file.read(size)
|
chunk = self.file.read(size)
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.application.directory import DirectoryHelper
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.adapters.system.fsproxy import fsproxy
|
from app.adapters.system.fsproxy import fsproxy
|
||||||
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.application.directory import DirectoryHelper
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.exception import StorageQueryError
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.adapters.system.host import SystemUtils
|
|
||||||
|
|
||||||
|
|
||||||
class LocalStorage(StorageBase):
|
class LocalStorage(StorageBase):
|
||||||
@@ -301,7 +301,7 @@ class LocalStorage(StorageBase):
|
|||||||
copied = fsproxy.copy(
|
copied = fsproxy.copy(
|
||||||
src, partial,
|
src, partial,
|
||||||
progress_cb=progress_callback,
|
progress_cb=progress_callback,
|
||||||
cancel_cb=lambda: global_vars.is_transfer_stopped(src.as_posix()),
|
cancel_cb=lambda: runtime_stop_state.consume_transfer_stop(src.as_posix()),
|
||||||
chunk_size=self.chunk_size,
|
chunk_size=self.chunk_size,
|
||||||
)
|
)
|
||||||
if not copied:
|
if not copied:
|
||||||
@@ -352,7 +352,7 @@ class LocalStorage(StorageBase):
|
|||||||
try:
|
try:
|
||||||
with open(src, "rb") as fsrc, open(dest, "wb") as fdst:
|
with open(src, "rb") as fsrc, open(dest, "wb") as fdst:
|
||||||
while True:
|
while True:
|
||||||
if global_vars.is_transfer_stopped(src.as_posix()):
|
if runtime_stop_state.consume_transfer_stop(src.as_posix()):
|
||||||
logger.info(f"【本地】{src} 复制已取消!")
|
logger.info(f"【本地】{src} 复制已取消!")
|
||||||
return False
|
return False
|
||||||
buf = fsrc.read(self.chunk_size)
|
buf = fsrc.read(self.chunk_size)
|
||||||
|
|||||||
@@ -4,19 +4,19 @@ import threading
|
|||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, List, Union
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.foundation import temporal as time_tools
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.exception import StorageQueryError
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.foundation import temporal as time_tools
|
|
||||||
from app.adapters.system.host import SystemUtils
|
|
||||||
|
|
||||||
_MAX_FOLDER_LOCKS = 4096
|
_MAX_FOLDER_LOCKS = 4096
|
||||||
_folder_locks: OrderedDict[str, threading.Lock] = OrderedDict()
|
_folder_locks: OrderedDict[str, threading.Lock] = OrderedDict()
|
||||||
|
|||||||
@@ -7,22 +7,22 @@ from typing import List, Optional, Union
|
|||||||
import smbclient
|
import smbclient
|
||||||
from smbclient import ClientConfig, register_session, reset_connection_cache
|
from smbclient import ClientConfig, register_session, reset_connection_cache
|
||||||
from smbprotocol.exceptions import (
|
from smbprotocol.exceptions import (
|
||||||
|
SMBAuthenticationError,
|
||||||
SMBException,
|
SMBException,
|
||||||
SMBResponseException,
|
SMBResponseException,
|
||||||
SMBAuthenticationError,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
from app.foundation.singleton import WeakSingleton
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.schemas.exception import StorageQueryError
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.foundation.singleton import WeakSingleton
|
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
|
|
||||||
@@ -572,7 +572,7 @@ class SMB(StorageBase, metaclass=WeakSingleton):
|
|||||||
with open(local_path, "wb") as dst_file:
|
with open(local_path, "wb") as dst_file:
|
||||||
downloaded_size = 0
|
downloaded_size = 0
|
||||||
while True:
|
while True:
|
||||||
if global_vars.is_transfer_stopped(fileitem.path):
|
if runtime_stop_state.consume_transfer_stop(fileitem.path):
|
||||||
logger.info(f"【SMB】{fileitem.path} 下载已取消!")
|
logger.info(f"【SMB】{fileitem.path} 下载已取消!")
|
||||||
return None
|
return None
|
||||||
chunk = src_file.read(self.chunk_size)
|
chunk = src_file.read(self.chunk_size)
|
||||||
@@ -622,7 +622,7 @@ class SMB(StorageBase, metaclass=WeakSingleton):
|
|||||||
with smbclient.open_file(smb_path, mode="wb") as dst_file:
|
with smbclient.open_file(smb_path, mode="wb") as dst_file:
|
||||||
uploaded_size = 0
|
uploaded_size = 0
|
||||||
while True:
|
while True:
|
||||||
if global_vars.is_transfer_stopped(path.as_posix()):
|
if runtime_stop_state.consume_transfer_stop(path.as_posix()):
|
||||||
logger.info(f"【SMB】{path} 上传已取消!")
|
logger.info(f"【SMB】{path} 上传已取消!")
|
||||||
return None
|
return None
|
||||||
chunk = src_file.read(self.chunk_size)
|
chunk = src_file.read(self.chunk_size)
|
||||||
|
|||||||
@@ -1,31 +1,30 @@
|
|||||||
import base64
|
import base64
|
||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
|
from hashlib import sha256
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from typing import List, Optional, Tuple, Union
|
from typing import List, Optional, Tuple, Union
|
||||||
from hashlib import sha256
|
|
||||||
|
|
||||||
import oss2
|
|
||||||
import httpx
|
import httpx
|
||||||
|
import oss2
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
from oss2 import SizedFileAdapter, determine_part_size
|
from oss2 import SizedFileAdapter, determine_part_size
|
||||||
from oss2.models import PartInfo
|
from oss2.models import PartInfo
|
||||||
from cryptography.hazmat.primitives import hashes
|
|
||||||
|
|
||||||
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
from app.schemas.file import StorageUsage as _SchemaStorageUsage
|
||||||
from app.schemas.workflow import FileItem as _SchemaFileItem
|
from app.schemas.workflow import FileItem as _SchemaFileItem
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.log import logger
|
from app.foundation import size as size_tools
|
||||||
|
from app.foundation.singleton import WeakSingleton
|
||||||
from app.modules.filemanager.storages import StorageBase, transfer_process
|
from app.modules.filemanager.storages import StorageBase, transfer_process
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.rate import QpsRateLimiter, RateStats
|
||||||
from app.schemas.exception import StorageQueryError
|
from app.schemas.exception import StorageQueryError
|
||||||
from app.schemas.types import StorageSchema
|
from app.schemas.types import StorageSchema
|
||||||
from app.foundation.singleton import WeakSingleton
|
|
||||||
from app.foundation import size as size_tools
|
|
||||||
from app.runtime.rate import QpsRateLimiter, RateStats
|
|
||||||
|
|
||||||
|
|
||||||
lock = Lock()
|
lock = Lock()
|
||||||
|
|
||||||
@@ -778,7 +777,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
|||||||
part_number = 1
|
part_number = 1
|
||||||
offset = 0
|
offset = 0
|
||||||
while offset < file_size:
|
while offset < file_size:
|
||||||
if global_vars.is_transfer_stopped(local_path.as_posix()):
|
if runtime_stop_state.consume_transfer_stop(local_path.as_posix()):
|
||||||
logger.info(f"【115】{local_path} 上传已取消!")
|
logger.info(f"【115】{local_path} 上传已取消!")
|
||||||
return None
|
return None
|
||||||
num_to_upload = min(part_size, file_size - offset)
|
num_to_upload = min(part_size, file_size - offset)
|
||||||
@@ -929,7 +928,7 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
|
|||||||
|
|
||||||
with open(local_path, "wb") as f:
|
with open(local_path, "wb") as f:
|
||||||
for chunk in r.iter_bytes(chunk_size=self.chunk_size):
|
for chunk in r.iter_bytes(chunk_size=self.chunk_size):
|
||||||
if global_vars.is_transfer_stopped(fileitem.path):
|
if runtime_stop_state.consume_transfer_stop(fileitem.path):
|
||||||
logger.info(f"【115】{fileitem.path} 下载已取消!")
|
logger.info(f"【115】{fileitem.path} 下载已取消!")
|
||||||
r.close()
|
r.close()
|
||||||
return None
|
return None
|
||||||
|
|||||||
+30
-28
@@ -9,31 +9,32 @@ import sys
|
|||||||
import threading
|
import threading
|
||||||
from asyncio import AbstractEventLoop
|
from asyncio import AbstractEventLoop
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Type, Union, get_origin, get_args
|
from typing import Any, Dict, List, Optional, Tuple, Type, Union, get_args, get_origin
|
||||||
from urllib.parse import quote, urlencode, urlparse
|
from urllib.parse import quote, urlencode, urlparse
|
||||||
|
|
||||||
from dotenv import set_key, unset_key
|
from dotenv import set_key, unset_key
|
||||||
from pydantic import BaseModel, Field, ConfigDict, model_validator
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
from app.foundation.environment import is_free_threaded_runtime
|
|
||||||
from app.runtime.log import (
|
|
||||||
LogConfigModel,
|
|
||||||
configure_log_settings,
|
|
||||||
configure_log_writer,
|
|
||||||
logger,
|
|
||||||
log_settings,
|
|
||||||
NonBlockingFileHandler,
|
|
||||||
)
|
|
||||||
from app.schemas.types import MediaType
|
|
||||||
from app.foundation.environment import (
|
from app.foundation.environment import (
|
||||||
cpu_arch,
|
cpu_arch,
|
||||||
get_env_path,
|
get_env_path,
|
||||||
is_docker,
|
is_docker,
|
||||||
|
is_free_threaded_runtime,
|
||||||
is_frozen,
|
is_frozen,
|
||||||
)
|
)
|
||||||
from app.foundation.url import UrlUtils
|
from app.foundation.url import UrlUtils
|
||||||
|
from app.runtime.log import (
|
||||||
|
LogConfigModel,
|
||||||
|
NonBlockingFileHandler,
|
||||||
|
configure_log_settings,
|
||||||
|
configure_log_writer,
|
||||||
|
log_settings,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.runtime.version import get_app_version
|
from app.runtime.version import get_app_version
|
||||||
|
from app.schemas.types import MediaType
|
||||||
|
|
||||||
|
|
||||||
class SystemConfModel(BaseModel):
|
class SystemConfModel(BaseModel):
|
||||||
@@ -1374,7 +1375,6 @@ class GlobalVar(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# 系统停止事件
|
# 系统停止事件
|
||||||
STOP_EVENT: threading.Event = threading.Event()
|
|
||||||
# webpush订阅
|
# webpush订阅
|
||||||
SUBSCRIPTIONS: List[dict] = []
|
SUBSCRIPTIONS: List[dict] = []
|
||||||
# webpush订阅读写锁
|
# webpush订阅读写锁
|
||||||
@@ -1391,18 +1391,28 @@ class GlobalVar(object):
|
|||||||
self._event_loop_owners: dict[object, AbstractEventLoop] = {}
|
self._event_loop_owners: dict[object, AbstractEventLoop] = {}
|
||||||
self._event_loop_owner_lock = threading.Lock()
|
self._event_loop_owner_lock = threading.Lock()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def STOP_EVENT(self) -> threading.Event:
|
||||||
|
"""兼容旧代码读取进程停止事件。"""
|
||||||
|
return runtime_stop_state.system_event
|
||||||
|
|
||||||
|
@STOP_EVENT.setter
|
||||||
|
def STOP_EVENT(self, event: threading.Event) -> None:
|
||||||
|
"""兼容旧测试替换事件,同时保持新 StopState 为唯一状态源。"""
|
||||||
|
runtime_stop_state.replace_system_event(event)
|
||||||
|
|
||||||
def stop_system(self):
|
def stop_system(self):
|
||||||
"""
|
"""
|
||||||
停止系统
|
停止系统
|
||||||
"""
|
"""
|
||||||
self.STOP_EVENT.set()
|
runtime_stop_state.stop_system()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_system_stopped(self):
|
def is_system_stopped(self):
|
||||||
"""
|
"""
|
||||||
是否停止
|
是否停止
|
||||||
"""
|
"""
|
||||||
return self.STOP_EVENT.is_set()
|
return runtime_stop_state.is_system_stopped
|
||||||
|
|
||||||
def get_subscriptions(self):
|
def get_subscriptions(self):
|
||||||
"""
|
"""
|
||||||
@@ -1444,39 +1454,31 @@ class GlobalVar(object):
|
|||||||
"""
|
"""
|
||||||
停止工作流
|
停止工作流
|
||||||
"""
|
"""
|
||||||
if workflow_id not in self.EMERGENCY_STOP_WORKFLOWS:
|
runtime_stop_state.stop_workflow(workflow_id)
|
||||||
self.EMERGENCY_STOP_WORKFLOWS.append(workflow_id)
|
|
||||||
|
|
||||||
def workflow_resume(self, workflow_id: int):
|
def workflow_resume(self, workflow_id: int):
|
||||||
"""
|
"""
|
||||||
恢复工作流
|
恢复工作流
|
||||||
"""
|
"""
|
||||||
if workflow_id in self.EMERGENCY_STOP_WORKFLOWS:
|
runtime_stop_state.resume_workflow(workflow_id)
|
||||||
self.EMERGENCY_STOP_WORKFLOWS.remove(workflow_id)
|
|
||||||
|
|
||||||
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||||
"""
|
"""
|
||||||
是否停止工作流
|
是否停止工作流
|
||||||
"""
|
"""
|
||||||
return self.is_system_stopped or workflow_id in self.EMERGENCY_STOP_WORKFLOWS
|
return runtime_stop_state.is_workflow_stopped(workflow_id)
|
||||||
|
|
||||||
def stop_transfer(self, path: str):
|
def stop_transfer(self, path: str):
|
||||||
"""
|
"""
|
||||||
停止文件整理
|
停止文件整理
|
||||||
"""
|
"""
|
||||||
if path not in self.EMERGENCY_STOP_TRANSFER:
|
runtime_stop_state.stop_transfer(path)
|
||||||
self.EMERGENCY_STOP_TRANSFER.append(path)
|
|
||||||
|
|
||||||
def is_transfer_stopped(self, path: str) -> bool:
|
def is_transfer_stopped(self, path: str) -> bool:
|
||||||
"""
|
"""
|
||||||
是否停止文件整理
|
是否停止文件整理
|
||||||
"""
|
"""
|
||||||
if self.is_system_stopped:
|
return runtime_stop_state.consume_transfer_stop(path)
|
||||||
return True
|
|
||||||
if path in self.EMERGENCY_STOP_TRANSFER:
|
|
||||||
self.EMERGENCY_STOP_TRANSFER.remove(path)
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def loop(self) -> AbstractEventLoop:
|
def loop(self) -> AbstractEventLoop:
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
"""进程停止与细粒度取消信号契约。"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import threading
|
||||||
|
from typing import Protocol
|
||||||
|
|
||||||
|
|
||||||
|
class StopState(Protocol):
|
||||||
|
"""向运行时消费者暴露系统、工作流和整理任务的停止状态。"""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_system_stopped(self) -> bool:
|
||||||
|
"""返回系统是否已经进入停止阶段。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def stop_system(self) -> None:
|
||||||
|
"""发布不可逆的当前进程停止信号。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def stop_workflow(self, workflow_id: int) -> None:
|
||||||
|
"""请求停止指定工作流。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def resume_workflow(self, workflow_id: int) -> None:
|
||||||
|
"""清除指定工作流的停止请求。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||||
|
"""返回系统或指定工作流是否已经停止。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def stop_transfer(self, path: str) -> None:
|
||||||
|
"""登记指定源路径的一次性整理停止请求。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def consume_transfer_stop(self, path: str) -> bool:
|
||||||
|
"""消费指定路径的停止请求;系统停止时始终返回真。"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessStopState:
|
||||||
|
"""线程安全地持有当前进程的停止与细粒度取消状态。"""
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._system_event = threading.Event()
|
||||||
|
self._workflow_ids: set[int] = set()
|
||||||
|
self._transfer_paths: set[str] = set()
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def system_event(self) -> threading.Event:
|
||||||
|
"""返回兼容入口使用的系统停止事件。"""
|
||||||
|
return self._system_event
|
||||||
|
|
||||||
|
def replace_system_event(self, event: threading.Event) -> None:
|
||||||
|
"""替换系统事件,仅供旧 ABI 与隔离测试继续使用。"""
|
||||||
|
self._system_event = event
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_system_stopped(self) -> bool:
|
||||||
|
"""返回系统是否已经进入停止阶段。"""
|
||||||
|
return self._system_event.is_set()
|
||||||
|
|
||||||
|
def stop_system(self) -> None:
|
||||||
|
"""发布不可逆的当前进程停止信号。"""
|
||||||
|
self._system_event.set()
|
||||||
|
|
||||||
|
def stop_workflow(self, workflow_id: int) -> None:
|
||||||
|
"""请求停止指定工作流。"""
|
||||||
|
with self._lock:
|
||||||
|
self._workflow_ids.add(workflow_id)
|
||||||
|
|
||||||
|
def resume_workflow(self, workflow_id: int) -> None:
|
||||||
|
"""清除指定工作流的停止请求。"""
|
||||||
|
with self._lock:
|
||||||
|
self._workflow_ids.discard(workflow_id)
|
||||||
|
|
||||||
|
def is_workflow_stopped(self, workflow_id: int) -> bool:
|
||||||
|
"""返回系统或指定工作流是否已经停止。"""
|
||||||
|
if self.is_system_stopped:
|
||||||
|
return True
|
||||||
|
with self._lock:
|
||||||
|
return workflow_id in self._workflow_ids
|
||||||
|
|
||||||
|
def stop_transfer(self, path: str) -> None:
|
||||||
|
"""登记指定源路径的一次性整理停止请求。"""
|
||||||
|
with self._lock:
|
||||||
|
self._transfer_paths.add(path)
|
||||||
|
|
||||||
|
def consume_transfer_stop(self, path: str) -> bool:
|
||||||
|
"""消费指定路径的停止请求;系统停止时始终返回真。"""
|
||||||
|
if self.is_system_stopped:
|
||||||
|
return True
|
||||||
|
with self._lock:
|
||||||
|
if path not in self._transfer_paths:
|
||||||
|
return False
|
||||||
|
self._transfer_paths.remove(path)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def workflow_stop_ids(self) -> list[int]:
|
||||||
|
"""返回旧诊断入口需要的工作流停止快照。"""
|
||||||
|
with self._lock:
|
||||||
|
return list(self._workflow_ids)
|
||||||
|
|
||||||
|
def transfer_stop_paths(self) -> list[str]:
|
||||||
|
"""返回旧诊断入口需要的整理停止快照。"""
|
||||||
|
with self._lock:
|
||||||
|
return list(self._transfer_paths)
|
||||||
|
|
||||||
|
|
||||||
|
runtime_stop_state = ProcessStopState()
|
||||||
+34
-33
@@ -9,16 +9,30 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from typing import Callable, Optional, Dict, Any, List
|
from typing import Any, Callable, Dict, List, Optional
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
from apscheduler.executors.pool import ThreadPoolExecutor
|
from apscheduler.executors.pool import ThreadPoolExecutor
|
||||||
from apscheduler.jobstores.base import JobLookupError
|
from apscheduler.jobstores.base import JobLookupError
|
||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from apscheduler.triggers.cron import CronTrigger
|
from apscheduler.triggers.cron import CronTrigger
|
||||||
from app.schemas.dashboard import ScheduleInfo as _SchemaScheduleInfo
|
|
||||||
from app.schemas.dashboard import ScheduleProgress as _SchemaScheduleProgress
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
from app.schemas.system import MediaServerConf as _SchemaMediaServerConf
|
from app.adapters.system.update import system_update_manager
|
||||||
|
from app.application.agentdata import get_agent_task_port
|
||||||
|
from app.application.configuration import (
|
||||||
|
SchedulerRuntimeConfig,
|
||||||
|
get_configured_system_config,
|
||||||
|
get_scheduler_runtime_config,
|
||||||
|
)
|
||||||
|
from app.application.database import get_database_governance
|
||||||
|
from app.application.image import WallpaperHelper
|
||||||
|
from app.application.mediaserver import get_mediaserver_configs
|
||||||
|
from app.application.messaging.message import MessageHelper
|
||||||
|
from app.application.outbox import dispatch_pending_outbox
|
||||||
|
from app.application.plugin.routes import register_plugin_api
|
||||||
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.chain.mediaserver import MediaServerChain
|
from app.chain.mediaserver import MediaServerChain
|
||||||
from app.chain.recommend import RecommendChain
|
from app.chain.recommend import RecommendChain
|
||||||
@@ -26,36 +40,23 @@ from app.chain.site import SiteChain
|
|||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.chain.transfer import TransferChain
|
from app.chain.transfer import TransferChain
|
||||||
from app.chain.workflow import WorkflowChain
|
from app.chain.workflow import WorkflowChain
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import Event, eventmanager
|
|
||||||
from app.application.agentdata import get_agent_task_port
|
|
||||||
from app.application.database import get_database_governance
|
|
||||||
from app.application.outbox import dispatch_pending_outbox
|
|
||||||
from app.application.plugin.runtime import get_plugin_manager
|
|
||||||
from app.application.plugin.routes import register_plugin_api
|
|
||||||
from app.application.configuration import (
|
|
||||||
SchedulerRuntimeConfig,
|
|
||||||
get_configured_system_config,
|
|
||||||
get_scheduler_runtime_config,
|
|
||||||
)
|
|
||||||
from app.application.image import WallpaperHelper
|
|
||||||
from app.application.mediaserver import get_mediaserver_configs
|
|
||||||
from app.application.messaging.message import MessageHelper
|
|
||||||
from app.runtime.progress import AsyncProgressHelper, ProgressHelper
|
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
|
||||||
from app.adapters.system.update import system_update_manager
|
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.message import MessageType
|
|
||||||
from app.schemas.workflow import Workflow
|
|
||||||
from app.schemas.types import EventType, SystemConfigKey
|
|
||||||
from app.runtime.gc import get_memory_usage
|
|
||||||
from app.runtime.reload import ConfigReloadMixin
|
|
||||||
from app.foundation.singleton import SingletonClass
|
from app.foundation.singleton import SingletonClass
|
||||||
from app.runtime.scheduling import TimerUtils
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.correlation import call_with_correlation, get_correlation_id
|
from app.runtime.correlation import call_with_correlation, get_correlation_id
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
|
from app.runtime.gc import get_memory_usage
|
||||||
|
from app.runtime.log import logger
|
||||||
from app.runtime.observability import record_metric
|
from app.runtime.observability import record_metric
|
||||||
|
from app.runtime.progress import AsyncProgressHelper, ProgressHelper
|
||||||
|
from app.runtime.reload import ConfigReloadMixin
|
||||||
|
from app.runtime.scheduling import TimerUtils
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.dashboard import ScheduleInfo as _SchemaScheduleInfo
|
||||||
|
from app.schemas.dashboard import ScheduleProgress as _SchemaScheduleProgress
|
||||||
|
from app.schemas.message import Message, MessageType
|
||||||
|
from app.schemas.system import MediaServerConf as _SchemaMediaServerConf
|
||||||
|
from app.schemas.types import EventType, SystemConfigKey
|
||||||
|
from app.schemas.workflow import Workflow
|
||||||
|
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
SCHEDULER_PROGRESS_PREFIX = "scheduler"
|
SCHEDULER_PROGRESS_PREFIX = "scheduler"
|
||||||
@@ -1966,7 +1967,7 @@ class Scheduler(ConfigReloadMixin, metaclass=SingletonClass):
|
|||||||
"""停止旧计划的提交入口,保留已开始任务直到其自然完成。"""
|
"""停止旧计划的提交入口,保留已开始任务直到其自然完成。"""
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if (
|
if (
|
||||||
global_vars.is_system_stopped
|
runtime_stop_state.is_system_stopped
|
||||||
or self._lifecycle_state in {"stopping", "reloading"}
|
or self._lifecycle_state in {"stopping", "reloading"}
|
||||||
):
|
):
|
||||||
return False, None
|
return False, None
|
||||||
@@ -2066,7 +2067,7 @@ class Scheduler(ConfigReloadMixin, metaclass=SingletonClass):
|
|||||||
if self._auth_count > __max_try__:
|
if self._auth_count > __max_try__:
|
||||||
if not self._auth_message:
|
if not self._auth_message:
|
||||||
SchedulerChain().messagehelper.put(
|
SchedulerChain().messagehelper.put(
|
||||||
title=f"用户认证失败",
|
title="用户认证失败",
|
||||||
message="用户认证失败次数过多,将不再尝试认证!",
|
message="用户认证失败次数过多,将不再尝试认证!",
|
||||||
role="system",
|
role="system",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,15 +4,13 @@ from collections.abc import AsyncGenerator, Callable, Generator
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
|
|
||||||
from app.runtime.tasks import TaskRegistry
|
from app.application.configuration import RuntimeConfiguration, RuntimeSettingsService
|
||||||
|
|
||||||
from app.application.messaging.chat import (
|
from app.application.messaging.chat import (
|
||||||
AsyncAgentChatRepository,
|
|
||||||
AgentChatPersistenceService,
|
AgentChatPersistenceService,
|
||||||
|
AsyncAgentChatRepository,
|
||||||
AsyncUnitOfWork,
|
AsyncUnitOfWork,
|
||||||
)
|
)
|
||||||
from app.application.outbox import AsyncOutboxTransaction
|
from app.application.outbox import AsyncOutboxTransaction
|
||||||
from app.application.configuration import RuntimeConfiguration, RuntimeSettingsService
|
|
||||||
from app.application.subscription.delete import SubscribeDeletionRepository
|
from app.application.subscription.delete import SubscribeDeletionRepository
|
||||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
||||||
from app.application.subscription.mutation import (
|
from app.application.subscription.mutation import (
|
||||||
@@ -20,6 +18,7 @@ from app.application.subscription.mutation import (
|
|||||||
SubscriptionMutationRepository,
|
SubscriptionMutationRepository,
|
||||||
)
|
)
|
||||||
from app.application.workflow import WorkflowCachePort
|
from app.application.workflow import WorkflowCachePort
|
||||||
|
from app.runtime.tasks import TaskRegistry
|
||||||
|
|
||||||
|
|
||||||
class AgentChatRepositoryFactory(Protocol):
|
class AgentChatRepositoryFactory(Protocol):
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from app.agent.llm.gateway import register_llm_provider_runtime
|
||||||
from app.agent.runtime_loader import (
|
from app.agent.runtime_loader import (
|
||||||
activate_agent_service,
|
activate_agent_service,
|
||||||
begin_agent_shutdown,
|
begin_agent_shutdown,
|
||||||
close_materialized_terminal_sessions,
|
close_materialized_terminal_sessions,
|
||||||
get_agent_manager as get_runtime_agent_manager,
|
|
||||||
get_running_agent_manager as get_runtime_running_agent_manager,
|
|
||||||
is_tool_factory_materialized,
|
is_tool_factory_materialized,
|
||||||
reconcile_agent_service,
|
reconcile_agent_service,
|
||||||
)
|
)
|
||||||
from app.agent.llm.gateway import register_llm_provider_runtime
|
from app.agent.runtime_loader import (
|
||||||
|
get_agent_manager as get_runtime_agent_manager,
|
||||||
|
)
|
||||||
|
from app.agent.runtime_loader import (
|
||||||
|
get_running_agent_manager as get_runtime_running_agent_manager,
|
||||||
|
)
|
||||||
from app.application.agent import register_agent_service_providers
|
from app.application.agent import register_agent_service_providers
|
||||||
from app.application.messaging.skill import register_skill_catalog_provider
|
from app.application.messaging.skill import register_skill_catalog_provider
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
@@ -19,7 +23,6 @@ from app.runtime.events import Event, eventmanager
|
|||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.types import EventType
|
from app.schemas.types import EventType
|
||||||
|
|
||||||
|
|
||||||
AGENT_BLOCKING_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10.0
|
AGENT_BLOCKING_EXECUTOR_SHUTDOWN_TIMEOUT_SECONDS = 10.0
|
||||||
|
|
||||||
|
|
||||||
@@ -226,6 +229,8 @@ async def stop_agent() -> bool:
|
|||||||
if is_tool_factory_materialized():
|
if is_tool_factory_materialized():
|
||||||
from app.agent.tools.base import (
|
from app.agent.tools.base import (
|
||||||
begin_blocking_executor_shutdown,
|
begin_blocking_executor_shutdown,
|
||||||
|
)
|
||||||
|
from app.agent.tools.base import (
|
||||||
close_blocking_executors as close_executors,
|
close_blocking_executors as close_executors,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import traceback
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from configparser import ConfigParser as _ConfigParser
|
from configparser import ConfigParser as _ConfigParser
|
||||||
import traceback
|
|
||||||
|
|
||||||
from alembic.command import upgrade
|
from alembic.command import upgrade
|
||||||
from alembic.config import Config
|
from alembic.config import Config
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from app.adapters.system import rust as rust_accelerator
|
||||||
from app.domain.context import configure_tmdb_image_url_builder
|
from app.domain.context import configure_tmdb_image_url_builder
|
||||||
from app.domain.media import configure_search_source_provider
|
from app.domain.media import configure_search_source_provider
|
||||||
from app.domain.meta.customization import configure_customization_provider
|
from app.domain.meta.customization import configure_customization_provider
|
||||||
@@ -5,7 +6,6 @@ from app.domain.meta.releasegroup import configure_release_groups_provider
|
|||||||
from app.domain.meta.runtime import configure_recognition_runtime
|
from app.domain.meta.runtime import configure_recognition_runtime
|
||||||
from app.domain.meta.words import configure_custom_words_provider
|
from app.domain.meta.words import configure_custom_words_provider
|
||||||
from app.domain.metainfo import clear_rust_parse_options_cache
|
from app.domain.metainfo import clear_rust_parse_options_cache
|
||||||
from app.adapters.system import rust as rust_accelerator
|
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ from app.runtime.managed_resources import (
|
|||||||
configure_managed_resource_runtime,
|
configure_managed_resource_runtime,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
_runtime_lock = threading.RLock()
|
_runtime_lock = threading.RLock()
|
||||||
_managed_resource_runtime: Optional[CapabilityRuntime] = None
|
_managed_resource_runtime: Optional[CapabilityRuntime] = None
|
||||||
|
|
||||||
|
|||||||
+104
-105
@@ -3,7 +3,7 @@ import inspect
|
|||||||
import sys
|
import sys
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from app.adapters.cache.redis import RedisHelper, AsyncRedisHelper
|
from app.adapters.cache.redis import AsyncRedisHelper, RedisHelper
|
||||||
from app.chain.mediaserver import MediaServerChain
|
from app.chain.mediaserver import MediaServerChain
|
||||||
from app.chain.tmdb import TmdbChain
|
from app.chain.tmdb import TmdbChain
|
||||||
|
|
||||||
@@ -17,53 +17,57 @@ except ImportError as e:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
from app.adapters.system.host import SystemUtils
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.runtime.config import settings as legacy_settings
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
from app.runtime.config import settings as legacy_settings
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.cache import AsyncFileCache, FileCache
|
from app.adapters.external.server import (
|
||||||
from app.runtime.extensions.module_manager import ModuleManager
|
MoviePilotServerHelper,
|
||||||
from app.runtime.extensions.module.dispatcher import ModuleInvocationDispatcher
|
configure_server_application_services,
|
||||||
from app.runtime.extensions.plugin_manager import PluginManager
|
)
|
||||||
from app.runtime.events import EventHandlerBinding, EventManager
|
|
||||||
from app.runtime.execution import run_in_threadpool_to_completion
|
|
||||||
from app.runtime.observability import record_metric
|
|
||||||
from app.runtime.state import SystemHelper
|
|
||||||
from app.runtime.settings import configure_runtime_setting_provider
|
|
||||||
from app.runtime.thread import ThreadHelper
|
|
||||||
from app.adapters.network.doh import DohHelper
|
from app.adapters.network.doh import DohHelper
|
||||||
from app.adapters.system.resource import (
|
from app.adapters.system.resource import (
|
||||||
ResourceHelper,
|
ResourceHelper,
|
||||||
configure_resource_version_provider,
|
configure_resource_version_provider,
|
||||||
)
|
)
|
||||||
from app.application.messaging.message import (
|
from app.adapters.web.security.access import set_superuser_token_payload_provider
|
||||||
MessageHelper,
|
from app.api.data import ApiDataPorts, configure_api_data_runtime
|
||||||
MessageQueueManager,
|
from app.application.agentdata import configure_agent_data_ports
|
||||||
stop_message,
|
from app.application.agenttask import (
|
||||||
|
AgentTaskExecutionService,
|
||||||
|
configure_agent_task_execution,
|
||||||
|
)
|
||||||
|
from app.application.chain.context import (
|
||||||
|
ChainRuntimeContext,
|
||||||
|
configure_chain_runtime_context_provider,
|
||||||
|
)
|
||||||
|
from app.application.chain.data import configure_chain_data_ports, get_chain_data_ports
|
||||||
|
from app.application.chain.durable_events import (
|
||||||
|
restore_download_added,
|
||||||
|
restore_transfer_result,
|
||||||
)
|
)
|
||||||
from app.application.configuration import (
|
from app.application.configuration import (
|
||||||
RuntimeConfiguration,
|
RuntimeConfiguration,
|
||||||
RuntimeSettingsService,
|
RuntimeSettingsService,
|
||||||
SystemConfigService,
|
SystemConfigService,
|
||||||
get_configured_system_config,
|
|
||||||
TransferRetryConfig,
|
TransferRetryConfig,
|
||||||
configure_token_runtime_config,
|
|
||||||
configure_runtime_configuration,
|
configure_runtime_configuration,
|
||||||
configure_runtime_settings,
|
configure_runtime_settings,
|
||||||
configure_system_config,
|
configure_system_config,
|
||||||
|
configure_token_runtime_config,
|
||||||
configure_transfer_retry_config,
|
configure_transfer_retry_config,
|
||||||
)
|
get_configured_system_config,
|
||||||
from app.startup.composition.configuration import (
|
|
||||||
build_api_runtime_config,
|
|
||||||
build_chain_runtime_config,
|
|
||||||
build_scheduler_runtime_config,
|
|
||||||
build_token_runtime_config,
|
|
||||||
)
|
)
|
||||||
from app.application.database import configure_database_governance
|
from app.application.database import configure_database_governance
|
||||||
from app.application.service import configure_service_directory
|
from app.application.history import configure_transfer_history_provider
|
||||||
from app.application.plugin.runtime import configure_plugin_runtime
|
from app.application.image import configure_wallpaper_providers
|
||||||
from app.application.module import configure_module_runtime
|
from app.application.messaging.agent import (
|
||||||
|
dispatch_web_agent_message_event,
|
||||||
|
shutdown_web_agent_background_tasks,
|
||||||
|
wait_web_agent_background_tasks,
|
||||||
|
)
|
||||||
from app.application.messaging.chat import (
|
from app.application.messaging.chat import (
|
||||||
AgentChatPersistenceService,
|
AgentChatPersistenceService,
|
||||||
AgentChatService,
|
AgentChatService,
|
||||||
@@ -71,43 +75,58 @@ from app.application.messaging.chat import (
|
|||||||
configure_agent_chat_service,
|
configure_agent_chat_service,
|
||||||
get_configured_agent_chat_persistence,
|
get_configured_agent_chat_persistence,
|
||||||
)
|
)
|
||||||
from app.application.messaging.agent import (
|
from app.application.messaging.message import (
|
||||||
dispatch_web_agent_message_event,
|
MessageHelper,
|
||||||
shutdown_web_agent_background_tasks,
|
MessageQueueManager,
|
||||||
wait_web_agent_background_tasks,
|
stop_message,
|
||||||
)
|
)
|
||||||
from app.application.security.user import configure_user_lookups
|
from app.application.module import configure_module_runtime
|
||||||
from app.application.security.auth import AuthService, configure_auth_service
|
|
||||||
from app.application.security.passkeys import PasskeyService, configure_passkey_service
|
|
||||||
from app.application.security.url import close_image_proxy_block_log_coalescer
|
|
||||||
from app.application.security.userconfig import (
|
|
||||||
UserConfigurationService,
|
|
||||||
configure_user_configuration,
|
|
||||||
)
|
|
||||||
from app.application.history import configure_transfer_history_provider
|
|
||||||
from app.application.outbox import (
|
from app.application.outbox import (
|
||||||
OutboxDispatcher,
|
OutboxDispatcher,
|
||||||
configure_outbox_dispatcher,
|
configure_outbox_dispatcher,
|
||||||
durable_event_topic,
|
durable_event_topic,
|
||||||
validate_durable_event_handlers,
|
validate_durable_event_handlers,
|
||||||
)
|
)
|
||||||
from app.db.adapters.outbox import SqlAlchemyAsyncOutboxStager, SqlAlchemyOutboxRepository
|
from app.application.plugin.runtime import configure_plugin_runtime
|
||||||
from app.application.site.query import SiteQueryService, configure_site_query_service
|
from app.application.security.auth import AuthService, build_superuser_token_payload, configure_auth_service
|
||||||
from app.application.site.health import SiteHealthService, configure_site_health_service
|
from app.application.security.passkeys import PasskeyService, configure_passkey_service
|
||||||
from app.application.workflow import WorkflowQueryService, configure_workflow_query
|
from app.application.security.url import close_image_proxy_block_log_coalescer
|
||||||
from app.application.agentdata import configure_agent_data_ports
|
from app.application.security.user import configure_user_lookups
|
||||||
from app.application.agenttask import (
|
from app.application.security.userconfig import (
|
||||||
AgentTaskExecutionService,
|
UserConfigurationService,
|
||||||
configure_agent_task_execution,
|
configure_user_configuration,
|
||||||
)
|
|
||||||
from app.api.data import ApiDataPorts, configure_api_data_runtime
|
|
||||||
from app.application.subscription.write import configure_subscribe_writer
|
|
||||||
from app.adapters.external.server import (
|
|
||||||
MoviePilotServerHelper,
|
|
||||||
configure_server_application_services,
|
|
||||||
)
|
)
|
||||||
from app.application.server.report import ServerReportService
|
from app.application.server.report import ServerReportService
|
||||||
from app.application.server.share import ServerSharingService
|
from app.application.server.share import ServerSharingService
|
||||||
|
from app.application.service import configure_service_directory
|
||||||
|
from app.application.site.health import SiteHealthService, configure_site_health_service
|
||||||
|
from app.application.site.query import SiteQueryService, configure_site_query_service
|
||||||
|
from app.application.subscription.write import configure_subscribe_writer
|
||||||
|
from app.application.workflow import WorkflowQueryService, configure_workflow_query
|
||||||
|
from app.command import CommandChain
|
||||||
|
from app.db.adapters.chain import TransactionalChainDurableEventWriter
|
||||||
|
from app.db.adapters.download import TransactionalDownloadFailureRepository
|
||||||
|
from app.db.adapters.outbox import SqlAlchemyAsyncOutboxStager, SqlAlchemyOutboxRepository
|
||||||
|
from app.db.adapters.site import TransactionalSiteRepository
|
||||||
|
from app.db.adapters.subscription import TransactionalSubscribeWriter
|
||||||
|
from app.db.adapters.transaction import TransactionalWriteRunner
|
||||||
|
from app.db.adapters.workflow import TransactionalWorkflowExecutionService
|
||||||
|
from app.db.oper.agentchat import AgentChatOper
|
||||||
|
from app.db.oper.agenttask import AgentTaskOper
|
||||||
|
from app.db.oper.downloadhistory import DownloadHistoryOper
|
||||||
|
from app.db.oper.mediaserver import MediaServerOper
|
||||||
|
from app.db.oper.message import MessageOper
|
||||||
|
from app.db.oper.passkey import PassKeyOper
|
||||||
|
from app.db.oper.plugindata import PluginDataOper
|
||||||
|
from app.db.oper.site import SiteOper
|
||||||
|
from app.db.oper.subscribe import SubscribeOper
|
||||||
|
from app.db.oper.subscribehistory import SubscribeHistoryOper
|
||||||
|
from app.db.oper.systemconfig import SystemConfigOper
|
||||||
|
from app.db.oper.transferhistory import TransferHistoryOper
|
||||||
|
from app.db.oper.transferpending import TransferPendingOper
|
||||||
|
from app.db.oper.user import UserOper
|
||||||
|
from app.db.oper.userconfig import UserConfigOper
|
||||||
|
from app.db.oper.workflow import WorkflowOper, configure_workflow_legacy_writer
|
||||||
from app.db.session import (
|
from app.db.session import (
|
||||||
SessionFactory,
|
SessionFactory,
|
||||||
async_session_scope,
|
async_session_scope,
|
||||||
@@ -115,47 +134,35 @@ from app.db.session import (
|
|||||||
get_async_db,
|
get_async_db,
|
||||||
get_db,
|
get_db,
|
||||||
)
|
)
|
||||||
from app.db.worker import DatabaseWorker
|
|
||||||
from app.db.uow import (
|
from app.db.uow import (
|
||||||
SqlAlchemyAsyncUnitOfWork,
|
SqlAlchemyAsyncUnitOfWork,
|
||||||
SqlAlchemyUnitOfWork,
|
SqlAlchemyUnitOfWork,
|
||||||
configure_transaction_runners,
|
configure_transaction_runners,
|
||||||
)
|
)
|
||||||
from app.db.oper.subscribe import SubscribeOper
|
from app.db.worker import DatabaseWorker
|
||||||
from app.db.oper.agentchat import AgentChatOper
|
from app.runtime.cache import AsyncFileCache, FileCache
|
||||||
from app.db.oper.agenttask import AgentTaskOper
|
from app.runtime.events import EventHandlerBinding, EventManager
|
||||||
from app.db.oper.user import UserOper
|
from app.runtime.execution import run_in_threadpool_to_completion
|
||||||
from app.db.oper.passkey import PassKeyOper
|
from app.runtime.extensions.module.dispatcher import ModuleInvocationDispatcher
|
||||||
from app.db.oper.userconfig import UserConfigOper
|
from app.runtime.extensions.module_manager import ModuleManager
|
||||||
from app.db.oper.transferhistory import TransferHistoryOper
|
from app.runtime.extensions.plugin_manager import PluginManager
|
||||||
from app.db.oper.downloadhistory import DownloadHistoryOper
|
from app.runtime.extensions.service_config import (
|
||||||
from app.db.oper.transferpending import TransferPendingOper
|
ServiceConfigHelper,
|
||||||
from app.db.oper.mediaserver import MediaServerOper
|
configure_service_config_reader,
|
||||||
from app.db.oper.site import SiteOper
|
)
|
||||||
from app.db.oper.message import MessageOper
|
from app.runtime.observability import record_metric
|
||||||
from app.db.oper.subscribehistory import SubscribeHistoryOper
|
from app.runtime.settings import configure_runtime_setting_provider
|
||||||
from app.db.oper.plugindata import PluginDataOper
|
from app.runtime.state import SystemHelper
|
||||||
from app.db.oper.systemconfig import SystemConfigOper
|
from app.runtime.tasks import get_task_registry
|
||||||
from app.db.oper.workflow import WorkflowOper, configure_workflow_legacy_writer
|
from app.runtime.thread import ThreadHelper
|
||||||
from app.command import CommandChain
|
from app.schemas.message import Message, MessageType
|
||||||
from app.schemas.message import Message
|
|
||||||
from app.schemas.message import MessageType
|
|
||||||
from app.schemas.types import EventType, SystemConfigKey
|
from app.schemas.types import EventType, SystemConfigKey
|
||||||
from app.startup.initializers.agent import init_agent
|
from app.startup.composition.configuration import (
|
||||||
from app.startup.composition.database import build_database_governance
|
build_api_runtime_config,
|
||||||
from app.startup.initializers.managed_resources import (
|
build_chain_runtime_config,
|
||||||
init_managed_resources,
|
build_scheduler_runtime_config,
|
||||||
stop_managed_resources,
|
build_token_runtime_config,
|
||||||
)
|
)
|
||||||
from app.db.adapters.subscription import TransactionalSubscribeWriter
|
|
||||||
from app.startup.composition.subscription import (
|
|
||||||
configure_transactional_subscription_scopes,
|
|
||||||
)
|
|
||||||
from app.db.adapters.chain import TransactionalChainDurableEventWriter
|
|
||||||
from app.db.adapters.download import TransactionalDownloadFailureRepository
|
|
||||||
from app.db.adapters.site import TransactionalSiteRepository
|
|
||||||
from app.db.adapters.workflow import TransactionalWorkflowExecutionService
|
|
||||||
from app.db.adapters.transaction import TransactionalWriteRunner
|
|
||||||
from app.startup.composition.context import (
|
from app.startup.composition.context import (
|
||||||
AgentChatRuntime,
|
AgentChatRuntime,
|
||||||
AuthenticationRuntime,
|
AuthenticationRuntime,
|
||||||
@@ -167,24 +174,15 @@ from app.startup.composition.context import (
|
|||||||
SubscriptionRuntime,
|
SubscriptionRuntime,
|
||||||
WorkflowRuntime,
|
WorkflowRuntime,
|
||||||
)
|
)
|
||||||
from app.adapters.web.security.access import set_superuser_token_payload_provider
|
from app.startup.composition.database import build_database_governance
|
||||||
from app.application.security.auth import build_superuser_token_payload
|
from app.startup.composition.subscription import (
|
||||||
from app.application.image import configure_wallpaper_providers
|
configure_transactional_subscription_scopes,
|
||||||
from app.application.chain.context import (
|
|
||||||
ChainRuntimeContext,
|
|
||||||
configure_chain_runtime_context_provider,
|
|
||||||
)
|
)
|
||||||
from app.application.chain.durable_events import (
|
from app.startup.initializers.agent import init_agent
|
||||||
restore_download_added,
|
from app.startup.initializers.managed_resources import (
|
||||||
restore_transfer_result,
|
init_managed_resources,
|
||||||
|
stop_managed_resources,
|
||||||
)
|
)
|
||||||
from app.application.chain.data import configure_chain_data_ports, get_chain_data_ports
|
|
||||||
from app.runtime.extensions.service_config import (
|
|
||||||
ServiceConfigHelper,
|
|
||||||
configure_service_config_reader,
|
|
||||||
)
|
|
||||||
from app.runtime.tasks import get_task_registry
|
|
||||||
|
|
||||||
|
|
||||||
_database_worker: DatabaseWorker | None = None
|
_database_worker: DatabaseWorker | None = None
|
||||||
|
|
||||||
@@ -252,6 +250,7 @@ def _build_chain_runtime_context() -> ChainRuntimeContext:
|
|||||||
configuration=build_chain_runtime_config(settings),
|
configuration=build_chain_runtime_config(settings),
|
||||||
data_ports=get_chain_data_ports(),
|
data_ports=get_chain_data_ports(),
|
||||||
durable_event_writer=TransactionalChainDurableEventWriter(SessionFactory),
|
durable_event_writer=TransactionalChainDurableEventWriter(SessionFactory),
|
||||||
|
stop_state=runtime_stop_state,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -503,7 +502,7 @@ def stop_frontend():
|
|||||||
or not SystemUtils.is_windows():
|
or not SystemUtils.is_windows():
|
||||||
return
|
return
|
||||||
import subprocess
|
import subprocess
|
||||||
subprocess.Popen(f"taskkill /f /im nginx.exe", shell=True)
|
subprocess.Popen("taskkill /f /im nginx.exe", shell=True)
|
||||||
|
|
||||||
|
|
||||||
def clear_temp():
|
def clear_temp():
|
||||||
|
|||||||
@@ -1,29 +1,36 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from app.application.plugin.routes import register_plugin_api
|
||||||
from app.runtime.compat.diagnostics import (
|
from app.runtime.compat.diagnostics import (
|
||||||
configure_legacy_import_diagnostics,
|
configure_legacy_import_diagnostics,
|
||||||
scan_plugin_legacy_imports,
|
scan_plugin_legacy_imports,
|
||||||
)
|
)
|
||||||
from app.runtime.compat.resource_imports import scan_plugin_resource_imports
|
from app.runtime.compat.resource_imports import scan_plugin_resource_imports
|
||||||
from app.application.plugin.routes import register_plugin_api
|
|
||||||
from app.runtime.config import global_vars
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.extensions.plugin_manager import (
|
from app.adapters.external.market import (
|
||||||
PluginManager,
|
VERSION_BACKWARD_COMPATIBLE_FLAGS,
|
||||||
configure_plugin_catalog_factory,
|
PluginHelper,
|
||||||
configure_plugin_install_reporter,
|
configure_installed_plugins_provider,
|
||||||
configure_plugin_legacy_import_services,
|
|
||||||
configure_plugin_route_refresher,
|
|
||||||
configure_plugin_resource_import_preparer,
|
|
||||||
configure_site_auth_level_provider,
|
|
||||||
)
|
)
|
||||||
from app.runtime.execution import run_in_threadpool_to_completion
|
from app.adapters.external.plugin.client import PluginMarketClient
|
||||||
from app.runtime.extensions.plugin.dependency import PluginDependencyInstallResult
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.adapters.system.host import SystemUtils
|
||||||
|
from app.adapters.system.plugin.dependency import PluginDependencyInstaller
|
||||||
|
from app.adapters.system.plugin.manifest import dependency_manifest_status
|
||||||
|
from app.adapters.system.plugin.package import PluginPackageManager
|
||||||
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.application.plugin.catalog import PluginCatalogService
|
from app.application.plugin.catalog import PluginCatalogService
|
||||||
from app.application.plugin.data import DeletePluginDataCommand
|
from app.application.plugin.data import DeletePluginDataCommand
|
||||||
from app.adapters.external.plugin.client import PluginMarketClient
|
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
||||||
|
from app.db.oper.plugindata import PluginDataOper
|
||||||
|
from app.db.session import SessionFactory
|
||||||
|
from app.db.uow import SqlAlchemyUnitOfWork
|
||||||
|
from app.foundation.version import compare_version
|
||||||
|
from app.runtime.execution import run_in_threadpool_to_completion
|
||||||
|
from app.runtime.extensions.plugin.dependency import PluginDependencyInstallResult
|
||||||
from app.runtime.extensions.plugin.storage import (
|
from app.runtime.extensions.plugin.storage import (
|
||||||
PluginStorage,
|
PluginStorage,
|
||||||
configure_plugin_storage,
|
configure_plugin_storage,
|
||||||
@@ -32,26 +39,19 @@ from app.runtime.extensions.plugin.system import (
|
|||||||
PluginSystemServices,
|
PluginSystemServices,
|
||||||
configure_plugin_system,
|
configure_plugin_system,
|
||||||
)
|
)
|
||||||
from app.runtime.managed_resources import acquire_managed_resource
|
from app.runtime.extensions.plugin_manager import (
|
||||||
from app.application.site.sites import SitesHelper # pylint: disable=import-error,no-name-in-module
|
PluginManager,
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
configure_plugin_catalog_factory,
|
||||||
from app.adapters.external.market import (
|
configure_plugin_install_reporter,
|
||||||
PluginHelper,
|
configure_plugin_legacy_import_services,
|
||||||
VERSION_BACKWARD_COMPATIBLE_FLAGS,
|
configure_plugin_resource_import_preparer,
|
||||||
configure_installed_plugins_provider,
|
configure_plugin_route_refresher,
|
||||||
|
configure_site_auth_level_provider,
|
||||||
)
|
)
|
||||||
from app.adapters.system.plugin.dependency import PluginDependencyInstaller
|
|
||||||
from app.adapters.system.plugin.manifest import dependency_manifest_status
|
|
||||||
from app.adapters.system.plugin.package import PluginPackageManager
|
|
||||||
from app.adapters.system.host import SystemUtils
|
|
||||||
from app.db.oper.plugindata import PluginDataOper
|
|
||||||
from app.application.configuration import get_configured_system_config
|
|
||||||
from app.db.session import SessionFactory
|
|
||||||
from app.db.uow import SqlAlchemyUnitOfWork
|
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.foundation.version import compare_version
|
from app.runtime.managed_resources import acquire_managed_resource
|
||||||
from app.schemas.plugin import PluginRuntimeStatus
|
|
||||||
from app.schemas.exception import PluginMutationRejectedError
|
from app.schemas.exception import PluginMutationRejectedError
|
||||||
|
from app.schemas.plugin import PluginRuntimeStatus
|
||||||
from app.schemas.types import SystemConfigKey
|
from app.schemas.types import SystemConfigKey
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
|
||||||
def init_routers(app: FastAPI, api_prefix: str = "/api/v1"):
|
def init_routers(app: FastAPI, api_prefix: str = "/api/v1"):
|
||||||
"""
|
"""
|
||||||
初始化路由
|
初始化路由
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
from app.application.workflow import configure_workflow_runtime
|
from app.application.workflow import configure_workflow_runtime
|
||||||
from app.workflow import WorkFlowManager
|
from app.workflow import WorkFlowManager
|
||||||
|
|
||||||
|
|
||||||
# 启动模块是 concrete WorkFlowManager 的唯一宿主装配边界。
|
# 启动模块是 concrete WorkFlowManager 的唯一宿主装配边界。
|
||||||
configure_workflow_runtime(lambda: WorkFlowManager())
|
configure_workflow_runtime(lambda: WorkFlowManager())
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import Awaitable, Callable
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
from app.startup.initializers.cache import configure_cache_dependencies
|
from app.startup.initializers.cache import configure_cache_dependencies
|
||||||
|
|
||||||
# 缓存装饰器会在业务模块导入时创建后端,必须先完成适配器装配。
|
# 缓存装饰器会在业务模块导入时创建后端,必须先完成适配器装配。
|
||||||
configure_cache_dependencies()
|
configure_cache_dependencies()
|
||||||
# urllib3-future 覆盖 urllib3 命名空间后删除了 format_header_param,导致 telebot 崩溃,需在加载模块前打补丁
|
# urllib3-future 覆盖 urllib3 命名空间后删除了 format_header_param,导致 telebot 崩溃,需在加载模块前打补丁
|
||||||
@@ -24,23 +25,29 @@ try:
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from app.chain.system import SystemChain
|
|
||||||
from app.application.plugin.lifecycle import plugin_lifecycle
|
from app.application.plugin.lifecycle import plugin_lifecycle
|
||||||
from app.application.plugin.runtime import get_plugin_manager
|
from app.application.plugin.runtime import get_plugin_manager
|
||||||
|
from app.chain.system import SystemChain
|
||||||
|
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
|
||||||
from app.runtime.config import global_vars
|
from app.runtime.config import global_vars
|
||||||
from app.runtime.settings import RuntimeSettingsCompat
|
from app.runtime.settings import RuntimeSettingsCompat
|
||||||
from app.foundation.environment import is_free_threaded_runtime, is_gil_enabled
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
|
||||||
settings = RuntimeSettingsCompat()
|
settings = RuntimeSettingsCompat()
|
||||||
from app.runtime.health import get_application_health
|
|
||||||
from app.runtime.execution import run_in_threadpool_to_completion
|
|
||||||
from app.runtime.topology import validate_process_topology
|
|
||||||
from app.runtime.tasks import TaskRegistry, configure_task_registry
|
|
||||||
from app.adapters.external.server import MoviePilotServerHelper
|
from app.adapters.external.server import MoviePilotServerHelper
|
||||||
|
from app.adapters.network.http import (
|
||||||
|
aclose_shared_async_transports,
|
||||||
|
configure_default_user_agent,
|
||||||
|
)
|
||||||
|
from app.db.engine import check_connection_budget, get_engine, get_global_async_engine
|
||||||
|
from app.runtime.execution import run_in_threadpool_to_completion
|
||||||
|
from app.runtime.health import get_application_health
|
||||||
|
from app.runtime.log import LoggerManager, logger
|
||||||
from app.runtime.state import SystemHelper
|
from app.runtime.state import SystemHelper
|
||||||
from app.runtime.log import logger, LoggerManager
|
from app.runtime.tasks import TaskRegistry, configure_task_registry
|
||||||
from app.startup.initializers.command import init_command, restart_command
|
from app.runtime.topology import validate_process_topology
|
||||||
from app.startup.initializers.agent import stop_agent
|
from app.startup.initializers.agent import stop_agent
|
||||||
|
from app.startup.initializers.command import init_command, restart_command
|
||||||
from app.startup.initializers.domain import configure_domain_dependencies
|
from app.startup.initializers.domain import configure_domain_dependencies
|
||||||
from app.startup.initializers.modules import (
|
from app.startup.initializers.modules import (
|
||||||
drain_events,
|
drain_events,
|
||||||
@@ -48,7 +55,7 @@ from app.startup.initializers.modules import (
|
|||||||
settle_events,
|
settle_events,
|
||||||
stop_modules,
|
stop_modules,
|
||||||
)
|
)
|
||||||
from app.startup.initializers.monitor import stop_monitor, init_monitor
|
from app.startup.initializers.monitor import init_monitor, stop_monitor
|
||||||
from app.startup.initializers.plugins import (
|
from app.startup.initializers.plugins import (
|
||||||
configure_plugin_services,
|
configure_plugin_services,
|
||||||
execute_task,
|
execute_task,
|
||||||
@@ -61,11 +68,10 @@ from app.startup.initializers.plugins import (
|
|||||||
)
|
)
|
||||||
from app.startup.initializers.routers import init_routers
|
from app.startup.initializers.routers import init_routers
|
||||||
from app.startup.initializers.scheduler import (
|
from app.startup.initializers.scheduler import (
|
||||||
stop_scheduler,
|
|
||||||
init_scheduler,
|
|
||||||
init_plugin_scheduler,
|
init_plugin_scheduler,
|
||||||
|
init_scheduler,
|
||||||
|
stop_scheduler,
|
||||||
)
|
)
|
||||||
from app.db.engine import check_connection_budget, get_engine, get_global_async_engine
|
|
||||||
from app.startup.initializers.transfer import (
|
from app.startup.initializers.transfer import (
|
||||||
replay_pending_transfers,
|
replay_pending_transfers,
|
||||||
stop_transfer_runtime,
|
stop_transfer_runtime,
|
||||||
@@ -77,10 +83,6 @@ from app.startup.lifecycle.components import (
|
|||||||
LifecycleMode,
|
LifecycleMode,
|
||||||
lifecycle_manifest,
|
lifecycle_manifest,
|
||||||
)
|
)
|
||||||
from app.adapters.network.http import (
|
|
||||||
aclose_shared_async_transports,
|
|
||||||
configure_default_user_agent,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def init_extra():
|
async def init_extra():
|
||||||
@@ -541,7 +543,8 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
|
|||||||
# 停止信号必须先于一切资源释放发出,让工作流、整理等长任务尽早感知停机。
|
# 停止信号必须先于一切资源释放发出,让工作流、整理等长任务尽早感知停机。
|
||||||
LifecycleComponent(
|
LifecycleComponent(
|
||||||
name="停止信号",
|
name="停止信号",
|
||||||
stop=global_vars.stop_system,
|
# 兼容旧测试与插件;GlobalVar 内部已委托到 StopState。
|
||||||
|
stop=getattr(global_vars, "stop_system"),
|
||||||
stop_order=4,
|
stop_order=4,
|
||||||
stop_timeout_seconds=10,
|
stop_timeout_seconds=10,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,18 +4,16 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import eventmanager, Event
|
|
||||||
from app.application.chain.data import get_chain_workflow_port
|
from app.application.chain.data import get_chain_workflow_port
|
||||||
from app.application.workflow import WorkflowExecutionOwner
|
from app.application.workflow import WorkflowExecutionOwner
|
||||||
from app.foundation.reflection import ModuleHelper
|
from app.foundation.reflection import ModuleHelper
|
||||||
from app.runtime.log import logger
|
|
||||||
from app.schemas.workflow import ActionContext
|
|
||||||
from app.schemas.workflow import Action
|
|
||||||
from app.schemas.workflow import ActionResult
|
|
||||||
from app.schemas.workflow import Workflow
|
|
||||||
from app.schemas.types import EventType
|
|
||||||
from app.foundation.singleton import Singleton
|
from app.foundation.singleton import Singleton
|
||||||
|
from app.runtime.config import global_vars
|
||||||
|
from app.runtime.events import Event, eventmanager
|
||||||
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.types import EventType
|
||||||
|
from app.schemas.workflow import Action, ActionContext, ActionResult, Workflow
|
||||||
|
|
||||||
_WORKFLOW_STOP_TIMEOUT_SECONDS = 10.0
|
_WORKFLOW_STOP_TIMEOUT_SECONDS = 10.0
|
||||||
|
|
||||||
@@ -283,7 +281,7 @@ class WorkFlowManager(metaclass=Singleton):
|
|||||||
def _is_cancelled(workflow_id: int, cancel_token: Optional[Any]) -> bool:
|
def _is_cancelled(workflow_id: int, cancel_token: Optional[Any]) -> bool:
|
||||||
if cancel_token and cancel_token.is_cancelled():
|
if cancel_token and cancel_token.is_cancelled():
|
||||||
return True
|
return True
|
||||||
return global_vars.is_workflow_stopped(workflow_id)
|
return runtime_stop_state.is_workflow_stopped(workflow_id)
|
||||||
|
|
||||||
def _sleep_with_cancel(self, workflow_id: int, seconds: float, cancel_token: Optional[Any]) -> None:
|
def _sleep_with_cancel(self, workflow_id: int, seconds: float, cancel_token: Optional[Any]) -> None:
|
||||||
deadline = monotonic() + seconds
|
deadline = monotonic() + seconds
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Any, ClassVar, Union
|
from typing import Any, ClassVar, Union
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.application.configuration import get_configured_system_config
|
from app.application.configuration import get_configured_system_config
|
||||||
from app.schemas.workflow import ActionContext
|
from app.chain import ChainBase
|
||||||
from app.schemas.workflow import ActionParams
|
from app.schemas.workflow import ActionContext, ActionParams, ActionResult
|
||||||
from app.schemas.workflow import ActionResult
|
|
||||||
|
|
||||||
|
|
||||||
class ActionChain(ChainBase):
|
class ActionChain(ChainBase):
|
||||||
|
|||||||
@@ -2,16 +2,14 @@ from typing import Optional
|
|||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from app.workflow.actions import BaseAction
|
|
||||||
from app.chain.download import DownloadChain
|
from app.chain.download import DownloadChain
|
||||||
from app.chain.media import MediaChain
|
from app.chain.media import MediaChain
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.metainfo import MetaInfo
|
from app.domain.metainfo import MetaInfo
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.workflow import ActionParams
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.workflow import ActionContext
|
|
||||||
from app.schemas.workflow import DownloadTask
|
|
||||||
from app.schemas.types import MediaType
|
from app.schemas.types import MediaType
|
||||||
|
from app.schemas.workflow import ActionContext, ActionParams, DownloadTask
|
||||||
|
from app.workflow.actions import BaseAction
|
||||||
|
|
||||||
|
|
||||||
class AddDownloadParams(ActionParams):
|
class AddDownloadParams(ActionParams):
|
||||||
@@ -55,7 +53,7 @@ class AddDownloadAction(BaseAction):
|
|||||||
params = AddDownloadParams(**params)
|
params = AddDownloadParams(**params)
|
||||||
_started = False
|
_started = False
|
||||||
for t in context.torrents:
|
for t in context.torrents:
|
||||||
if global_vars.is_workflow_stopped(workflow_id):
|
if runtime_stop_state.is_workflow_stopped(workflow_id):
|
||||||
break
|
break
|
||||||
# 检查缓存
|
# 检查缓存
|
||||||
cache_key = f"{t.torrent_info.site}-{t.torrent_info.title}"
|
cache_key = f"{t.torrent_info.site}-{t.torrent_info.title}"
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
from app.workflow.actions import BaseAction
|
|
||||||
from app.chain.subscribe import SubscribeChain
|
|
||||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.domain.context import MediaInfo
|
|
||||||
from app.application.chain.data import get_chain_subscribe_port
|
from app.application.chain.data import get_chain_subscribe_port
|
||||||
|
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||||
|
from app.chain.subscribe import SubscribeChain
|
||||||
|
from app.domain.context import MediaInfo
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
from app.schemas.workflow import ActionParams
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.workflow import ActionContext
|
from app.schemas.workflow import ActionContext, ActionParams
|
||||||
|
from app.workflow.actions import BaseAction
|
||||||
|
|
||||||
|
|
||||||
class AddSubscribeParams(ActionParams):
|
class AddSubscribeParams(ActionParams):
|
||||||
@@ -45,7 +44,7 @@ class AddSubscribeAction(BaseAction):
|
|||||||
"""
|
"""
|
||||||
_started = False
|
_started = False
|
||||||
for media in context.medias:
|
for media in context.medias:
|
||||||
if global_vars.is_workflow_stopped(workflow_id):
|
if runtime_stop_state.is_workflow_stopped(workflow_id):
|
||||||
break
|
break
|
||||||
# 检查缓存
|
# 检查缓存
|
||||||
cache_key = f"{media.type}-{media.title}-{media.year}-{media.season}"
|
cache_key = f"{media.type}-{media.title}-{media.year}-{media.season}"
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from app.workflow.actions import BaseAction, ActionChain
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.schemas.workflow import ActionParams
|
|
||||||
from app.schemas.workflow import ActionContext
|
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
|
from app.schemas.workflow import ActionContext, ActionParams
|
||||||
|
from app.workflow.actions import ActionChain, BaseAction
|
||||||
|
|
||||||
|
|
||||||
class FetchDownloadsParams(ActionParams):
|
class FetchDownloadsParams(ActionParams):
|
||||||
@@ -45,7 +44,7 @@ class FetchDownloadsAction(BaseAction):
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
for download in self._downloads:
|
for download in self._downloads:
|
||||||
if global_vars.is_workflow_stopped(workflow_id):
|
if runtime_stop_state.is_workflow_stopped(workflow_id):
|
||||||
break
|
break
|
||||||
logger.info(f"获取下载任务 {download.download_id} 状态 ...")
|
logger.info(f"获取下载任务 {download.download_id} 状态 ...")
|
||||||
torrents = ActionChain().list_torrents(
|
torrents = ActionChain().list_torrents(
|
||||||
|
|||||||
@@ -2,18 +2,16 @@ from typing import List, Optional
|
|||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from app.workflow.actions import BaseAction
|
from app.adapters.network.http import RequestUtils
|
||||||
from app.chain.recommend import RecommendChain
|
|
||||||
from app.application.configuration import get_chain_runtime_config_snapshot
|
from app.application.configuration import get_chain_runtime_config_snapshot
|
||||||
from app.schemas.workflow import ActionParams
|
from app.chain.recommend import RecommendChain
|
||||||
from app.schemas.workflow import ActionContext
|
|
||||||
from app.runtime.config import global_vars
|
|
||||||
from app.runtime.events import eventmanager
|
from app.runtime.events import eventmanager
|
||||||
from app.runtime.log import logger
|
from app.runtime.log import logger
|
||||||
|
from app.runtime.stop import runtime_stop_state
|
||||||
from app.schemas.event import RecommendSourceEventData
|
from app.schemas.event import RecommendSourceEventData
|
||||||
from app.schemas.workflow import MediaInfo
|
|
||||||
from app.schemas.types import ChainEventType
|
from app.schemas.types import ChainEventType
|
||||||
from app.adapters.network.http import RequestUtils
|
from app.schemas.workflow import ActionContext, ActionParams, MediaInfo
|
||||||
|
from app.workflow.actions import BaseAction
|
||||||
|
|
||||||
|
|
||||||
class FetchMediasParams(ActionParams):
|
class FetchMediasParams(ActionParams):
|
||||||
@@ -141,7 +139,7 @@ class FetchMediasAction(BaseAction):
|
|||||||
try:
|
try:
|
||||||
if params.source_type == "ranking":
|
if params.source_type == "ranking":
|
||||||
for api_path in params.sources:
|
for api_path in params.sources:
|
||||||
if global_vars.is_workflow_stopped(workflow_id):
|
if runtime_stop_state.is_workflow_stopped(workflow_id):
|
||||||
break
|
break
|
||||||
source = self.__get_source(api_path)
|
source = self.__get_source(api_path)
|
||||||
if not source:
|
if not source:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user