Implement a "schema guard" for `api` module

In order to catch errors related to assumed JSON schema, regroup all
JSON data access under a context manager that catch related errors:
- KeyError
- IndexError
- ValueError
This commit is contained in:
Barbagus 2023-01-16 21:12:55 +01:00
parent fcadd531c4
commit ed5ba06a98
2 changed files with 32 additions and 16 deletions

View File

@ -3,13 +3,22 @@
"""Provide ArteTV JSON API utilities."""
import contextlib
from .error import UnexpectedAPIResponse, UnsupportedHLSProtocol
from .model import ProgramMeta
MIME_TYPE = "application/vnd.api+json; charset=utf-8"
def _fetch_api_data(http_session, path, object_type):
@contextlib.contextmanager
def _schema_guard(*context):
try:
yield
except (KeyError, IndexError, ValueError) as e:
raise UnexpectedAPIResponse("SCHEMA", *context) from e
def _fetch_api_object(http_session, path, object_type):
# Fetch an API object.
url = "https://api.arte.tv/api/player/v2/" + path
@ -19,35 +28,43 @@ def _fetch_api_data(http_session, path, object_type):
if (_ := r.headers["content-type"]) != MIME_TYPE:
raise UnexpectedAPIResponse("MIME_TYPE", path, MIME_TYPE, _)
obj = r.json()["data"]
obj = r.json()
if (_ := obj["type"]) != object_type:
with _schema_guard(path):
data_type = obj["data"]["type"]
data_attributes = obj["data"]["attributes"]
if data_type != object_type:
raise UnexpectedAPIResponse("OBJECT_TYPE", path, object_type, _)
return obj["attributes"]
return data_attributes
def fetch_program_info(http_session, p_meta):
"""Fetch the given program metadata and indexes."""
obj = _fetch_api_data(
obj = _fetch_api_object(
http_session, f"config/{p_meta.site}/{p_meta.id}", "ConfigPlayer"
)
if (_ := obj["metadata"]["providerId"]) != p_meta.id:
with _schema_guard(p_meta.site, p_meta.id):
provider_id = obj["metadata"]["providerId"]
streams = [(s["protocol"], s["url"]) for s in obj["streams"]]
if provider_id != p_meta.id:
raise UnexpectedAPIResponse(
"PROGRAM_ID_MISMATCH",
p_meta.site,
p_meta.id,
_,
provider_id,
)
program_index_urls = set()
for s in obj["streams"]:
if (_ := s["protocol"]) != "HLS_NG":
raise UnsupportedHLSProtocol(p_meta.site, p_meta.id, _)
for protocol, program_index_url in streams:
if protocol != "HLS_NG":
raise UnsupportedHLSProtocol(p_meta.site, p_meta.id, protocol)
if (program_index_url := s["url"]) in program_index_urls:
if program_index_url in program_index_urls:
raise UnexpectedAPIResponse(
"DUPLICATE_PROGRAM_INDEX_URL",
p_meta.site,

View File

@ -3,8 +3,8 @@
"""Provide ArteTV website utilities."""
import contextlib
import json
from contextlib import contextmanager
from .error import InvalidPage, PageNotFound, PageNotSupported
from .model import ProgramMeta
@ -12,12 +12,12 @@ from .model import ProgramMeta
_DATA_MARK = '<script id="__NEXT_DATA__" type="application/json">'
@contextmanager
@contextlib.contextmanager
def _schema_guard(*context):
try:
yield
except (KeyError, IndexError, ValueError) as e:
raise InvalidPage("SCHEMA", *context, e)
raise InvalidPage("SCHEMA", *context) from e
def _process_programs_page(page_value):
@ -56,7 +56,6 @@ def fetch_program(http_session, url):
# special handling of 404
if r.status_code == 404:
raise PageNotFound(url)
# other network errors
r.raise_for_status()