diff --git a/app/core/config.py b/app/core/config.py index cdbba08a..a6fc9634 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -575,7 +575,7 @@ class ConfigModel(BaseModel): # LLM Base URL 预设标识,用于区分同一 Base URL 下的不同模型目录 LLM_BASE_URL_PRESET: Optional[str] = None # LLM最大上下文Token数量(K) - LLM_MAX_CONTEXT_TOKENS: int = 64 + LLM_MAX_CONTEXT_TOKENS: int = 128 # LLM OpenAI兼容接口请求User-Agent LLM_USER_AGENT: Optional[str] = None # LLM温度参数 diff --git a/skills/organize-files/SKILL.md b/skills/organize-files/SKILL.md new file mode 100644 index 00000000..fa496f80 --- /dev/null +++ b/skills/organize-files/SKILL.md @@ -0,0 +1,147 @@ +--- +name: organize-files +version: 1 +description: >- + Use this skill when the user asks the MoviePilot agent to identify and organize downloaded/local media files that automatic transfer cannot handle. Typical triggers include: manually organize a file or folder, organize unrecognized downloads, fix files stuck in a download directory, identify a messy episode pack, move/copy/link files into the library, or organize files by explicit TMDB/Douban ID. If the user gives failed transfer history IDs, prefer transfer-failed-retry instead. +allowed-tools: list_directory query_directory_settings query_download_tasks query_transfer_history delete_transfer_history recognize_media search_media query_media_detail query_library_exists transfer_file ask_user_choice send_message +--- + +# Organize Files (智能整理文件) + +Use this skill to help the user identify media files that MoviePilot could not organize automatically, then call the normal transfer pipeline through `transfer_file`. Do not rename, move, or copy files manually; let MoviePilot's directory, transfer mode, rename template, overwrite, scrape, and notification settings handle the actual organization. + +## MoviePilot Transfer Flow + +MoviePilot's normal flow is: + +1. `DownloadChain.download_single` adds a downloader task, records `DownloadHistory` and `DownloadFiles`, runs downloader-specific `download_added`, then sends `DownloadAdded`. +2. `TransferChain.process` scans completed downloader tasks in monitored download directories. If a `DownloadHistory` exists for the hash, it reuses the recorded media IDs; otherwise it falls back to path recognition. +3. Agent/manual organization calls `transfer_file`, which enters `TransferFileTool` -> `TransferChain.manual_transfer` -> `TransferChain.do_transfer`. +4. `do_transfer` recursively collects eligible media/subtitle/audio files, ignores recycle/hidden paths and configured exclude words, resolves download history when possible, builds `MetaInfoPath`, then either uses explicit media info or calls `MediaChain.recognize_by_meta`. +5. `TransferChain.__handle_transfer` chooses the target directory through `DirectoryHelper`, delegates file operations to the file manager module, and lets `TransHandler` build the final target path and name. +6. The callback writes `TransferHistory` success/failure records, emits transfer events, sends notifications, and may trigger `transfer-failed-retry` for failed history records. + +Important implication: an existing `TransferHistory` for the same source path can make a later transfer skip. Delete only stale or failed history records, and only after the user has confirmed the record is safe to remove. + +## Workflow + +### 1. Classify The Request + +- If the user provides one or more failed transfer history IDs, stop and use `transfer-failed-retry`. +- If the user provides a path, start from that path. +- If the user describes a download task, use `query_download_tasks` to find its save path or hash, then continue with the path. +- If the user only says "整理一下下载目录", use `query_directory_settings(directory_type="download")` first, then ask which directory or subdirectory to process if more than one candidate exists. + +### 2. Inspect Candidate Files + +Use `list_directory` for any directory the user provides. Prefer `sort_by="time"` for "recent" or "刚下载的" requests. + +For directories with more than 20 items, ask the user to narrow the folder or choose the relevant child directory before running transfers. Avoid organizing a broad shared download root unless the user explicitly confirms the scope. + +Treat these as transfer candidates: + +- main media files and Blu-ray folders; +- matching subtitle and external audio files in the same media folder; +- episode packs where files share the same title/season pattern. + +Skip obvious samples, trailers, screenshots, hidden folders, recycle folders, and files that are not media/subtitle/audio. + +### 3. Identify The Media + +For the best sample file, call: + +```text +recognize_media(path="") +``` + +If recognition fails or looks wrong: + +1. Extract likely title, year, media type, season, and episode range from filenames. +2. Call `search_media(title="...", year="...", media_type="movie|tv")`. +3. If several results are plausible, use `ask_user_choice` when available, or ask the user directly to choose the correct title/TMDB ID. +4. For TV season confusion, use `query_media_detail(tmdb_id=, media_type="tv")` before deciding the season number. + +Never invent a TMDB/Douban ID. When unsure, ask for confirmation. + +### 4. Check Existing State + +Before writing: + +- Use `query_library_exists` when a precise `tmdb_id` and `media_type` are known and duplicate risk matters. +- Use `query_transfer_history(title="", status="all")` if the file may already have a success or failure record. +- If `transfer_file` later returns "已整理过", query transfer history, identify the matching source path, and ask before deleting the stale record. + +Only call `delete_transfer_history(history_id=<id>)` for the exact stale/failed record that blocks the requested source path. Do not delete unrelated successful history. + +### 5. Transfer Through MoviePilot + +Use `transfer_file` with explicit identity whenever possible: + +```text +transfer_file( + file_path="<source path>", + storage="local", + media_type="movie|tv", + tmdbid=<tmdb_id>, + season=<season_number_if_tv> +) +``` + +Rules: + +- For directories, pass a trailing slash in `file_path` so the tool treats it as a directory. +- Prefer leaving `target_path`, `target_storage`, and `transfer_type` empty so configured directory rules apply. +- Set `target_path` or `transfer_type` only when the user explicitly asks or the default directory configuration cannot handle the file. +- For a single movie or a single TV season folder, transfer the folder once with the shared identity. +- For mixed folders, split by media and transfer each file/subfolder separately. +- For episode packs, identify the media once, then reuse `tmdbid`, `media_type="tv"`, and the confirmed `season` for each item. + +### 6. Report Clearly + +After each transfer batch, report: + +- source path(s) processed; +- recognized media title, type, TMDB/Douban ID, season/episode range when relevant; +- success/failure count; +- any failed message exactly enough for the user to act, such as missing media library directory, unsupported storage, existing history, or no media recognized. + +If the result creates failed history records, tell the user they can retry with the history ID or let the agent continue with `transfer-failed-retry`. + +## Common Cases + +### User Gives A Single File + +1. `recognize_media(path=...)` +2. If needed, `search_media(...)` and confirm the result. +3. `transfer_file(file_path=..., media_type=..., tmdbid=..., season=...)` + +### User Gives A Season Folder + +1. `list_directory(path=...)` +2. Pick a representative episode and run `recognize_media(path=...)`. +3. Confirm `tmdbid`, `media_type="tv"`, and season. +4. `transfer_file(file_path="<folder>/", media_type="tv", tmdbid=<id>, season=<season>)` + +### User Gives A Messy Mixed Folder + +1. `list_directory(path=...)` +2. Group candidates by likely title/year/season. +3. Confirm groups before writing if there is more than one media. +4. Transfer each group separately; do not run one directory transfer over unrelated media. + +### Transfer Says The File Was Already Organized + +1. `query_transfer_history(title="<title or source path keyword>", status="all")` +2. Find the exact record with matching `src`. +3. Ask the user to confirm deletion if the record is stale or failed. +4. `delete_transfer_history(history_id=<id>)` +5. Retry `transfer_file(...)`. + +## Guardrails + +- Do not use shell commands, raw database edits, or manual filesystem moves for organization. +- Do not delete transfer history without an exact matching source path and user confirmation. +- Do not use broad download roots as transfer targets unless the user explicitly confirms the scope. +- Do not process unrelated media in one directory transfer. +- Do not override target directories or transfer modes unless necessary. +- Prefer asking one focused question over guessing media identity, season mapping, or destructive cleanup.