Module hdrezka.post.page
Any HDRezka catalog page.
Classes
class Page (url: str, *, client: HDRezkaClient)-
Expand source code
class Page: """Paginated HDRezka catalog or listing page.""" __slots__ = ('_page', '_page_format', '__yields', '__yields_page', 'client') def __init__(self, url: str, *, client: 'HDRezkaClient'): """ :param url: Absolute page URL (or host root). :param client: Session used for HTTP requests. """ self.client = client self.__yields: list[InlineItem] = [] self.__yields_page = 0 self.page = url @property def page(self) -> str: """Current page URL template base.""" return self._page @page.setter def page(self, value): """Cast value to str and set the paginator format.""" if not isinstance(value, str): value = str(value) # noinspection HttpUrlsUsage if not (value.startswith('https://') or value.startswith('http://')): value = f'https://{value}' self._page = value self._page_format = self._concat_paginator(value.removesuffix('/')) @staticmethod def _concat_paginator(url: str) -> Callable[[...], str]: def __concat_paginator(page, *args, **kwargs): if page is not None: return f'{url}/page/{{0}}/'.format(page, *args, **kwargs) return url.format(*args, **kwargs) return __concat_paginator @staticmethod def _inline_info(years: str, country: str, genre: str): year, *finals = years.split('-') if finals: final, = finals year_final = ... if final.strip() == '...' else int(final) else: year_final = None return InlineInfo(int(year), year_final, country.strip(), genre.strip()) def _request_url(self, page: int | slice | Iterable[int] | Any, **query) -> str: """Build a paginated request URL with optional query parameters.""" return merge_query(self._page_format(page), **query) def _parse_items(self, soup: BeautifulSoup) -> list[InlineItem]: items = soup.find_all(class_='b-content__inline_item') return [InlineItem( (a := (link := i.find(class_='b-content__inline_item-link')).find('a'))['href'], a.text, self._inline_info(*link.find('div').text.split(',', 3)), i.find(class_='b-content__inline_item-cover').find('img').get('src', ''), self.client, ) for i in items] async def get_page_content( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageContent: """ Fetch a catalog page and parse items, filters, series updates, and favorites cats. Extra keyword arguments are merged into the request query string (for example ``filter`` / ``genre`` from ``PageFilters``). Raises ``EmptyPage`` when no inline items are present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text soup = BeautifulSoup(html, builder=BUILDER) items = self._parse_items(soup) if not items: raise EmptyPage(page) return PageContent( items=items, filters=parse_page_filters(soup), series_updates=parse_series_updates(soup), favorites_cats=parse_favorites_cats(soup), ) async def get_page( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> list[InlineItem]: """ Fetch inline items for the given page number. Optional ``filter`` / ``genre`` (and other query kwargs) are appended to the request URL. Raises ``EmptyPage`` when the page has no items. """ return (await self.get_page_content(page, filter=filter, genre=genre, **query)).items async def get_filters( self, page: int | slice | Iterable[int] = 1, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageFilters: """ Fetch the page and return available sort/type filters (empty if absent). Does not require inline items to be present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text return parse_page_filters(html) async def get_series_updates(self) -> tuple[SeriesUpdateBlock, ...]: """ Fetch this page URL and parse sidebar series updates. Typically present on the site home page. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_series_updates(html) async def get_navbar(self) -> Navbar: """Fetch this page URL and parse the top navigation menu.""" html = (await self.client.get_response('GET', self._page)).text return parse_navbar(html) async def get_favorites_cats(self) -> tuple[FavoritesCat, ...]: """ Fetch this page URL and parse favorites collection links. Present on ``/favorites/`` pages. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_favorites_cats(html) def __aiter__(self): """Async iterator over items across successive pages.""" self.__yields_page = 0 self.__yields.clear() return self async def __anext__(self) -> InlineItem: """Return the next ``InlineItem`` across pages.""" if not self.__yields: try: self.__yields_page += 1 self.__yields += await self.get_page(self.__yields_page) except EmptyPage: raise StopAsyncIteration return self.__yields.pop(0) def __repr__(self): return f'{self.__class__.__qualname__}({self.page!r})'Paginated HDRezka catalog or listing page.
:param url: Absolute page URL (or host root). :param client: Session used for HTTP requests.
Subclasses
Instance variables
var client-
Expand source code
class Page: """Paginated HDRezka catalog or listing page.""" __slots__ = ('_page', '_page_format', '__yields', '__yields_page', 'client') def __init__(self, url: str, *, client: 'HDRezkaClient'): """ :param url: Absolute page URL (or host root). :param client: Session used for HTTP requests. """ self.client = client self.__yields: list[InlineItem] = [] self.__yields_page = 0 self.page = url @property def page(self) -> str: """Current page URL template base.""" return self._page @page.setter def page(self, value): """Cast value to str and set the paginator format.""" if not isinstance(value, str): value = str(value) # noinspection HttpUrlsUsage if not (value.startswith('https://') or value.startswith('http://')): value = f'https://{value}' self._page = value self._page_format = self._concat_paginator(value.removesuffix('/')) @staticmethod def _concat_paginator(url: str) -> Callable[[...], str]: def __concat_paginator(page, *args, **kwargs): if page is not None: return f'{url}/page/{{0}}/'.format(page, *args, **kwargs) return url.format(*args, **kwargs) return __concat_paginator @staticmethod def _inline_info(years: str, country: str, genre: str): year, *finals = years.split('-') if finals: final, = finals year_final = ... if final.strip() == '...' else int(final) else: year_final = None return InlineInfo(int(year), year_final, country.strip(), genre.strip()) def _request_url(self, page: int | slice | Iterable[int] | Any, **query) -> str: """Build a paginated request URL with optional query parameters.""" return merge_query(self._page_format(page), **query) def _parse_items(self, soup: BeautifulSoup) -> list[InlineItem]: items = soup.find_all(class_='b-content__inline_item') return [InlineItem( (a := (link := i.find(class_='b-content__inline_item-link')).find('a'))['href'], a.text, self._inline_info(*link.find('div').text.split(',', 3)), i.find(class_='b-content__inline_item-cover').find('img').get('src', ''), self.client, ) for i in items] async def get_page_content( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageContent: """ Fetch a catalog page and parse items, filters, series updates, and favorites cats. Extra keyword arguments are merged into the request query string (for example ``filter`` / ``genre`` from ``PageFilters``). Raises ``EmptyPage`` when no inline items are present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text soup = BeautifulSoup(html, builder=BUILDER) items = self._parse_items(soup) if not items: raise EmptyPage(page) return PageContent( items=items, filters=parse_page_filters(soup), series_updates=parse_series_updates(soup), favorites_cats=parse_favorites_cats(soup), ) async def get_page( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> list[InlineItem]: """ Fetch inline items for the given page number. Optional ``filter`` / ``genre`` (and other query kwargs) are appended to the request URL. Raises ``EmptyPage`` when the page has no items. """ return (await self.get_page_content(page, filter=filter, genre=genre, **query)).items async def get_filters( self, page: int | slice | Iterable[int] = 1, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageFilters: """ Fetch the page and return available sort/type filters (empty if absent). Does not require inline items to be present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text return parse_page_filters(html) async def get_series_updates(self) -> tuple[SeriesUpdateBlock, ...]: """ Fetch this page URL and parse sidebar series updates. Typically present on the site home page. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_series_updates(html) async def get_navbar(self) -> Navbar: """Fetch this page URL and parse the top navigation menu.""" html = (await self.client.get_response('GET', self._page)).text return parse_navbar(html) async def get_favorites_cats(self) -> tuple[FavoritesCat, ...]: """ Fetch this page URL and parse favorites collection links. Present on ``/favorites/`` pages. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_favorites_cats(html) def __aiter__(self): """Async iterator over items across successive pages.""" self.__yields_page = 0 self.__yields.clear() return self async def __anext__(self) -> InlineItem: """Return the next ``InlineItem`` across pages.""" if not self.__yields: try: self.__yields_page += 1 self.__yields += await self.get_page(self.__yields_page) except EmptyPage: raise StopAsyncIteration return self.__yields.pop(0) def __repr__(self): return f'{self.__class__.__qualname__}({self.page!r})' prop page : str-
Expand source code
@property def page(self) -> str: """Current page URL template base.""" return self._pageCurrent page URL template base.
Methods
async def get_favorites_cats(self) ‑> tuple[FavoritesCat, ...]-
Expand source code
async def get_favorites_cats(self) -> tuple[FavoritesCat, ...]: """ Fetch this page URL and parse favorites collection links. Present on ``/favorites/`` pages. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_favorites_cats(html)Fetch this page URL and parse favorites collection links.
Present on
/favorites/pages. Returns an empty tuple if missing. async def get_filters(self,
page: int | slice | Iterable[int] = 1,
*,
filter: str | None = None,
genre: int | str | None = None,
**query) ‑> PageFilters-
Expand source code
async def get_filters( self, page: int | slice | Iterable[int] = 1, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageFilters: """ Fetch the page and return available sort/type filters (empty if absent). Does not require inline items to be present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text return parse_page_filters(html)Fetch the page and return available sort/type filters (empty if absent).
Does not require inline items to be present.
-
Expand source code
async def get_navbar(self) -> Navbar: """Fetch this page URL and parse the top navigation menu.""" html = (await self.client.get_response('GET', self._page)).text return parse_navbar(html)Fetch this page URL and parse the top navigation menu.
async def get_page(self,
page: int | slice | Iterable[int] | None = None,
*,
filter: str | None = None,
genre: int | str | None = None,
**query) ‑> list[InlineItem]-
Expand source code
async def get_page( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> list[InlineItem]: """ Fetch inline items for the given page number. Optional ``filter`` / ``genre`` (and other query kwargs) are appended to the request URL. Raises ``EmptyPage`` when the page has no items. """ return (await self.get_page_content(page, filter=filter, genre=genre, **query)).itemsFetch inline items for the given page number.
Optional
filter/genre(and other query kwargs) are appended to the request URL. RaisesEmptyPagewhen the page has no items. async def get_page_content(self,
page: int | slice | Iterable[int] | None = None,
*,
filter: str | None = None,
genre: int | str | None = None,
**query) ‑> PageContent-
Expand source code
async def get_page_content( self, page: int | slice | Iterable[int] | None = None, *, filter: str | None = None, genre: str | int | None = None, **query, ) -> PageContent: """ Fetch a catalog page and parse items, filters, series updates, and favorites cats. Extra keyword arguments are merged into the request query string (for example ``filter`` / ``genre`` from ``PageFilters``). Raises ``EmptyPage`` when no inline items are present. """ if filter is not None: query['filter'] = filter if genre is not None: query['genre'] = genre html = (await self.client.get_response('GET', self._request_url(page, **query))).text soup = BeautifulSoup(html, builder=BUILDER) items = self._parse_items(soup) if not items: raise EmptyPage(page) return PageContent( items=items, filters=parse_page_filters(soup), series_updates=parse_series_updates(soup), favorites_cats=parse_favorites_cats(soup), )Fetch a catalog page and parse items, filters, series updates, and favorites cats.
Extra keyword arguments are merged into the request query string (for example
filter/genrefromPageFilters). RaisesEmptyPagewhen no inline items are present. async def get_series_updates(self) ‑> tuple[SeriesUpdateBlock, ...]-
Expand source code
async def get_series_updates(self) -> tuple[SeriesUpdateBlock, ...]: """ Fetch this page URL and parse sidebar series updates. Typically present on the site home page. Returns an empty tuple if missing. """ html = (await self.client.get_response('GET', self._page)).text return parse_series_updates(html)Fetch this page URL and parse sidebar series updates.
Typically present on the site home page. Returns an empty tuple if missing.