correctly handle missing/incorrect prowlarr settings

This commit is contained in:
Markbeep
2025-04-13 17:48:54 +02:00
parent 2a5bbd2417
commit abb64f88b8
3 changed files with 79 additions and 26 deletions

View File

@@ -6,6 +6,7 @@ from typing import Any, Literal, Optional
from urllib.parse import urlencode
from aiohttp import ClientResponse, ClientSession
from pydantic import BaseModel
from sqlmodel import Session
from app.internal.indexers.abstract import SessionContainer
@@ -246,37 +247,75 @@ async def query_prowlarr(
return sources
class IndexerResponse(BaseModel):
indexers: dict[int, Indexer] = {}
state: Literal["ok", "missingUrlKey", "failedFetch"]
error: Optional[str] = None
@property
def json_string(self) -> str:
return json.dumps(
{id: indexer.model_dump() for id, indexer in self.indexers.items()}
)
@property
def ok(self) -> bool:
return self.state == "ok"
async def get_indexers(
session: Session, client_session: ClientSession
) -> dict[int, Indexer]:
) -> IndexerResponse:
"""Fetch the list of all indexers from Prowlarr."""
base_url = prowlarr_config.get_base_url(session)
api_key = prowlarr_config.get_api_key(session)
assert base_url is not None and api_key is not None
source_ttl = prowlarr_config.get_source_ttl(session)
if not base_url or not api_key:
logger.warning("Prowlarr base url or api key not set, skipping indexer fetch")
return IndexerResponse(
state="failedFetch",
error="Missing Prowlarr base url or api key",
)
indexers = prowlarr_indexer_cache.get_all(source_ttl).values()
if len(indexers) > 0:
return {indexer.id: indexer for indexer in indexers}
return IndexerResponse(
indexers={indexer.id: indexer for indexer in indexers},
state="ok",
)
url = posixpath.join(base_url, "api/v1/indexer")
logger.info("Fetching indexers from Prowlarr: %s", url)
async with client_session.get(
url,
headers={"X-Api-Key": api_key},
) as response:
if not response.ok:
logger.error("Failed to fetch indexers: %s", response)
return {}
try:
async with client_session.get(
url,
headers={"X-Api-Key": api_key},
) as response:
if not response.ok:
logger.error("Failed to fetch indexers: %s", response)
return IndexerResponse(
state="failedFetch",
error=f"{response.status}: {response.reason}",
)
json_response = await response.json()
json_response = await response.json()
for indexer in json_response:
indexer_obj = Indexer.model_validate(indexer)
prowlarr_indexer_cache.set(indexer_obj, str(indexer_obj.id))
for indexer in json_response:
indexer_obj = Indexer.model_validate(indexer)
prowlarr_indexer_cache.set(indexer_obj, str(indexer_obj.id))
return {
indexer.id: indexer
for indexer in prowlarr_indexer_cache.get_all(source_ttl).values()
}
return IndexerResponse(
indexers={
indexer.id: indexer
for indexer in prowlarr_indexer_cache.get_all(source_ttl).values()
},
state="ok",
)
except Exception as e:
logger.error("Failed to access Prowlarr to fetch indexers: %s", e)
return IndexerResponse(
state="failedFetch",
error=str(e),
)

View File

@@ -223,7 +223,6 @@ async def read_prowlarr(
prowlarr_api_key = prowlarr_config.get_api_key(session)
selected = set(prowlarr_config.get_categories(session))
indexers = await get_indexers(session, client_session)
indexers = {id: indexer.model_dump() for id, indexer in indexers.items()}
selected_indexers = set(prowlarr_config.get_indexers(session))
return template_response(
@@ -236,7 +235,7 @@ async def read_prowlarr(
"prowlarr_api_key": prowlarr_api_key,
"indexer_categories": indexer_categories,
"selected_categories": selected,
"indexers": json.dumps(indexers),
"indexers": indexers,
"selected_indexers": selected_indexers,
"prowlarr_misconfigured": True if prowlarr_misconfigured else False,
},
@@ -252,6 +251,7 @@ def update_prowlarr_api_key(
],
):
prowlarr_config.set_api_key(session, api_key)
flush_prowlarr_cache()
return Response(status_code=204, headers={"HX-Refresh": "true"})
@@ -264,6 +264,7 @@ def update_prowlarr_base_url(
],
):
prowlarr_config.set_base_url(session, base_url)
flush_prowlarr_cache()
return Response(status_code=204, headers={"HX-Refresh": "true"})
@@ -306,7 +307,6 @@ async def update_selected_indexers(
prowlarr_config.set_indexers(session, indexer_ids)
indexers = await get_indexers(session, client_session)
indexers = {id: indexer.model_dump() for id, indexer in indexers.items()}
selected_indexers = set(prowlarr_config.get_indexers(session))
flush_prowlarr_cache()
@@ -315,9 +315,9 @@ async def update_selected_indexers(
request,
admin_user,
{
"indexers": json.dumps(indexers),
"indexers": indexers,
"selected_indexers": selected_indexers,
"success": "Categories updated",
"success": "Indexers updated",
},
block_name="indexer",
)

View File

@@ -110,18 +110,30 @@
<form
id="indexer-select"
class="flex flex-col gap-1"
x-data="{ selectedIndexers: [{{ selected_indexers|join(',') }}].sort(), indexers: {{ indexers }}, dirty: false }"
x-data="{ selectedIndexers: [{{ selected_indexers|join(',') }}].sort(), indexers: {{ indexers.json_string }}, dirty: false }"
hx-put="{{base_url}}/settings/prowlarr/indexers"
hx-disabled-elt="#indexer-submit-button"
hx-target="this"
hx-swap="outerHTML"
>
{% if success %}
<script>
toast("{{success|safe}}", "success");
</script>
{% endif %}
<label for="indexers">Indexers</label>
<p class="text-xs opacity-60">
Select the indexers to use with Prowlarr. If none are selected, all
indexers will be used.
indexers will be used. {% if not indexers.ok %}
<span class="text-error font-semibold">
<br />
No indexers found. {{ indexers.error or "" }}
</span>
{% endif %}
</p>
{% if indexers.ok %}
<div class="flex flex-wrap gap-1">
<template x-for="indexerId in selectedIndexers">
<div
@@ -139,8 +151,10 @@
</div>
</template>
</div>
{% endif %}
<select name="group" class="select w-full">
<!-- prettier-ignore -->
<select name="group" class="select w-full" {% if not indexers.ok %}disabled{% endif %}>
<template x-for="indexerId in Object.keys(indexers)">
<template x-if="!selectedIndexers.includes(indexerId)">
<option