Home Assistant Unofficial Reference 2024.12.1
media_search.py
Go to the documentation of this file.
1 """Helper methods to search for Plex media."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 from plexapi.base import PlexObject
8 from plexapi.exceptions import BadRequest, NotFound
9 from plexapi.library import LibrarySection
10 
11 from .errors import MediaNotFound
12 
13 LEGACY_PARAM_MAPPING = {
14  "show_name": "show.title",
15  "season_number": "season.index",
16  "episode_name": "episode.title",
17  "episode_number": "episode.index",
18  "artist_name": "artist.title",
19  "album_name": "album.title",
20  "track_name": "track.title",
21  "track_number": "track.index",
22  "video_name": "movie.title",
23 }
24 
25 PREFERRED_LIBTYPE_ORDER = (
26  "episode",
27  "season",
28  "show",
29  "track",
30  "album",
31  "artist",
32 )
33 
34 
35 _LOGGER = logging.getLogger(__name__)
36 
37 
39  media_type: str,
40  library_section: LibrarySection,
41  allow_multiple: bool = False,
42  **kwargs,
43 ) -> PlexObject | list[PlexObject]:
44  """Search for specified Plex media in the provided library section.
45 
46  Returns a media item or a list of items if `allow_multiple` is set.
47 
48  Raises MediaNotFound if the search was unsuccessful.
49  """
50  original_query = kwargs.copy()
51  search_query = {}
52  libtype = kwargs.pop("libtype", None)
53 
54  # Preserve legacy service parameters
55  for legacy_key, key in LEGACY_PARAM_MAPPING.items():
56  if value := kwargs.pop(legacy_key, None):
57  _LOGGER.debug(
58  "Legacy parameter '%s' used, consider using '%s'", legacy_key, key
59  )
60  search_query[key] = value
61 
62  search_query.update(**kwargs)
63 
64  if not libtype:
65  # Default to a sane libtype if not explicitly provided
66  for preferred_libtype in PREFERRED_LIBTYPE_ORDER:
67  if any(key.startswith(preferred_libtype) for key in search_query):
68  libtype = preferred_libtype
69  break
70 
71  search_query.update(libtype=libtype)
72  _LOGGER.debug("Processed search query: %s", search_query)
73 
74  try:
75  results = library_section.search(**search_query)
76  except (BadRequest, NotFound) as exc:
77  raise MediaNotFound(f"Problem in query {original_query}: {exc}") from exc
78 
79  if not results:
80  raise MediaNotFound(
81  f"No {media_type} results in '{library_section.title}' for {original_query}"
82  )
83 
84  if len(results) > 1:
85  if allow_multiple:
86  return results
87 
88  if title := search_query.get("title") or search_query.get("movie.title"):
89  exact_matches = [x for x in results if x.title.lower() == title.lower()]
90  if len(exact_matches) == 1:
91  return exact_matches[0]
92  raise MediaNotFound(
93  "Multiple matches, make content_id more specific or use `allow_multiple`:"
94  f" {results}"
95  )
96 
97  return results[0]
PlexObject|list[PlexObject] search_media(str media_type, LibrarySection library_section, bool allow_multiple=False, **kwargs)
Definition: media_search.py:43