fix: unify task workspace directory resolution

This commit is contained in:
theshy
2026-04-14 16:44:31 +08:00
parent d5d9693581
commit 055474360e
11 changed files with 192 additions and 56 deletions

View File

@ -4,6 +4,7 @@ import json
from pathlib import Path
from biliup_next.app.retry_meta import retry_meta_for_step
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class ControlPlaneSerializer:
@ -78,7 +79,7 @@ class ControlPlaneSerializer:
task = task or self.state["repo"].get_task(task_id)
if task is None:
return {}
session_dir = Path(str(self.state["settings"]["paths"]["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
source_path = Path(task.source_path)
split_dir = session_dir / "split_video"
@ -246,7 +247,7 @@ class ControlPlaneSerializer:
task = task or self.state["repo"].get_task(task_id)
if task is None:
return None
session_dir = Path(str(self.state["settings"]["paths"]["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
path = session_dir / filename
if not path.exists():
return None

View File

@ -5,6 +5,7 @@ from pathlib import Path
import re
from biliup_next.core.models import ActionRecord, SessionBinding, TaskContext, utc_now_iso
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class SessionDeliveryService:
@ -222,10 +223,10 @@ class SessionDeliveryService:
return None
return bvid
def _full_video_bvid_path(self, task_title: str) -> Path:
session_dir = Path(str(self.settings["paths"]["session_dir"])) / task_title
session_dir.mkdir(parents=True, exist_ok=True)
return session_dir / "full_video_bvid.txt"
def _full_video_bvid_path(self, task) -> Path: # type: ignore[no-untyped-def]
work_dir = resolve_task_work_dir(task)
work_dir.mkdir(parents=True, exist_ok=True)
return work_dir / "full_video_bvid.txt"
def _upsert_session_binding_for_context(self, context: TaskContext, full_video_bvid: str, now: str) -> None:
self.repo.upsert_session_binding(
@ -253,6 +254,6 @@ class SessionDeliveryService:
context.updated_at = now
self.repo.upsert_task_context(context)
self._upsert_session_binding_for_context(context, full_video_bvid, now)
path = self._full_video_bvid_path(task.title)
path = self._full_video_bvid_path(task)
path.write_text(full_video_bvid, encoding="utf-8")
return path

View File

@ -1,9 +1,9 @@
from __future__ import annotations
import shutil
from pathlib import Path
from biliup_next.infra.task_repository import TaskRepository
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class WorkspaceCleanupService:
@ -15,7 +15,7 @@ class WorkspaceCleanupService:
if task is None:
raise RuntimeError(f"task not found: {task_id}")
session_dir = Path(str(settings["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
removed: list[str] = []
skipped: list[str] = []

View File

@ -0,0 +1,10 @@
from __future__ import annotations
from pathlib import Path
def resolve_task_work_dir(task) -> Path: # type: ignore[no-untyped-def]
source = Path(task.source_path).resolve()
if source.is_file() or source.suffix:
return source.parent
return source

View File

@ -11,6 +11,7 @@ from biliup_next.core.models import Task
from biliup_next.core.providers import ProviderManifest
from biliup_next.infra.adapters.bilibili_api import BilibiliApiAdapter
from biliup_next.infra.adapters.full_video_locator import resolve_full_video_bvid
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class BilibiliCollectionProvider:
@ -29,7 +30,7 @@ class BilibiliCollectionProvider:
)
def sync(self, task: Task, target: str, settings: dict[str, Any]) -> dict[str, object]:
session_dir = Path(str(settings["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
cookies = self.bilibili_api.load_cookies(Path(str(settings["cookies_file"])))
csrf = cookies.get("bili_jct")
if not csrf:

View File

@ -11,6 +11,7 @@ from biliup_next.core.models import Task
from biliup_next.core.providers import ProviderManifest
from biliup_next.infra.adapters.bilibili_api import BilibiliApiAdapter
from biliup_next.infra.adapters.full_video_locator import resolve_full_video_bvid
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class BilibiliTopCommentProvider:
@ -28,7 +29,7 @@ class BilibiliTopCommentProvider:
)
def comment(self, task: Task, settings: dict[str, Any]) -> dict[str, object]:
session_dir = Path(str(settings["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
songs_path = session_dir / "songs.txt"
songs_json_path = session_dir / "songs.json"
bvid_path = session_dir / "bvid.txt"
@ -164,14 +165,13 @@ class BilibiliTopCommentProvider:
def _build_split_comment(self, task: Task, settings: dict[str, Any]) -> tuple[str, str | None]:
repo = settings.get("__repo")
session_dir_root = Path(str(settings["session_dir"]))
if repo is None or not hasattr(repo, "get_task_context") or not hasattr(repo, "list_task_contexts_by_session_key"):
session_dir = session_dir_root / task.title
session_dir = resolve_task_work_dir(task)
return self._build_split_comment_content(session_dir / "songs.json", session_dir / "songs.txt"), None
context = repo.get_task_context(task.id)
if context is None or not context.session_key or context.session_key.startswith("task:"):
session_dir = session_dir_root / task.title
session_dir = resolve_task_work_dir(task)
return self._build_split_comment_content(session_dir / "songs.json", session_dir / "songs.txt"), None
ordered_contexts = self._ordered_session_contexts(repo, context.session_key)
@ -183,7 +183,10 @@ class BilibiliTopCommentProvider:
blocks: list[str] = []
for index, session_context in enumerate(ordered_contexts, start=1):
task_dir = session_dir_root / session_context.task_id
session_task = repo.get_task(session_context.task_id)
if session_task is None:
continue
task_dir = resolve_task_work_dir(session_task)
content = self._build_split_comment_content(task_dir / "songs.json", task_dir / "songs.txt")
if not content:
continue
@ -195,13 +198,13 @@ class BilibiliTopCommentProvider:
def _build_full_comment_content(self, task: Task, settings: dict[str, Any]) -> tuple[str, str | None]:
repo = settings.get("__repo")
if repo is None or not hasattr(repo, "get_task_context") or not hasattr(repo, "list_task_contexts_by_session_key"):
session_dir = Path(str(settings["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
content = session_dir.joinpath("songs.txt").read_text(encoding="utf-8").strip()
return content, None if content else "timeline_comment_empty"
context = repo.get_task_context(task.id)
if context is None or not context.session_key or context.session_key.startswith("task:"):
session_dir = Path(str(settings["session_dir"])) / task.title
session_dir = resolve_task_work_dir(task)
content = session_dir.joinpath("songs.txt").read_text(encoding="utf-8").strip()
return content, None if content else "timeline_comment_empty"
@ -214,7 +217,10 @@ class BilibiliTopCommentProvider:
blocks: list[str] = []
for index, session_context in enumerate(ordered_contexts, start=1):
task_dir = Path(str(settings["session_dir"])) / session_context.task_id
session_task = repo.get_task(session_context.task_id)
if session_task is None:
continue
task_dir = resolve_task_work_dir(session_task)
songs_path = task_dir / "songs.txt"
if not songs_path.exists():
continue

View File

@ -11,6 +11,7 @@ from biliup_next.core.errors import ModuleError
from biliup_next.core.models import PublishRecord, Task, utc_now_iso
from biliup_next.core.providers import ProviderManifest
from biliup_next.infra.adapters.biliup_cli import BiliupCliAdapter
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class BiliupCliPublishProvider:
@ -28,7 +29,7 @@ class BiliupCliPublishProvider:
)
def publish(self, task: Task, clip_videos: list, settings: dict[str, Any]) -> PublishRecord:
work_dir = Path(str(settings["session_dir"])) / task.title
work_dir = resolve_task_work_dir(task)
bvid_file = work_dir / "bvid.txt"
upload_done = work_dir / "upload_done.flag"
publish_log = work_dir / "publish.log"

View File

@ -8,6 +8,7 @@ from typing import Any
from biliup_next.core.models import Artifact, PublishRecord, TaskContext, utc_now_iso
from biliup_next.core.registry import Registry
from biliup_next.infra.task_repository import TaskRepository
from biliup_next.infra.workspace_paths import resolve_task_work_dir
class PublishService:
@ -26,7 +27,7 @@ class PublishService:
if len(session_contexts) <= 1:
clip_videos = self._clip_videos_for_task(task_id)
record = provider.publish(task, clip_videos, settings)
self._persist_publish_success(task_id, task.title, record, settings)
self._persist_publish_success(task, record)
return record
anchor_context = session_contexts[0]
@ -41,7 +42,7 @@ class PublishService:
title=task.title,
published_at=utc_now_iso(),
)
self._persist_publish_success(task_id, task.title, record, settings)
self._persist_publish_success(task, record)
return record
clip_videos = self._session_clip_videos(session_contexts)
@ -64,7 +65,7 @@ class PublishService:
title=record.title,
published_at=record.published_at,
)
self._persist_publish_success(context.task_id, session_task.title, session_record, settings)
self._persist_publish_success(session_task, session_record)
return PublishRecord(
id=None,
task_id=task_id,
@ -75,12 +76,13 @@ class PublishService:
published_at=record.published_at,
)
def _persist_publish_success(self, task_id: str, task_title: str, record: PublishRecord, settings: dict[str, object]) -> None:
def _persist_publish_success(self, task, record: PublishRecord) -> None: # type: ignore[no-untyped-def]
task_id = task.id
self.repo.add_publish_record(record)
if record.bvid:
session_dir = Path(str(settings.get("session_dir", "session"))) / task_title
session_dir.mkdir(parents=True, exist_ok=True)
bvid_path_obj = session_dir / "bvid.txt"
work_dir = resolve_task_work_dir(task)
work_dir.mkdir(parents=True, exist_ok=True)
bvid_path_obj = work_dir / "bvid.txt"
bvid_path_obj.write_text(record.bvid, encoding="utf-8")
self.repo.add_artifact(
Artifact(
@ -120,12 +122,11 @@ class PublishService:
return aggregated
def _shared_session_bvid(self, contexts: list[TaskContext], settings: dict[str, object]) -> str | None:
session_dir_root = Path(str(settings.get("session_dir", "session")))
for context in contexts:
task = self.repo.get_task(context.task_id)
if task is None:
continue
bvid_path = session_dir_root / task.title / "bvid.txt"
bvid_path = resolve_task_work_dir(task) / "bvid.txt"
if bvid_path.exists():
bvid = bvid_path.read_text(encoding="utf-8").strip()
if bvid.startswith("BV"):
@ -138,8 +139,7 @@ class PublishService:
contexts: list[TaskContext],
settings: dict[str, object],
) -> dict[str, Any]: # type: ignore[no-untyped-def]
session_dir_root = Path(str(settings.get("session_dir", "session")))
anchor_work_dir = (session_dir_root / anchor_task.title).resolve()
anchor_work_dir = resolve_task_work_dir(anchor_task)
anchor_work_dir.mkdir(parents=True, exist_ok=True)
aggregate_txt_lines: list[str] = []
aggregate_songs: list[dict[str, object]] = []
@ -148,7 +148,7 @@ class PublishService:
task = self.repo.get_task(context.task_id)
if task is None:
continue
task_work_dir = (session_dir_root / task.title).resolve()
task_work_dir = resolve_task_work_dir(task)
songs_txt = task_work_dir / "songs.txt"
songs_json = task_work_dir / "songs.json"