Initial commit
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.pyc
|
||||
*.egg-info/
|
||||
.pytest_cache/
|
||||
.coverage
|
9
omdb/__init__.py
Normal file
9
omdb/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .result import (
|
||||
ExtendedResult,
|
||||
MovieExtendedResult,
|
||||
MovieSearchResult,
|
||||
Rating,
|
||||
SearchResult,
|
||||
SeriesExtendedResult,
|
||||
SeriesSearchResult,
|
||||
)
|
355
omdb/result.py
Normal file
355
omdb/result.py
Normal file
@@ -0,0 +1,355 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, datetime
|
||||
from typing import Literal, override
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
from httpx import URL
|
||||
|
||||
MediaType = Literal["movie", "series"]
|
||||
RatingSource = Literal["imdb", "metacritic", "tomatometerallcritics"]
|
||||
|
||||
YEAR_RANGE_PATTERN = re.compile(r"(\d{4})-(\d{4})$")
|
||||
YEAR_ONGOING_PATTERN = re.compile(r"(\d{4})-$")
|
||||
YEAR_SINGLE_PATTERN = re.compile(r"(\d{4})$")
|
||||
|
||||
RATING_SOURCE_MAP: dict[str, RatingSource] = {
|
||||
"Internet Movie Database": "imdb",
|
||||
"Metacritic": "metacritic",
|
||||
"Rotten Tomatoes": "tomatometerallcritics",
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SearchResult(ABC):
|
||||
title: str
|
||||
imdb_id: str
|
||||
poster: URL | None
|
||||
|
||||
@classmethod
|
||||
def parse(cls, data) -> SearchResult:
|
||||
"""Parses a search result object returned from OMDb."""
|
||||
|
||||
media_type: MediaType = data["Type"]
|
||||
match media_type:
|
||||
case "movie":
|
||||
return MovieSearchResult.parse(data)
|
||||
case "series":
|
||||
return SeriesSearchResult.parse(data)
|
||||
case _:
|
||||
raise NotImplementedError(
|
||||
f'Result type "{media_type}" is not supported.'
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MovieSearchResult(SearchResult):
|
||||
year: int | None
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def parse(cls, data) -> MovieSearchResult:
|
||||
"""Parses a movie search result object returned from OMDb."""
|
||||
|
||||
return cls(
|
||||
data["Title"],
|
||||
data["imdbID"],
|
||||
_parse_optional_url(data["Poster"]),
|
||||
_parse_optional_int(data["Year"]),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeriesSearchResult(SearchResult):
|
||||
year_start: int | None
|
||||
year_end: int | None
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def parse(cls, data) -> SeriesSearchResult:
|
||||
"""Parses a series search result object returned from OMDb."""
|
||||
|
||||
year_start, year_end = _parse_optional_year_range(data["Year"])
|
||||
return cls(
|
||||
data["Title"],
|
||||
data["imdbID"],
|
||||
_parse_optional_url(data["Poster"]),
|
||||
year_start,
|
||||
year_end,
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Rating:
|
||||
source: RatingSource
|
||||
value: float
|
||||
max_value: int
|
||||
|
||||
@classmethod
|
||||
def parse(cls, data) -> Rating:
|
||||
"""Parses a rating object returned from OMDb."""
|
||||
|
||||
source = RATING_SOURCE_MAP.get(data["Source"])
|
||||
if source is None:
|
||||
raise NotImplementedError(
|
||||
f'Rating source "{data["Source"]}" is not supported.'
|
||||
)
|
||||
|
||||
value, max_value = _parse_rating(data["Value"])
|
||||
return Rating(source, value, max_value)
|
||||
|
||||
def nfo_element(self) -> Element:
|
||||
"""Generates a `<rating>` XML element for use in an NFO file."""
|
||||
|
||||
rating_element = Element(
|
||||
"rating",
|
||||
attrib={
|
||||
"name": self.source,
|
||||
"max": str(self.max_value),
|
||||
"default": "true" if self.source == "imdb" else "false",
|
||||
},
|
||||
)
|
||||
value_element = SubElement(rating_element, "value")
|
||||
value_element.text = str(self.value)
|
||||
return rating_element
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ExtendedResult(ABC):
|
||||
title: str
|
||||
imdb_id: str
|
||||
mpaa_rating: str | None
|
||||
release_date: date | None
|
||||
genres: set[str]
|
||||
plot: str | None
|
||||
language: set[str]
|
||||
countries: set[str]
|
||||
poster: URL | None
|
||||
ratings: list[Rating]
|
||||
|
||||
@classmethod
|
||||
def parse(cls, data) -> ExtendedResult:
|
||||
"""Parses an extended result object returned from OMDb."""
|
||||
|
||||
media_type: MediaType = data["Type"]
|
||||
match media_type:
|
||||
case "movie":
|
||||
return MovieExtendedResult.parse(data)
|
||||
case "series":
|
||||
return SeriesExtendedResult.parse(data)
|
||||
case _:
|
||||
raise NotImplementedError(
|
||||
f'Result type "{media_type}" is not supported.'
|
||||
)
|
||||
|
||||
@abstractmethod
|
||||
def nfo_element(self) -> Element: ...
|
||||
|
||||
def _nfo_subelements(self) -> list[Element]:
|
||||
subelements: list[Element] = []
|
||||
|
||||
title_element = Element("title")
|
||||
title_element.text = self.title
|
||||
subelements.append(title_element)
|
||||
|
||||
uniqueid_element = Element(
|
||||
"uniqueid",
|
||||
attrib={"type": "imdb", "default": "true"},
|
||||
)
|
||||
uniqueid_element.text = self.imdb_id
|
||||
subelements.append(uniqueid_element)
|
||||
|
||||
if self.mpaa_rating is not None:
|
||||
mpaa_element = Element("mpaa")
|
||||
mpaa_element.text = self.mpaa_rating
|
||||
subelements.append(mpaa_element)
|
||||
|
||||
if self.release_date is not None:
|
||||
premiered_element = Element("premiered")
|
||||
premiered_element.text = self.release_date.isoformat()
|
||||
subelements.append(premiered_element)
|
||||
|
||||
if len(self.genres):
|
||||
for genre in self.genres:
|
||||
genre_element = Element("genre")
|
||||
genre_element.text = genre
|
||||
subelements.append(genre_element)
|
||||
|
||||
if self.plot is not None:
|
||||
plot_element = Element("plot")
|
||||
plot_element.text = self.plot
|
||||
subelements.append(plot_element)
|
||||
|
||||
if len(self.countries):
|
||||
for country in self.countries:
|
||||
country_element = Element("country")
|
||||
country_element.text = country
|
||||
subelements.append(country_element)
|
||||
|
||||
if len(self.ratings):
|
||||
ratings_element = Element("ratings")
|
||||
subelements.append(ratings_element)
|
||||
for rating in self.ratings:
|
||||
rating_element = rating.nfo_element()
|
||||
ratings_element.append(rating_element)
|
||||
|
||||
return subelements
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MovieExtendedResult(ExtendedResult):
|
||||
runtime: int | None
|
||||
directors: set[str]
|
||||
writers: set[str]
|
||||
actors: set[str]
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def parse(cls, data) -> MovieExtendedResult:
|
||||
return cls(
|
||||
data["Title"],
|
||||
data["imdbID"],
|
||||
_parse_optional_string(data["Rated"]),
|
||||
_parse_optional_date(data["Released"]),
|
||||
_parse_string_set(data["Genre"]),
|
||||
_parse_optional_string(data["Plot"]),
|
||||
_parse_string_set(data["Language"]),
|
||||
_parse_string_set(data["Country"]),
|
||||
_parse_optional_url(data["Poster"]),
|
||||
[Rating.parse(rating_data) for rating_data in data["Ratings"]],
|
||||
_parse_optional_minutes(data["Runtime"]),
|
||||
_parse_string_set(data["Director"]),
|
||||
_parse_string_set(data["Writer"]),
|
||||
_parse_string_set(data["Actors"]),
|
||||
)
|
||||
|
||||
def nfo_element(self) -> Element:
|
||||
root = Element("movie")
|
||||
|
||||
for subelement in self._nfo_subelements():
|
||||
root.append(subelement)
|
||||
|
||||
if self.runtime is not None:
|
||||
runtime_element = SubElement(root, "runtime")
|
||||
runtime_element.text = str(self.runtime)
|
||||
|
||||
if len(self.directors):
|
||||
for director in self.directors:
|
||||
director_element = SubElement(root, "director")
|
||||
director_element.text = director
|
||||
|
||||
if len(self.writers):
|
||||
for writer in self.writers:
|
||||
credits_element = SubElement(root, "credits")
|
||||
credits_element.text = writer
|
||||
|
||||
if len(self.actors):
|
||||
for actor in self.actors:
|
||||
actor_element = SubElement(root, "actor")
|
||||
actor_name_element = SubElement(actor_element, "name")
|
||||
actor_name_element.text = actor
|
||||
|
||||
return root
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeriesExtendedResult(ExtendedResult):
|
||||
total_seasons: int | None
|
||||
|
||||
@override
|
||||
@classmethod
|
||||
def parse(cls, data) -> SeriesExtendedResult:
|
||||
return cls(
|
||||
title=data["Title"],
|
||||
imdb_id=data["imdbID"],
|
||||
mpaa_rating=_parse_optional_string(data["Rated"]),
|
||||
release_date=_parse_optional_date(data["Released"]),
|
||||
genres=_parse_string_set(data["Genre"]),
|
||||
plot=_parse_optional_string(data["Plot"]),
|
||||
language=_parse_string_set(data["Language"]),
|
||||
countries=_parse_string_set(data["Country"]),
|
||||
poster=_parse_optional_url(data["Poster"]),
|
||||
ratings=[Rating.parse(rating_data) for rating_data in data["Ratings"]],
|
||||
total_seasons=_parse_optional_int(data["totalSeasons"]),
|
||||
)
|
||||
|
||||
def nfo_element(self) -> Element:
|
||||
root = Element("tvshow")
|
||||
|
||||
for subelement in self._nfo_subelements():
|
||||
root.append(subelement)
|
||||
|
||||
if self.total_seasons is not None:
|
||||
season_element = SubElement(root, "season")
|
||||
season_element.text = str(self.total_seasons)
|
||||
|
||||
return root
|
||||
|
||||
|
||||
def _parse_optional_string(desc: str) -> str | None:
|
||||
if desc == "N/A":
|
||||
return None
|
||||
|
||||
return desc
|
||||
|
||||
|
||||
def _parse_optional_int(desc: str) -> int | None:
|
||||
if desc == "N/A":
|
||||
return None
|
||||
|
||||
return int(desc)
|
||||
|
||||
|
||||
def _parse_optional_url(desc: str) -> URL | None:
|
||||
if desc == "N/A":
|
||||
return None
|
||||
|
||||
return URL(desc)
|
||||
|
||||
|
||||
def _parse_optional_date(desc: str) -> date | None:
|
||||
if desc == "N/A":
|
||||
return None
|
||||
|
||||
return datetime.strptime(desc, "%d %b %Y").date()
|
||||
|
||||
|
||||
def _parse_optional_minutes(desc: str) -> int | None:
|
||||
if desc == "N/A":
|
||||
return None
|
||||
|
||||
return int(desc[:-4])
|
||||
|
||||
|
||||
def _parse_optional_year_range(desc: str) -> tuple[int | None, int | None]:
|
||||
if desc == "N/A":
|
||||
return None, None
|
||||
|
||||
y1, sep, y2 = desc.partition("–")
|
||||
|
||||
if sep == "":
|
||||
return int(y1), int(y1)
|
||||
|
||||
if y2 == "":
|
||||
return int(y1), None
|
||||
|
||||
return int(y1), int(y2)
|
||||
|
||||
|
||||
def _parse_rating(desc: str) -> tuple[float, int]:
|
||||
if desc.endswith("%"):
|
||||
return float(desc[:-1]), 100
|
||||
|
||||
n1, sep, n2 = desc.partition("/")
|
||||
|
||||
return float(n1), int(n2)
|
||||
|
||||
|
||||
def _parse_string_set(desc: str) -> set[str]:
|
||||
if desc == "N/A":
|
||||
return set()
|
||||
|
||||
return {item.strip() for item in desc.split(",")}
|
16
pyproject.toml
Normal file
16
pyproject.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "omdb"
|
||||
version = "0.1.0"
|
||||
description = "OMDb API wrapper for the HTTPX client."
|
||||
requires-python = ">= 3.12"
|
||||
dependencies = ["httpx"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest", "pytest-httpx", "pytest-cov"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--cov=omdb --cov-report term-missing"
|
392
tests/result_test.py
Normal file
392
tests/result_test.py
Normal file
@@ -0,0 +1,392 @@
|
||||
from datetime import date
|
||||
|
||||
import pytest
|
||||
from httpx import URL
|
||||
|
||||
from omdb.result import (
|
||||
ExtendedResult,
|
||||
MovieExtendedResult,
|
||||
MovieSearchResult,
|
||||
Rating,
|
||||
SearchResult,
|
||||
SeriesExtendedResult,
|
||||
SeriesSearchResult,
|
||||
_parse_optional_date,
|
||||
_parse_optional_int,
|
||||
_parse_optional_minutes,
|
||||
_parse_optional_string,
|
||||
_parse_optional_url,
|
||||
_parse_optional_year_range,
|
||||
_parse_rating,
|
||||
_parse_string_set,
|
||||
)
|
||||
|
||||
mock_movie_search_result = {
|
||||
"Title": "Movie Title",
|
||||
"Year": "2000",
|
||||
"imdbID": "tt0000000",
|
||||
"Type": "movie",
|
||||
"Poster": "https://example.com/poster.jpg",
|
||||
}
|
||||
|
||||
mock_series_search_result = {
|
||||
"Title": "Series Title",
|
||||
"Year": "2000–2010",
|
||||
"imdbID": "tt0000000",
|
||||
"Type": "series",
|
||||
"Poster": "https://example.com/poster.jpg",
|
||||
}
|
||||
|
||||
mock_movie_extended_result = {
|
||||
"Type": "movie",
|
||||
"Title": "Movie Title",
|
||||
"Year": "2000",
|
||||
"Rated": "R",
|
||||
"Released": "01 Jan 2000",
|
||||
"Genre": "Action, Adventure, Comedy",
|
||||
"Plot": "Example movie",
|
||||
"Language": "English, Esperanto",
|
||||
"Country": "United States, Molossia",
|
||||
"Poster": "https://example.com/poster.jpg",
|
||||
"Ratings": [
|
||||
{"Source": "Internet Movie Database", "Value": "10.0/10"},
|
||||
],
|
||||
"imdbID": "tt0000000",
|
||||
"Runtime": "123 min",
|
||||
"Director": "Alan Smithee",
|
||||
"Writer": "Alan Smithee",
|
||||
"Actors": "Jack Nicholson, Robert De Niro, Anthony Hopkins",
|
||||
}
|
||||
|
||||
mock_series_extended_result = {
|
||||
"Type": "series",
|
||||
"Title": "Series Title",
|
||||
"Year": "2000",
|
||||
"Rated": "TV-MA",
|
||||
"Released": "01 Jan 2000",
|
||||
"Genre": "Action, Adventure, Comedy",
|
||||
"Plot": "Example series",
|
||||
"Language": "English, Esperanto",
|
||||
"Country": "United States, Molossia",
|
||||
"Poster": "https://example.com/poster.jpg",
|
||||
"Ratings": [
|
||||
{"Source": "Internet Movie Database", "Value": "10.0/10"},
|
||||
],
|
||||
"imdbID": "tt0000000",
|
||||
"totalSeasons": 10,
|
||||
}
|
||||
|
||||
|
||||
def test_search_result_parse():
|
||||
"""Tests the `SearchResult.parse` class method."""
|
||||
|
||||
movie_result = SearchResult.parse(mock_movie_search_result)
|
||||
assert movie_result == MovieSearchResult.parse(mock_movie_search_result)
|
||||
|
||||
series_result = SearchResult.parse(mock_series_search_result)
|
||||
assert series_result == SeriesSearchResult.parse(mock_series_search_result)
|
||||
|
||||
with pytest.raises(
|
||||
NotImplementedError,
|
||||
match='Result type "unknown" is not supported.',
|
||||
):
|
||||
SearchResult.parse({"Type": "unknown"})
|
||||
|
||||
|
||||
def test_movie_search_result_parse():
|
||||
"""Tests the `MovieSearchResult.parse` class method."""
|
||||
|
||||
result = MovieSearchResult.parse(mock_movie_search_result)
|
||||
assert result == MovieSearchResult(
|
||||
title="Movie Title",
|
||||
imdb_id="tt0000000",
|
||||
poster=URL("https://example.com/poster.jpg"),
|
||||
year=2000,
|
||||
)
|
||||
|
||||
|
||||
def test_series_search_result_parse():
|
||||
"""Tests the `SeriesSearchResult.parse` class method."""
|
||||
|
||||
result = SeriesSearchResult.parse(mock_series_search_result)
|
||||
assert result == SeriesSearchResult(
|
||||
title="Series Title",
|
||||
imdb_id="tt0000000",
|
||||
poster=URL("https://example.com/poster.jpg"),
|
||||
year_start=2000,
|
||||
year_end=2010,
|
||||
)
|
||||
|
||||
|
||||
def test_rating_parse():
|
||||
"""Tests the `Rating.parse` class method."""
|
||||
|
||||
rating = Rating.parse({"Source": "Internet Movie Database", "Value": "50.0/100"})
|
||||
assert rating == Rating("imdb", 50.0, 100)
|
||||
|
||||
with pytest.raises(
|
||||
NotImplementedError,
|
||||
match='Rating source "Unknown" is not supported.',
|
||||
):
|
||||
Rating.parse({"Source": "Unknown", "Value": "50%"})
|
||||
|
||||
|
||||
def test_rating_nfo_element():
|
||||
"""Tests the `Rating.nfo_element` method."""
|
||||
|
||||
rating = Rating("imdb", 50.0, 100)
|
||||
element = rating.nfo_element()
|
||||
assert element.tag == "rating"
|
||||
assert element.attrib == {"name": "imdb", "default": "true", "max": "100"}
|
||||
assert element[0].tag == "value"
|
||||
assert element[0].text == "50.0"
|
||||
|
||||
rating = Rating("metacritic", 5.0, 10)
|
||||
element = rating.nfo_element()
|
||||
assert element.tag == "rating"
|
||||
assert element.attrib == {"name": "metacritic", "default": "false", "max": "10"}
|
||||
assert element[0].tag == "value"
|
||||
assert element[0].text == "5.0"
|
||||
|
||||
|
||||
def test_extended_result_parse():
|
||||
"""Tests the `ExtendedResult.parse` class method."""
|
||||
|
||||
movie_result = ExtendedResult.parse(mock_movie_extended_result)
|
||||
assert movie_result == MovieExtendedResult.parse(mock_movie_extended_result)
|
||||
|
||||
series_result = ExtendedResult.parse(mock_series_extended_result)
|
||||
assert series_result == SeriesExtendedResult.parse(mock_series_extended_result)
|
||||
|
||||
with pytest.raises(
|
||||
NotImplementedError,
|
||||
match='Result type "unknown" is not supported.',
|
||||
):
|
||||
ExtendedResult.parse({"Type": "unknown"})
|
||||
|
||||
|
||||
def test_extended_result_nfo_subelements():
|
||||
"""Tests the `ExtendedResult._nfo_subelements` method."""
|
||||
|
||||
result = SeriesExtendedResult(
|
||||
title="Series title",
|
||||
imdb_id="tt0000000",
|
||||
mpaa_rating="TV-MA",
|
||||
release_date=date(2000, 1, 1),
|
||||
genres={"Action", "Adventure", "Comedy"},
|
||||
plot="Example series",
|
||||
language={"English", "Esperanto"},
|
||||
countries={"United States", "Molossia"},
|
||||
poster=URL("https://example.com/poster.jpg"),
|
||||
ratings=[Rating("imdb", 10.0, 10)],
|
||||
total_seasons=0,
|
||||
)
|
||||
elements = result._nfo_subelements()
|
||||
|
||||
title_elements = [element for element in elements if element.tag == "title"]
|
||||
assert len(title_elements) == 1
|
||||
assert title_elements[0].text == "Series title"
|
||||
|
||||
uniqueid_elements = [element for element in elements if element.tag == "uniqueid"]
|
||||
assert len(uniqueid_elements) == 1
|
||||
assert uniqueid_elements[0].attrib == {"type": "imdb", "default": "true"}
|
||||
assert uniqueid_elements[0].text == "tt0000000"
|
||||
|
||||
mpaa_elements = [element for element in elements if element.tag == "mpaa"]
|
||||
assert len(mpaa_elements) == 1
|
||||
assert mpaa_elements[0].text == "TV-MA"
|
||||
|
||||
premiered_elements = [element for element in elements if element.tag == "premiered"]
|
||||
assert len(premiered_elements) == 1
|
||||
assert premiered_elements[0].text == "2000-01-01"
|
||||
|
||||
genre_elements = [element for element in elements if element.tag == "genre"]
|
||||
assert len(genre_elements) == 3
|
||||
assert any(genre_element.text == "Action" for genre_element in genre_elements)
|
||||
|
||||
plot_elements = [element for element in elements if element.tag == "plot"]
|
||||
assert len(plot_elements) == 1
|
||||
assert plot_elements[0].text == "Example series"
|
||||
|
||||
country_elements = [element for element in elements if element.tag == "country"]
|
||||
assert len(country_elements) == 2
|
||||
assert any(
|
||||
country_element.text == "United States" for country_element in country_elements
|
||||
)
|
||||
|
||||
ratings_elements = [element for element in elements if element.tag == "ratings"]
|
||||
assert len(ratings_elements) == 1
|
||||
assert len(ratings_elements[0]) == 1
|
||||
assert ratings_elements[0][0].tag == "rating"
|
||||
assert ratings_elements[0][0].attrib == {
|
||||
"name": "imdb",
|
||||
"max": "10",
|
||||
"default": "true",
|
||||
}
|
||||
assert ratings_elements[0][0][0].tag == "value"
|
||||
assert ratings_elements[0][0][0].text == "10.0"
|
||||
|
||||
|
||||
def test_movie_extended_result_parse():
|
||||
"""Tests the `MovieExtendedResult.parse` class method."""
|
||||
|
||||
result = MovieExtendedResult.parse(mock_movie_extended_result)
|
||||
assert result == MovieExtendedResult(
|
||||
title="Movie Title",
|
||||
imdb_id="tt0000000",
|
||||
mpaa_rating="R",
|
||||
release_date=date(2000, 1, 1),
|
||||
genres={"Action", "Adventure", "Comedy"},
|
||||
plot="Example movie",
|
||||
language={"English", "Esperanto"},
|
||||
countries={"United States", "Molossia"},
|
||||
poster=URL("https://example.com/poster.jpg"),
|
||||
ratings=[Rating("imdb", 10.0, 10)],
|
||||
runtime=123,
|
||||
directors={"Alan Smithee"},
|
||||
writers={"Alan Smithee"},
|
||||
actors={"Jack Nicholson", "Robert De Niro", "Anthony Hopkins"},
|
||||
)
|
||||
|
||||
|
||||
def test_movie_extended_result_nfo_element():
|
||||
"""Tests the `MovieExtendedResult.nfo_element` method."""
|
||||
|
||||
result = MovieExtendedResult.parse(mock_movie_extended_result)
|
||||
element = result.nfo_element()
|
||||
assert element.tag == "movie"
|
||||
|
||||
for tag in (
|
||||
"title",
|
||||
"uniqueid",
|
||||
"mpaa",
|
||||
"premiered",
|
||||
"genre",
|
||||
"plot",
|
||||
"country",
|
||||
"ratings",
|
||||
):
|
||||
assert element.find(tag) is not None
|
||||
|
||||
runtime_element = element.find("runtime")
|
||||
assert runtime_element is not None
|
||||
assert runtime_element.text == "123"
|
||||
|
||||
director_elements = element.findall("director")
|
||||
assert len(director_elements) == 1
|
||||
assert director_elements[0].text == "Alan Smithee"
|
||||
|
||||
credits_elements = element.findall("credits")
|
||||
assert len(credits_elements) == 1
|
||||
assert credits_elements[0].text == "Alan Smithee"
|
||||
|
||||
actor_elements = element.findall("actor")
|
||||
assert len(actor_elements) == 3
|
||||
assert any(
|
||||
actor_element[0].tag == "name" and actor_element[0].text == "Anthony Hopkins"
|
||||
for actor_element in actor_elements
|
||||
)
|
||||
|
||||
|
||||
def test_series_extended_result_parse():
|
||||
"""Tests the `SeriesExtendedResult.parse` class method."""
|
||||
|
||||
series_extended_result = SeriesExtendedResult.parse(mock_series_extended_result)
|
||||
assert series_extended_result == SeriesExtendedResult(
|
||||
title="Series Title",
|
||||
imdb_id="tt0000000",
|
||||
mpaa_rating="TV-MA",
|
||||
release_date=date(2000, 1, 1),
|
||||
genres={"Action", "Adventure", "Comedy"},
|
||||
plot="Example series",
|
||||
language={"English", "Esperanto"},
|
||||
countries={"United States", "Molossia"},
|
||||
poster=URL("https://example.com/poster.jpg"),
|
||||
ratings=[Rating("imdb", 10.0, 10)],
|
||||
total_seasons=10,
|
||||
)
|
||||
|
||||
|
||||
def test_series_extended_result_nfo():
|
||||
"""Tests the `SeriesExtendedResult.nfo_element` method."""
|
||||
|
||||
result = SeriesExtendedResult.parse(mock_series_extended_result)
|
||||
element = result.nfo_element()
|
||||
assert element.tag == "tvshow"
|
||||
|
||||
for tag in (
|
||||
"title",
|
||||
"uniqueid",
|
||||
"mpaa",
|
||||
"premiered",
|
||||
"genre",
|
||||
"plot",
|
||||
"country",
|
||||
"ratings",
|
||||
):
|
||||
assert element.find(tag) is not None
|
||||
|
||||
season_element = element.find("season")
|
||||
assert season_element is not None
|
||||
assert season_element.text == "10"
|
||||
|
||||
|
||||
def test_parse_optional_string():
|
||||
"""Tests the `_parse_optional_string` function."""
|
||||
|
||||
assert _parse_optional_string("N/A") is None
|
||||
assert _parse_optional_string("TV-MA") == "TV-MA"
|
||||
|
||||
|
||||
def test_parse_optional_int():
|
||||
"""Tests the `_parse_optional_int` function."""
|
||||
|
||||
assert _parse_optional_int("N/A") is None
|
||||
assert _parse_optional_int("2000") == 2000
|
||||
|
||||
|
||||
def test_parse_optional_url():
|
||||
"""Tests the `_parse_optional_url` function."""
|
||||
|
||||
assert _parse_optional_url("N/A") is None
|
||||
assert _parse_optional_url("https://example.com/poster.jpg") == URL(
|
||||
"https://example.com/poster.jpg"
|
||||
)
|
||||
|
||||
|
||||
def test_parse_optional_date():
|
||||
"""Tests the `_parse_optional_date` function."""
|
||||
|
||||
assert _parse_optional_date("N/A") is None
|
||||
assert _parse_optional_date("01 Jan 2000") == date(2000, 1, 1)
|
||||
|
||||
|
||||
def test_parse_optional_minutes():
|
||||
"""Tests the `_parse_optional_minutes` function."""
|
||||
|
||||
assert _parse_optional_minutes("N/A") is None
|
||||
assert _parse_optional_minutes("123 min") == 123
|
||||
|
||||
|
||||
def test_parse_optional_year_range():
|
||||
"""Tests the `_parse_optional_year_range` function."""
|
||||
|
||||
assert _parse_optional_year_range("N/A") == (None, None)
|
||||
assert _parse_optional_year_range("2000") == (2000, 2000)
|
||||
assert _parse_optional_year_range("2000–") == (2000, None)
|
||||
assert _parse_optional_year_range("2000–2001") == (2000, 2001)
|
||||
|
||||
|
||||
def test_parse_rating():
|
||||
"""Tests the `_parse_rating` function."""
|
||||
|
||||
assert _parse_rating("50%") == (50.0, 100)
|
||||
assert _parse_rating("50/100") == (50.0, 100)
|
||||
assert _parse_rating("5.0/10") == (5.0, 10)
|
||||
|
||||
|
||||
def test_parse_string_set():
|
||||
"""Tests the `_parse_string_set` function."""
|
||||
|
||||
assert _parse_string_set("N/A") == set()
|
||||
assert _parse_string_set("Foo, Bar, Baz Qux") == {"Foo", "Bar", "Baz Qux"}
|
Reference in New Issue
Block a user