Add initial implementation of Audible Series Checker with API connectors and configuration
This commit is contained in:
commit
223bfbf6bc
10 changed files with 630 additions and 0 deletions
12
connectors/__init__.py
Normal file
12
connectors/__init__.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from .abs_connector import ABSConnector, ABSConnectorMock
|
||||
from .audible_connector import AudibleConnector, AudibleConnectorMock
|
||||
from .audnexus_connector import AudNexusConnector, AudNexusConnectorMock
|
||||
|
||||
__all__ = [
|
||||
ABSConnector,
|
||||
ABSConnectorMock,
|
||||
AudibleConnector,
|
||||
AudibleConnectorMock,
|
||||
AudNexusConnector,
|
||||
AudNexusConnectorMock,
|
||||
]
|
||||
61
connectors/abs_connector.py
Normal file
61
connectors/abs_connector.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import requests
|
||||
import json
|
||||
|
||||
|
||||
class ABSConnector:
|
||||
def __init__(self, abs_url, token=None):
|
||||
self.abs_url = abs_url
|
||||
self.requests = requests.Session()
|
||||
self.requests.headers = {"Authorization": f"Bearer {token}"}
|
||||
|
||||
def get_library_ids(self):
|
||||
endpoint = f"{self.abs_url}/api/libraries"
|
||||
response = self.requests.get(endpoint)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
return data["libraries"]
|
||||
|
||||
def get_series_by_library_id(self, library_id, page_size=100):
|
||||
endpoint = f"{self.abs_url}/api/libraries/{library_id}/series"
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
response = self.requests.get(
|
||||
endpoint,
|
||||
params={
|
||||
"limit": page_size,
|
||||
"page": page,
|
||||
"minified": 1,
|
||||
"sort": "name",
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
yield from data["results"]
|
||||
|
||||
page += 1
|
||||
|
||||
if data["total"] < page_size * page: # Stop if no more data
|
||||
break
|
||||
|
||||
|
||||
class ABSConnectorMock(ABSConnector):
|
||||
def get_library_ids(self):
|
||||
with open("dumps/libraries.json", "r") as f:
|
||||
data = json.load(f)
|
||||
return data["libraries"]
|
||||
|
||||
def get_series_by_library_id(self, library_id, page_size=100):
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
with open(f"dumps/library_{library_id}.page{page}.json", "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
yield from data["results"]
|
||||
|
||||
page += 1
|
||||
|
||||
if data["total"] < page_size * page: # Stop if no more data
|
||||
break
|
||||
56
connectors/audible_connector.py
Normal file
56
connectors/audible_connector.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import os
|
||||
import audible
|
||||
import json
|
||||
from getpass import getpass
|
||||
|
||||
|
||||
class AudibleConnector:
|
||||
def __init__(self, authFile):
|
||||
self.client: audible.Client = None
|
||||
self._setup_auth(authFile)
|
||||
|
||||
def __del__(self):
|
||||
if self.client:
|
||||
self.client.close()
|
||||
|
||||
def _setup_auth(self, authFile=None):
|
||||
try:
|
||||
if authFile and os.path.exists(authFile):
|
||||
self.auth = audible.Authenticator.from_file(authFile)
|
||||
else:
|
||||
self.auth = audible.Authenticator.from_login(
|
||||
input("Username "),
|
||||
getpass("Password "),
|
||||
locale="us",
|
||||
with_username=False,
|
||||
)
|
||||
if authFile:
|
||||
self.auth.to_file(authFile)
|
||||
except (
|
||||
OSError,
|
||||
audible.exceptions.AuthFlowError,
|
||||
) as e:
|
||||
print(f"Authentication failed: {e}")
|
||||
raise ConnectionError(f"Failed to authenticate: {e}")
|
||||
|
||||
self.client = audible.Client(self.auth)
|
||||
|
||||
def get_produce_from_asin(self, asin):
|
||||
endpoint = f"1.0/catalog/products/{asin}"
|
||||
response = self.client.get(
|
||||
endpoint, response_groups="series, relationships, product_attrs"
|
||||
)
|
||||
return response["product"]
|
||||
|
||||
|
||||
class AudibleConnectorMock(AudibleConnector):
|
||||
def get_produce_from_asin(self, asin):
|
||||
try:
|
||||
with open(f"dumps/products_{asin}.json", "r") as f:
|
||||
data = json.load(f)
|
||||
return data["product"]
|
||||
except FileNotFoundError:
|
||||
data = AudibleConnector.get_produce_from_asin(self, asin)
|
||||
with open(f"dumps/products_{asin}.json", "w+") as f:
|
||||
json.dump({"product": data}, f, indent=4)
|
||||
return data
|
||||
29
connectors/audnexus_connector.py
Normal file
29
connectors/audnexus_connector.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from ratelimit import limits
|
||||
import requests
|
||||
import json
|
||||
|
||||
|
||||
class AudNexusConnector:
|
||||
|
||||
@limits(calls=100, period=60)
|
||||
def request(self, url):
|
||||
return requests.get(url, {"update": 0, "seedAuthors": 0})
|
||||
|
||||
def get_book_from_asin(self, book_asin):
|
||||
endpoint = f"https://api.audnex.us/books/{book_asin}"
|
||||
response = self.request(endpoint)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
class AudNexusConnectorMock(AudNexusConnector):
|
||||
def get_book_from_asin(self, book_asin):
|
||||
try:
|
||||
with open(f"dumps/book_{book_asin}.json", "r") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
except FileNotFoundError:
|
||||
data = AudNexusConnector.get_book_from_asin(self, book_asin)
|
||||
with open(f"dumps/book_{book_asin}.json", "w+") as f:
|
||||
json.dump(data, f, indent=4)
|
||||
return data
|
||||
Loading…
Add table
Add a link
Reference in a new issue