Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/myanonamouse-enrichment.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ Compare it with the IP listed next to the session on MAM's security page.

## How it works

After a Prowlarr search, Shelfmark sends the same query text to MAM's JSON search API (normally one request) and matches torrents back to Prowlarr's results by their MAM torrent ID. Lookups are cached for an hour, stay inside the Prowlarr search time budget, and never fail the search: if MAM errors or rejects the session, the release list simply shows without the extra columns filled in.
After a Prowlarr search, Shelfmark reruns the search Prowlarr sent to MyAnonamouse's JSON search API: the same cleaned-up query text, the same categories (audiobooks, or e-books), and your MyAnonamouse indexer's own options from Prowlarr (search type, search in description/series/filenames, languages). That normally returns the same torrents in one request per title. Shelfmark then matches them back to Prowlarr's results by their MAM torrent ID. Only results from the MyAnonamouse indexer are looked up, and the session ID is only ever sent to `https://www.myanonamouse.net`.

If some torrents are missing from the first page, Shelfmark reads further pages, up to 4 requests per search. Lookups are cached for an hour, stay inside the Prowlarr search time budget, and never fail the search: if MAM errors or rejects the session, the release list simply shows without the extra columns filled in.

After a failed request (a 403, a timeout, an unexpected reply), Shelfmark waits before trying MAM again: 1 minute, then 2, 4 and so on, up to 30 minutes. After 10 failures in a row it stops using MAM. **Test MAM Session** (once it succeeds), a new session ID, or a restart turns it back on. Each failure is logged with the reason.
26 changes: 16 additions & 10 deletions shelfmark/release_sources/prowlarr/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ def _normalize_json_object_list(payload: object, *, context: str) -> list[dict[s
return [_normalize_json_object(item, context=context) for item in payload]


def _get_field_value(fields: object, name: str) -> object | None:
def get_indexer_field(fields: object, name: str) -> object | None:
"""Return a setting's value from a Prowlarr indexer's "fields" list."""
if not isinstance(fields, list):
return None

Expand All @@ -120,6 +121,17 @@ def _get_field_value(fields: object, name: str) -> object | None:
return None


def is_myanonamouse_indexer(indexer: Mapping[str, Any]) -> bool:
"""Whether a Prowlarr indexer record is the MyAnonamouse implementation."""
implementation = str(
indexer.get("implementation")
or indexer.get("implementationName")
or indexer.get("definitionName")
or ""
)
return implementation.strip().lower() == "myanonamouse"


class ProwlarrClient:
"""Client for interacting with the Prowlarr API."""

Expand Down Expand Up @@ -300,14 +312,8 @@ def get_enriched_indexer_ids(
if restrict_to is not None and idx_id_int not in restrict_to:
continue

impl = str(
idx.get("implementation")
or idx.get("implementationName")
or idx.get("definitionName")
or ""
)
# Currently only MyAnonamouse provides consistently rich Torznab metadata.
if impl.strip().lower() == "myanonamouse":
if is_myanonamouse_indexer(idx):
enriched_ids.append(idx_id_int)

return enriched_ids
Expand Down Expand Up @@ -339,9 +345,9 @@ def get_indexer_seed_settings(
continue

fields = idx.get("fields")
ratio_limit = coerce_float_like(_get_field_value(fields, _INDEXER_FIELD_SEED_RATIO))
ratio_limit = coerce_float_like(get_indexer_field(fields, _INDEXER_FIELD_SEED_RATIO))
seeding_time_limit = coerce_int_like(
_get_field_value(fields, _INDEXER_FIELD_SEED_TIME_MINUTES)
get_indexer_field(fields, _INDEXER_FIELD_SEED_TIME_MINUTES)
)

settings: IndexerSeedSettings = {}
Expand Down
Loading
Loading