diff --git a/nrk_psapi/cli.py b/nrk_psapi/cli.py index 041b5ff..fccc461 100644 --- a/nrk_psapi/cli.py +++ b/nrk_psapi/cli.py @@ -7,9 +7,11 @@ from dataclasses import fields import logging import re +from _io import TextIOWrapper from typing import TYPE_CHECKING, Callable from rich import print as rprint +from rich.box import SIMPLE from rich.console import Console from rich.logging import RichHandler from rich.panel import Panel @@ -29,6 +31,7 @@ SearchResponseResultsResult, SearchResultType, ) +from nrk_psapi.rss.feed import NrkPodcastFeed if TYPE_CHECKING: from nrk_psapi.models.common import BaseDataClassORJSONMixin @@ -187,7 +190,11 @@ def header_panel(title: str, subtitle: str): title, subtitle, ) - return Panel(grid) + return Panel( + grid, + style="white on dark_red", + box=SIMPLE, + ) def highlight_context( @@ -368,6 +375,21 @@ def main_parser() -> argparse.ArgumentParser: recommendations_parser.add_argument("podcast_id", type=str, help="Podcast id.") recommendations_parser.set_defaults(func=get_recommendations) + # + # RSS + # + rss_parser = subparsers.add_parser("rss", description="Get RSS feed.") + rss_parser.add_argument("podcast_id", type=str, help="Podcast id.") + rss_parser.add_argument("output_path", type=argparse.FileType("w", encoding="utf-8"), help="Output path.") + rss_parser.add_argument("--base_url", type=str, help="Base URL.") + parser.add_argument( + "--limit", + type=int, + default=None, + help="The number of episodes to include in the feed. Default is all episodes.", + ) + rss_parser.set_defaults(func=get_rss_feed) + # # Search # @@ -649,6 +671,18 @@ async def get_recommendations(args): ) +async def get_rss_feed(args): + """Get RSS feed.""" + + output_file: TextIOWrapper = args.output_path + async with NrkPodcastAPI() as client: + rss = NrkPodcastFeed(client, args.base_url) + feed = await rss.build_podcast_rss(args.podcast_id, args.limit) + xml = feed.rss() + output_file.write(xml) + console.print(f"Wrote {len(xml)} bytes to {output_file.name}") + + async def get_episode(args): """Get episode.""" async with NrkPodcastAPI() as client: @@ -820,43 +854,45 @@ async def search(args): search_results = await client.search( args.query, per_page=args.limit, page=args.page, search_type=args.type ) + total_counts = search_results.total_count for field in fields(search_results.results): field_value: SearchResponseResultsResult = getattr(search_results.results, field.name) if len(field_value.results) > 0: - # noinspection PyTypeChecker - res_fields = fields(field_value.results[0]) - console.print([f.name for f in res_fields]) - console.print( - pretty_dataclass_list( - field_value.results, - title=field.name, - hidden_fields=[ - "id", - "type", - "images", - "square_images", - "score", - # "highlights", - "description", - "date", - "series_title", - "season_id", - ], - field_formatters={ - "highlights": pretty_highlights, - }, - field_widths={ - "highlights": 50, - }, - field_order=[ - "id", - "episode_id", - "series_id", - "title", - "highlights", - ], - ), - ) + console.print( + header_panel( + field.name, + f"[bold]{getattr(total_counts, field.name)}[/bold] results", + ) + ) + console.print( + pretty_dataclass_list( + field_value.results, + hidden_fields=[ + "id", + "type", + "images", + "square_images", + "score", + "description", + "date", + "series_title", + "season_id", + ], + field_formatters={ + "highlights": pretty_highlights, + }, + field_widths={ + "highlights": 50, + }, + field_order=[ + "id", + "episode_id", + "series_id", + "title", + "highlights", + ], + ), + ) def main(): diff --git a/nrk_psapi/models/catalog.py b/nrk_psapi/models/catalog.py index cf60e28..7cef7db 100644 --- a/nrk_psapi/models/catalog.py +++ b/nrk_psapi/models/catalog.py @@ -321,6 +321,13 @@ class PodcastUmbrella(Podcast): deserialize=lambda x: [SeasonEmbedded.from_dict(d) for d in x["seasons"]], ), ) + episodes: list[Episode] = field( + default_factory=list, + metadata=field_options( + alias="_embedded", + deserialize=lambda x: [Episode.from_dict(d) for d in x["episodes"]["_embedded"]["episodes"]], + ) + ) @dataclass diff --git a/nrk_psapi/utils.py b/nrk_psapi/utils.py index 493bc27..72bfb73 100644 --- a/nrk_psapi/utils.py +++ b/nrk_psapi/utils.py @@ -10,6 +10,8 @@ if TYPE_CHECKING: from yarl import URL + from nrk_psapi.models import Image + def get_nested_items(data: dict[str, any], items_key: str) -> list[dict[str, any]]: """Get nested items from a dictionary based on the provided items_key.""" @@ -24,6 +26,14 @@ def get_nested_items(data: dict[str, any], items_key: str) -> list[dict[str, any return items +def get_image(images: list[Image], min_size: int | None = None) -> Image | None: + candidates = [img for img in images if img.width is not None] + if min_size is None: + candidates.sort(key=lambda img: img.width, reverse=True) + return candidates[0] if candidates else None + return next((img for img in candidates if img.width >= min_size), None) + + def sanitize_string(s: str, delimiter: str = "_"): """Sanitize a string to be used as a URL parameter."""