d
This commit is contained in:
@@ -74,6 +74,54 @@ class MetadataProvider(ABC):
|
||||
tags.append(f"source:{self.name}")
|
||||
return tags
|
||||
|
||||
def search_tags(self, query: str, limit: int = 1) -> List[str]:
|
||||
"""Return tags for the best match from `search(query)`.
|
||||
|
||||
Providers can override this when tags should be extracted differently from
|
||||
the default search->first-item->to_tags flow.
|
||||
"""
|
||||
|
||||
try:
|
||||
items = self.search(query, limit=max(1, int(limit)))
|
||||
except Exception:
|
||||
return []
|
||||
if not items:
|
||||
return []
|
||||
try:
|
||||
return [str(t) for t in self.to_tags(items[0]) if t is not None]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def identifier_query(self, identifiers: Dict[str, Any]) -> Optional[str]:
|
||||
"""Return provider-specific identifier query text from parsed identifiers."""
|
||||
|
||||
_ = identifiers
|
||||
return None
|
||||
|
||||
def combined_query(
|
||||
self,
|
||||
*,
|
||||
title_hint: Optional[str],
|
||||
artist_hint: Optional[str],
|
||||
) -> Optional[str]:
|
||||
"""Return provider-specific title+artist query text."""
|
||||
|
||||
_ = title_hint
|
||||
_ = artist_hint
|
||||
return None
|
||||
|
||||
def extract_url_query(self, result: Any, get_field: Any) -> Optional[str]:
|
||||
"""Return provider-specific URL query derived from a piped result."""
|
||||
|
||||
_ = result
|
||||
_ = get_field
|
||||
return None
|
||||
|
||||
def emits_direct_tags(self) -> bool:
|
||||
"""True when provider should skip selection table and emit tags directly."""
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class ITunesProvider(MetadataProvider):
|
||||
"""Metadata provider using the iTunes Search API."""
|
||||
@@ -112,6 +160,21 @@ class ITunesProvider(MetadataProvider):
|
||||
debug(f"iTunes returned {len(items)} items for '{query}'")
|
||||
return items
|
||||
|
||||
def identifier_query(self, identifiers: Dict[str, Any]) -> Optional[str]:
|
||||
return identifiers.get("musicbrainz") or identifiers.get("musicbrainzalbum")
|
||||
|
||||
def combined_query(
|
||||
self,
|
||||
*,
|
||||
title_hint: Optional[str],
|
||||
artist_hint: Optional[str],
|
||||
) -> Optional[str]:
|
||||
title_text = str(title_hint or "").strip()
|
||||
artist_text = str(artist_hint or "").strip()
|
||||
if not title_text or not artist_text:
|
||||
return None
|
||||
return f"{title_text} {artist_text}"
|
||||
|
||||
|
||||
class OpenLibraryMetadataProvider(MetadataProvider):
|
||||
"""Metadata provider for OpenLibrary book metadata."""
|
||||
@@ -220,6 +283,14 @@ class OpenLibraryMetadataProvider(MetadataProvider):
|
||||
tags.append(f"source:{self.name}")
|
||||
return tags
|
||||
|
||||
def identifier_query(self, identifiers: Dict[str, Any]) -> Optional[str]:
|
||||
return (
|
||||
identifiers.get("isbn_13")
|
||||
or identifiers.get("isbn_10")
|
||||
or identifiers.get("isbn")
|
||||
or identifiers.get("openlibrary")
|
||||
)
|
||||
|
||||
|
||||
class GoogleBooksMetadataProvider(MetadataProvider):
|
||||
"""Metadata provider for Google Books volumes API."""
|
||||
@@ -329,6 +400,14 @@ class GoogleBooksMetadataProvider(MetadataProvider):
|
||||
tags.append(f"source:{self.name}")
|
||||
return tags
|
||||
|
||||
def identifier_query(self, identifiers: Dict[str, Any]) -> Optional[str]:
|
||||
return (
|
||||
identifiers.get("isbn_13")
|
||||
or identifiers.get("isbn_10")
|
||||
or identifiers.get("isbn")
|
||||
or identifiers.get("openlibrary")
|
||||
)
|
||||
|
||||
|
||||
class ISBNsearchMetadataProvider(MetadataProvider):
|
||||
"""Metadata provider that scrapes isbnsearch.org by ISBN.
|
||||
@@ -624,6 +703,18 @@ class MusicBrainzMetadataProvider(MetadataProvider):
|
||||
tags.append(f"musicbrainz:{mbid}")
|
||||
return tags
|
||||
|
||||
def combined_query(
|
||||
self,
|
||||
*,
|
||||
title_hint: Optional[str],
|
||||
artist_hint: Optional[str],
|
||||
) -> Optional[str]:
|
||||
title_text = str(title_hint or "").strip()
|
||||
artist_text = str(artist_hint or "").strip()
|
||||
if not title_text or not artist_text:
|
||||
return None
|
||||
return f'recording:"{title_text}" AND artist:"{artist_text}"'
|
||||
|
||||
|
||||
class ImdbMetadataProvider(MetadataProvider):
|
||||
"""Metadata provider for IMDb titles (movies/series/episodes)."""
|
||||
@@ -757,6 +848,9 @@ class ImdbMetadataProvider(MetadataProvider):
|
||||
deduped.append(s)
|
||||
return deduped
|
||||
|
||||
def identifier_query(self, identifiers: Dict[str, Any]) -> Optional[str]:
|
||||
return identifiers.get("imdb")
|
||||
|
||||
|
||||
class YtdlpMetadataProvider(MetadataProvider):
|
||||
"""Metadata provider that extracts tags from a supported URL using yt-dlp.
|
||||
@@ -904,6 +998,23 @@ class YtdlpMetadataProvider(MetadataProvider):
|
||||
out.append(s)
|
||||
return out
|
||||
|
||||
def extract_url_query(self, result: Any, get_field: Any) -> Optional[str]:
|
||||
raw_url = (
|
||||
get_field(result, "url", None)
|
||||
or get_field(result, "source_url", None)
|
||||
or get_field(result, "target", None)
|
||||
)
|
||||
if isinstance(raw_url, list) and raw_url:
|
||||
raw_url = raw_url[0]
|
||||
if isinstance(raw_url, str):
|
||||
text = raw_url.strip()
|
||||
if text.startswith(("http://", "https://")):
|
||||
return text
|
||||
return None
|
||||
|
||||
def emits_direct_tags(self) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def _coerce_archive_field_list(value: Any) -> List[str]:
|
||||
"""Coerce an Archive.org metadata field to a list of strings."""
|
||||
|
||||
Reference in New Issue
Block a user