Updated folder structure (for Docker)

This commit is contained in:
2025-12-09 05:35:59 +01:00
parent 6940c76e58
commit ec7d2d623f
108 changed files with 0 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
"""Tools module - filesystem and API tools for the agent."""
from .api import (
add_torrent_by_index,
add_torrent_to_qbittorrent,
find_media_imdb_id,
find_torrent,
get_torrent_by_index,
)
from .filesystem import list_folder, set_path_for_folder
from .language import set_language
__all__ = [
"set_path_for_folder",
"list_folder",
"find_media_imdb_id",
"find_torrent",
"get_torrent_by_index",
"add_torrent_to_qbittorrent",
"add_torrent_by_index",
"set_language",
]
+196
View File
@@ -0,0 +1,196 @@
"""API tools for interacting with external services."""
import logging
from typing import Any
from application.movies import SearchMovieUseCase
from application.torrents import AddTorrentUseCase, SearchTorrentsUseCase
from infrastructure.api.knaben import knaben_client
from infrastructure.api.qbittorrent import qbittorrent_client
from infrastructure.api.tmdb import tmdb_client
from infrastructure.persistence import get_memory
logger = logging.getLogger(__name__)
def find_media_imdb_id(media_title: str) -> dict[str, Any]:
"""
Find the IMDb ID for a given media title using TMDB API.
Args:
media_title: Title of the media to search for.
Returns:
Dict with IMDb ID and media info, or error details.
"""
use_case = SearchMovieUseCase(tmdb_client)
response = use_case.execute(media_title)
result = response.to_dict()
if result.get("status") == "ok":
memory = get_memory()
memory.stm.set_entity(
"last_media_search",
{
"title": result.get("title"),
"imdb_id": result.get("imdb_id"),
"media_type": result.get("media_type"),
"tmdb_id": result.get("tmdb_id"),
},
)
memory.stm.set_topic("searching_media")
logger.debug(f"Stored media search result in STM: {result.get('title')}")
return result
def find_torrent(media_title: str) -> dict[str, Any]:
"""
Find torrents for a given media title using Knaben API.
Results are stored in episodic memory so the user can reference them
by index (e.g., "download the 3rd one").
Args:
media_title: Title of the media to search for.
Returns:
Dict with torrent list or error details.
"""
logger.info(f"Searching torrents for: {media_title}")
use_case = SearchTorrentsUseCase(knaben_client)
response = use_case.execute(media_title, limit=10)
result = response.to_dict()
if result.get("status") == "ok":
memory = get_memory()
torrents = result.get("torrents", [])
memory.episodic.store_search_results(
query=media_title, results=torrents, search_type="torrent"
)
memory.stm.set_topic("selecting_torrent")
logger.info(f"Stored {len(torrents)} torrent results in episodic memory")
return result
def get_torrent_by_index(index: int) -> dict[str, Any]:
"""
Get a torrent from the last search results by its index.
Allows the user to reference results by number after a search.
Args:
index: 1-based index of the torrent in the search results.
Returns:
Dict with torrent data or error if not found.
"""
logger.info(f"Getting torrent at index: {index}")
memory = get_memory()
if memory.episodic.last_search_results:
results_count = len(memory.episodic.last_search_results.get("results", []))
query = memory.episodic.last_search_results.get("query", "unknown")
logger.debug(f"Episodic memory has {results_count} results from: {query}")
else:
logger.warning("No search results in episodic memory")
result = memory.episodic.get_result_by_index(index)
if result:
logger.info(f"Found torrent at index {index}: {result.get('name', 'unknown')}")
return {"status": "ok", "torrent": result}
logger.warning(f"No torrent found at index {index}")
return {
"status": "error",
"error": "not_found",
"message": f"No torrent found at index {index}. Search for torrents first.",
}
def add_torrent_to_qbittorrent(magnet_link: str) -> dict[str, Any]:
"""
Add a torrent to qBittorrent using a magnet link.
Args:
magnet_link: Magnet link of the torrent to add.
Returns:
Dict with success status or error details.
"""
logger.info("Adding torrent to qBittorrent")
use_case = AddTorrentUseCase(qbittorrent_client)
response = use_case.execute(magnet_link)
result = response.to_dict()
if result.get("status") == "ok":
memory = get_memory()
last_search = memory.episodic.get_search_results()
torrent_name = "Unknown"
if last_search:
for t in last_search.get("results", []):
if t.get("magnet") == magnet_link:
torrent_name = t.get("name", "Unknown")
break
memory.episodic.add_active_download(
{
"task_id": magnet_link[:20],
"name": torrent_name,
"magnet": magnet_link,
"progress": 0,
"status": "queued",
}
)
memory.stm.set_topic("downloading")
memory.stm.end_workflow()
logger.info(f"Added download to episodic memory: {torrent_name}")
return result
def add_torrent_by_index(index: int) -> dict[str, Any]:
"""
Add a torrent from the last search results by its index.
Combines get_torrent_by_index and add_torrent_to_qbittorrent.
Args:
index: 1-based index of the torrent in the search results.
Returns:
Dict with success status or error details.
"""
logger.info(f"Adding torrent by index: {index}")
torrent_result = get_torrent_by_index(index)
if torrent_result.get("status") != "ok":
return torrent_result
torrent = torrent_result.get("torrent", {})
magnet = torrent.get("magnet")
if not magnet:
logger.error("Torrent has no magnet link")
return {
"status": "error",
"error": "no_magnet",
"message": "The selected torrent has no magnet link",
}
logger.info(f"Adding torrent: {torrent.get('name', 'unknown')}")
result = add_torrent_to_qbittorrent(magnet)
if result.get("status") == "ok":
result["torrent_name"] = torrent.get("name", "Unknown")
return result
+40
View File
@@ -0,0 +1,40 @@
"""Filesystem tools for folder management."""
from typing import Any
from application.filesystem import ListFolderUseCase, SetFolderPathUseCase
from infrastructure.filesystem import FileManager
def set_path_for_folder(folder_name: str, path_value: str) -> dict[str, Any]:
"""
Set a folder path in the configuration.
Args:
folder_name: Name of folder to set (download, tvshow, movie, torrent).
path_value: Absolute path to the folder.
Returns:
Dict with status or error information.
"""
file_manager = FileManager()
use_case = SetFolderPathUseCase(file_manager)
response = use_case.execute(folder_name, path_value)
return response.to_dict()
def list_folder(folder_type: str, path: str = ".") -> dict[str, Any]:
"""
List contents of a configured folder.
Args:
folder_type: Type of folder to list (download, tvshow, movie, torrent).
path: Relative path within the folder (default: root).
Returns:
Dict with folder contents or error information.
"""
file_manager = FileManager()
use_case = ListFolderUseCase(file_manager)
response = use_case.execute(folder_type, path)
return response.to_dict()
+35
View File
@@ -0,0 +1,35 @@
"""Language management tools for the agent."""
import logging
from typing import Any
from infrastructure.persistence import get_memory
logger = logging.getLogger(__name__)
def set_language(language: str) -> dict[str, Any]:
"""
Set the conversation language.
Args:
language: Language code (e.g., 'en', 'fr', 'es', 'de')
Returns:
Status dictionary
"""
try:
memory = get_memory()
memory.stm.set_language(language)
memory.save()
logger.info(f"Language set to: {language}")
return {
"status": "ok",
"message": f"Language set to {language}",
"language": language,
}
except Exception as e:
logger.error(f"Failed to set language: {e}")
return {"status": "error", "error": str(e)}