Refactor IMVDBApi and types for improved type safety and clarity
- Updated IMVDBApi to use Annotated types for query parameters and response models. - Removed commented-out sync_video_search method to clean up the code. - Refactored types.py to replace TypedDict with Pydantic's ModelBase for better validation and documentation. - Enhanced field descriptions in VideoType and SearchResponseType for clearer API documentation.
This commit is contained in:
@@ -1,18 +1,17 @@
|
||||
from typing import Any
|
||||
from typing import Annotated, Any, Self
|
||||
|
||||
from beets import logging
|
||||
from httpx_auth import HeaderApiKey
|
||||
from lapidary.runtime import (
|
||||
Body,
|
||||
ClientBase,
|
||||
Query,
|
||||
Response,
|
||||
Responses,
|
||||
UnexpectedResponse,
|
||||
get,
|
||||
)
|
||||
from typing import Annotated, Any, Literal, Sequence, cast, Self
|
||||
from httpx_auth import HeaderApiKey
|
||||
|
||||
from beetsplug.beets_music_videos.types import SearchResponseType, VideoType
|
||||
from beets import logging
|
||||
from beetsplug.beets_music_videos.types import SearchResponseType
|
||||
|
||||
log = logging.getLogger("beets")
|
||||
|
||||
@@ -46,12 +45,13 @@ class IMVDBApi(ClientBase): # type: ignore
|
||||
@get("/search/videos") # type: ignore[misc]
|
||||
def video_search(
|
||||
self: Self,
|
||||
*,
|
||||
q: Annotated[str, Query()],
|
||||
) -> Annotated[
|
||||
SearchResponseType,
|
||||
Responses(
|
||||
responses={
|
||||
"2xx": Response(body=Body({"application/json": SearchResponseType})),
|
||||
"2xx": Response(Body({"application/json": SearchResponseType})),
|
||||
}
|
||||
),
|
||||
]:
|
||||
@@ -60,35 +60,3 @@ class IMVDBApi(ClientBase): # type: ignore
|
||||
Implemented at runtime by Lapidary; this stub is never executed.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
# def sync_video_search(self, query: str) -> list[VideoType]:
|
||||
# """Sync wrapper used by the plugin. Runs video_search and returns .results."""
|
||||
# import asyncio
|
||||
# # Reuse a single event loop to avoid "Event loop is closed" on subsequent calls.
|
||||
# try:
|
||||
# loop = asyncio.get_event_loop()
|
||||
# if loop.is_closed():
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
# except RuntimeError:
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
# try:
|
||||
# resp = loop.run_until_complete(self.video_search(q=query))
|
||||
# return resp["results"]
|
||||
# except UnexpectedResponse as e:
|
||||
# # Lapidary raises UnexpectedResponse when body validation fails
|
||||
# # (e.g. API shape differs from SearchResponseType). For 200, parse
|
||||
# # body and return results.
|
||||
# if e.response.status_code == 200:
|
||||
# try:
|
||||
# body = e.response.json()
|
||||
# if isinstance(body, dict):
|
||||
# return cast(list[VideoType], body["results"])
|
||||
# except Exception:
|
||||
# pass
|
||||
# log.debug("imvdb search failed for %r: %s", query[:50], e)
|
||||
# return []
|
||||
# except Exception as e:
|
||||
# log.debug("imvdb search failed for %r: %s", query[:50], e)
|
||||
# return []
|
||||
|
||||
@@ -1,43 +1,48 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Sequence
|
||||
from typing import Annotated
|
||||
|
||||
from lapidary.runtime import ModelBase
|
||||
from pydantic import Field
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
|
||||
class ImageSizes(TypedDict, total=False):
|
||||
"""Image URL set; keys are size codes (o=original, l=large, b=big, t=thumb)."""
|
||||
|
||||
o: str
|
||||
l: str
|
||||
b: str
|
||||
t: str
|
||||
o: Annotated[str, Field(description="Original image URL")]
|
||||
l: Annotated[str, Field(description="Large image URL")]
|
||||
b: Annotated[str, Field(description="Big image URL")]
|
||||
t: Annotated[str, Field(description="Thumbnail image URL")]
|
||||
|
||||
|
||||
class ArtistType(TypedDict):
|
||||
name: str
|
||||
slug: str
|
||||
url: str
|
||||
discogs_id: int | None
|
||||
class ArtistType(ModelBase):
|
||||
name: Annotated[str, Field(description="Name")]
|
||||
slug: Annotated[str, Field(description="Slug")]
|
||||
url: Annotated[str, Field(description="URL")]
|
||||
discogs_id: Annotated[int | None, Field(description="Discogs ID")]
|
||||
|
||||
|
||||
class VideoType(TypedDict):
|
||||
id: str
|
||||
production_status: str
|
||||
song_title: str
|
||||
url: str
|
||||
multiple_versions: bool
|
||||
version_name: str | None
|
||||
version_number: int
|
||||
is_imvdb_pick: bool
|
||||
aspect_ratio: str | None
|
||||
year: int
|
||||
verified_credits: bool
|
||||
image: ImageSizes
|
||||
artists: list[ArtistType] # API returns "artists", not "artist"
|
||||
class VideoType(ModelBase):
|
||||
id: Annotated[str, Field(description="Video ID")]
|
||||
production_status: Annotated[str, Field(description="Production status")]
|
||||
song_title: Annotated[str, Field(description="Song title")]
|
||||
url: Annotated[str, Field(description="Video URL")]
|
||||
multiple_versions: Annotated[bool, Field(description="Whether the video has multiple versions")]
|
||||
version_name: Annotated[str | None, Field(description="Version name")]
|
||||
version_number: Annotated[int, Field(description="Version number")]
|
||||
is_imvdb_pick: Annotated[bool, Field(description="Whether the video is an IMVDB pick")]
|
||||
aspect_ratio: Annotated[str | None, Field(description="Aspect ratio")]
|
||||
year: Annotated[int, Field(description="Year")]
|
||||
verified_credits: Annotated[bool, Field(description="Whether the video has verified credits")]
|
||||
image: Annotated[ImageSizes, Field(description="Image sizes")]
|
||||
artists: Annotated[Sequence[ArtistType], Field(description="List of artists")] # API returns "artists", not "artist"
|
||||
|
||||
|
||||
class SearchResponseType(TypedDict):
|
||||
total: int
|
||||
current_page: int
|
||||
total_pages: int
|
||||
per_page: int
|
||||
results: list[VideoType]
|
||||
class SearchResponseType(ModelBase):
|
||||
total: Annotated[int, Field(description="Total number of results")]
|
||||
current_page: Annotated[int, Field(description="Current page number")]
|
||||
total_pages: Annotated[int, Field(description="Total number of pages")]
|
||||
per_page: Annotated[int, Field(description="Number of results per page")]
|
||||
results: Annotated[Sequence[VideoType], Field(description="List of video results")]
|
||||
|
||||
Reference in New Issue
Block a user