Skip to content

Commit 0390ab6

Browse files
authored
Merge pull request #161 from marksie1988/158-add-release-push
feat: added ability to create alias method names & added release/push to Radarr
2 parents 8f7b030 + 3a18ee6 commit 0390ab6

File tree

7 files changed

+157
-5
lines changed

7 files changed

+157
-5
lines changed

pyarr/lib/__init__.py

Whitespace-only changes.

pyarr/lib/alias_decorator.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import functools
2+
from typing import Any, Callable, Dict, Optional, Set
3+
import warnings
4+
5+
6+
class FunctionWrapper:
7+
"""Function wrapper"""
8+
9+
def __init__(self, func: Callable[..., Any]) -> None:
10+
self.func = func
11+
self._aliases: Set[str] = set()
12+
13+
14+
class alias(object):
15+
"""Add an alias to a function"""
16+
17+
def __init__(self, *aliases: str, deprecated_version: str = None) -> None:
18+
"""Constructor
19+
20+
Args:
21+
deprecated_version (str, optional): Version number that deprecation will happen. Defaults to None.
22+
"""
23+
self.aliases: Set[str] = set(aliases)
24+
self.deprecated_version: Optional[str] = deprecated_version
25+
26+
def __call__(self, f: Callable[..., Any]) -> FunctionWrapper:
27+
"""call"""
28+
wrapped_func = FunctionWrapper(f)
29+
wrapped_func._aliases = self.aliases
30+
31+
@functools.wraps(f)
32+
def wrapper(*args: Any, **kwargs: Any) -> Any:
33+
"""Alias wrapper"""
34+
if self.deprecated_version:
35+
aliases_str = ", ".join(self.aliases)
36+
msg = f"{aliases_str} is deprecated and will be removed in version {self.deprecated_version}. Use {f.__name__} instead."
37+
warnings.warn(msg, DeprecationWarning)
38+
return f(*args, **kwargs)
39+
40+
wrapped_func.func = wrapper # Assign wrapper directly to func attribute
41+
return wrapped_func
42+
43+
44+
def aliased(aliased_class: Any) -> Any:
45+
"""Class has aliases"""
46+
original_methods: Dict[str, Any] = aliased_class.__dict__.copy()
47+
for name, method in original_methods.items():
48+
if isinstance(method, FunctionWrapper) and hasattr(method, "_aliases"):
49+
for alias in method._aliases:
50+
setattr(aliased_class, alias, method.func)
51+
52+
# Also replace the original method with the wrapped function
53+
setattr(aliased_class, name, method.func)
54+
55+
return aliased_class

pyarr/radarr.py

+54
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from datetime import datetime
12
from typing import Any, Optional, Union
23
from warnings import warn
34

@@ -634,3 +635,56 @@ def upd_manual_import(self, data: JsonObject) -> JsonObject:
634635
JsonObject: Dictionary of updated record
635636
"""
636637
return self._put("manualimport", self.ver_uri, data=data)
638+
639+
## RELEASE
640+
641+
# GET /release
642+
def get_release(self, id_: Optional[int] = None) -> JsonArray:
643+
"""Query indexers for latest releases.
644+
645+
Args:
646+
id_ (int): Database id for movie to check
647+
648+
Returns:
649+
JsonArray: List of dictionaries with items
650+
"""
651+
return self._get("release", self.ver_uri, {"movieId": id_} if id_ else None)
652+
653+
# POST /release
654+
def post_release(self, guid: str, indexer_id: int) -> JsonObject:
655+
"""Adds a previously searched release to the download client, if the release is
656+
still in Radarr's search cache (30 minute cache). If the release is not found
657+
in the cache Radarr will return a 404.
658+
659+
Args:
660+
guid (str): Recently searched result guid
661+
indexer_id (int): Database id of indexer to use
662+
663+
Returns:
664+
JsonObject: Dictionary with download release details
665+
"""
666+
data = {"guid": guid, "indexerId": indexer_id}
667+
return self._post("release", self.ver_uri, data=data)
668+
669+
# POST /release/push
670+
def post_release_push(
671+
self, title: str, download_url: str, protocol: str, publish_date: datetime
672+
) -> Any:
673+
"""If the title is wanted, Radarr will grab it.
674+
675+
Args:
676+
title (str): Release name
677+
download_url (str): .torrent file URL
678+
protocol (str): "Usenet" or "Torrent
679+
publish_date (datetime): ISO8601 date
680+
681+
Returns:
682+
JSON: Array
683+
"""
684+
data = {
685+
"title": title,
686+
"downloadUrl": download_url,
687+
"protocol": protocol,
688+
"publishDate": publish_date.isoformat(),
689+
}
690+
return self._post("release/push", self.ver_uri, data=data)

pyarr/sonarr.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
from pyarr.types import JsonArray, JsonObject
1010

1111
from .base import BaseArrAPI
12+
from .lib.alias_decorator import alias, aliased
1213
from .models.common import PyarrHistorySortKey, PyarrSortDirection
1314
from .models.sonarr import SonarrCommands, SonarrSortKey
1415

1516

17+
@aliased
1618
class SonarrAPI(BaseArrAPI):
1719
"""API wrapper for Sonarr endpoints."""
1820

@@ -398,7 +400,8 @@ def get_parsed_path(self, file_path: str) -> JsonObject:
398400
## RELEASE
399401

400402
# GET /release
401-
def get_releases(self, id_: Optional[int] = None) -> JsonArray:
403+
@alias("get_releases", deprecated_version="6.0.0")
404+
def get_release(self, id_: Optional[int] = None) -> JsonArray:
402405
"""Query indexers for latest releases.
403406
404407
Args:
@@ -410,7 +413,8 @@ def get_releases(self, id_: Optional[int] = None) -> JsonArray:
410413
return self._get("release", self.ver_uri, {"episodeId": id_} if id_ else None)
411414

412415
# POST /release
413-
def download_release(self, guid: str, indexer_id: int) -> JsonObject:
416+
@alias("download_release", "6.0.0")
417+
def post_release(self, guid: str, indexer_id: int) -> JsonObject:
414418
"""Adds a previously searched release to the download client, if the release is
415419
still in Sonarr's search cache (30 minute cache). If the release is not found
416420
in the cache Sonarr will return a 404.
@@ -427,7 +431,8 @@ def download_release(self, guid: str, indexer_id: int) -> JsonObject:
427431

428432
# POST /release/push
429433
# TODO: find response
430-
def push_release(
434+
@alias("push_release", "6.0.0")
435+
def post_release_push(
431436
self, title: str, download_url: str, protocol: str, publish_date: datetime
432437
) -> Any:
433438
"""If the title is wanted, Sonarr will grab it.

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "pyarr"
3-
version = "5.1.2"
3+
version = "5.2.0"
44
description = "Synchronous Sonarr, Radarr, Lidarr and Readarr API's for Python"
55
authors = ["Steven Marks <[email protected]>"]
66
license = "MIT"

tests/test_radarr.py

+38
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,11 @@ def test_get_indexer(radarr_client: RadarrAPI):
840840
assert isinstance(data, list)
841841

842842

843+
def test_get_release(radarr_client: RadarrAPI):
844+
data = radarr_client.get_release()
845+
assert isinstance(data, list)
846+
847+
843848
# TODO: get correct fixture
844849
@pytest.mark.usefixtures
845850
@responses.activate
@@ -902,6 +907,39 @@ def test_upd_manual_import(radarr_mock_client: RadarrAPI):
902907
assert isinstance(data, dict)
903908

904909

910+
@pytest.mark.usefixtures
911+
@responses.activate
912+
def test_post_release(radarr_mock_client: RadarrAPI):
913+
responses.add(
914+
responses.POST,
915+
"https://127.0.0.1:7878/api/v3/release",
916+
headers={"Content-Type": "application/json"},
917+
body=load_fixture("common/blank_dict.json"),
918+
status=201,
919+
)
920+
data = radarr_mock_client.post_release(guid="1450590", indexer_id=2)
921+
assert isinstance(data, dict)
922+
923+
924+
@pytest.mark.usefixtures
925+
@responses.activate
926+
def test_post_release_push(radarr_mock_client: RadarrAPI):
927+
responses.add(
928+
responses.POST,
929+
"https://127.0.0.1:7878/api/v3/release/push",
930+
headers={"Content-Type": "application/json"},
931+
body=load_fixture("common/blank_dict.json"),
932+
status=201,
933+
)
934+
data = radarr_mock_client.post_release_push(
935+
title="test",
936+
download_url="https://ipt.beelyrics.net/t/1450590",
937+
protocol="Torrent",
938+
publish_date=datetime(2020, 5, 17),
939+
)
940+
assert isinstance(data, dict)
941+
942+
905943
#### DELETES MUST BE LAST
906944

907945

tests/test_sonarr.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ def test_get_parsed_path(sonarr_mock_client: SonarrAPI):
830830

831831
@pytest.mark.usefixtures
832832
@responses.activate
833-
def test_download_release(sonarr_mock_client: SonarrAPI):
833+
def test_post_release(sonarr_mock_client: SonarrAPI):
834834
responses.add(
835835
responses.POST,
836836
"https://127.0.0.1:8989/api/v3/release",

0 commit comments

Comments
 (0)