20a8987dd9
Adds 9 read-only tools across three new services: - Lidarr: queue, artist_search, library_stats (music library awareness) - Whisparr: queue, series_search, library_stats (adult content, v3 API) - Overseerr: search, requests, request_counts (cross-library availability, request tracking via X-Api-Key header reusing arr_get helper) Fix _common.py urlencode to use %20 instead of + for query values — Overseerr rejects + as reserved. Safe for all arr services. Pirate catalog: 11 → 20 tools.
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
"""Whisparr tools — adult library queries (read-only).
|
|
|
|
Whisparr V2 uses a Sonarr-style series/episode model with `/api/v3/` paths.
|
|
"""
|
|
|
|
from ._common import arr_get
|
|
|
|
|
|
def _compact_series(s):
|
|
st = s.get("statistics", {}) or {}
|
|
return {
|
|
"id": s.get("id"),
|
|
"title": s.get("title"),
|
|
"year": s.get("year"),
|
|
"status": s.get("status"),
|
|
"monitored": s.get("monitored"),
|
|
"episode_count": st.get("episodeCount"),
|
|
"episode_file_count": st.get("episodeFileCount"),
|
|
"size_on_disk_gb": round((st.get("sizeOnDisk", 0) or 0) / 1e9, 1),
|
|
}
|
|
|
|
|
|
def whisparr_queue(limit=20):
|
|
"""Adult content currently downloading or pending import."""
|
|
data = arr_get("whisparr", "/api/v3/queue", {"pageSize": limit, "includeSeries": "true"})
|
|
items = []
|
|
for r in data.get("records", []):
|
|
items.append({
|
|
"title": r.get("title"),
|
|
"series": r.get("series", {}).get("title") if r.get("series") else None,
|
|
"status": r.get("status"),
|
|
"timeleft": r.get("timeleft"),
|
|
"size_gb": round((r.get("size", 0) or 0) / 1e9, 2),
|
|
"size_left_gb": round((r.get("sizeleft", 0) or 0) / 1e9, 2),
|
|
"download_client": r.get("downloadClient"),
|
|
})
|
|
return {"total_records": data.get("totalRecords", 0), "items": items}
|
|
|
|
|
|
def whisparr_series_search(query):
|
|
"""Look up a title in the user's Whisparr library by partial match.
|
|
Does NOT search indexers — only what's already tracked."""
|
|
all_series = arr_get("whisparr", "/api/v3/series")
|
|
q = query.lower().strip()
|
|
hits = [s for s in all_series if q in (s.get("title") or "").lower()]
|
|
return {"query": query, "count": len(hits), "series": [_compact_series(s) for s in hits[:20]]}
|
|
|
|
|
|
def whisparr_library_stats():
|
|
"""Summary stats of the Whisparr adult library."""
|
|
all_series = arr_get("whisparr", "/api/v3/series")
|
|
total_eps = total_on_disk = total_size = 0
|
|
for s in all_series:
|
|
st = s.get("statistics", {}) or {}
|
|
total_eps += st.get("episodeCount", 0) or 0
|
|
total_on_disk += st.get("episodeFileCount", 0) or 0
|
|
total_size += st.get("sizeOnDisk", 0) or 0
|
|
return {
|
|
"series_count": len(all_series),
|
|
"episode_total": total_eps,
|
|
"episodes_on_disk": total_on_disk,
|
|
"total_size_gb": round(total_size / 1e9, 1),
|
|
}
|
|
|
|
|
|
TOOLS = [
|
|
{
|
|
"name": "whisparr_queue",
|
|
"description": "List adult content Whisparr is currently downloading or importing.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {"limit": {"type": "integer", "default": 20}},
|
|
},
|
|
"read_only": True,
|
|
"fn": whisparr_queue,
|
|
},
|
|
{
|
|
"name": "whisparr_series_search",
|
|
"description": "Search the user's Whisparr library by partial title match. Does NOT search indexers.",
|
|
"input_schema": {
|
|
"type": "object",
|
|
"properties": {"query": {"type": "string"}},
|
|
"required": ["query"],
|
|
},
|
|
"read_only": True,
|
|
"fn": whisparr_series_search,
|
|
},
|
|
{
|
|
"name": "whisparr_library_stats",
|
|
"description": "High-level stats about the Whisparr adult library: title count, episodes on disk, total size.",
|
|
"input_schema": {"type": "object", "properties": {}},
|
|
"read_only": True,
|
|
"fn": whisparr_library_stats,
|
|
},
|
|
]
|