diff --git a/VERSION b/VERSION index 92bd9dc9..151e4512 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.17.3-develop15 +1.17.3-develop16 diff --git a/docs/metadata/builders/plex.md b/docs/metadata/builders/plex.md index 62bb0755..6de53fc5 100644 --- a/docs/metadata/builders/plex.md +++ b/docs/metadata/builders/plex.md @@ -7,6 +7,7 @@ No configuration is required for these builders. | Attribute | Description | Works with Movies | Works with Shows | Works with Playlists and Custom Sort | |:----------------------------------------------|:-------------------------------------------------------------------------|:-----------------:|:----------------:|:------------------------------------:| | [`plex_all`](#plex-all) | Gets every movie/show in your library. Useful with [Filters](../filters) | ✅ | ✅ | ❌ | +| [`plex_watchlist`](#plex-watchlist) | Gets every movie/show in your Watchlist. | ✅ | ✅ | ✅ | | [`plex_pilots`](#plex-pilots) | Gets the first episode of every show in your library | ❌ | ✅ | ✅ | | [`plex_collectionless`](#plex-collectionless) | Gets every movie/show that is not in a collection | ✅ | ✅ | ❌ | | [`plex_search`](#plex-search) | Gets every movie/show based on the search parameters provided | ✅ | ✅ | ✅ | @@ -25,6 +26,31 @@ collections: rating.gte: 9 ``` +## Plex Watchlist + +Finds every item in your Watchlist. + +The expected input is the sort you want returned. It defaults to `added.asc`. + +### Sort Options + +| Sort Option | Description | +|:--------------------------------------------|:--------------------------------------------| +| `title.asc`
`title.desc` | Sort by Title | +| `release.asc`
`release.desc` | Sort by Release Date (Originally Available) | +| `critic_rating.asc`
`critic_rating.desc` | Sort by Critic Rating | +| `added.asc`
`added.desc` | Sort by Date Added to your Watchlist | + +The `sync_mode: sync` and `collection_order: custom` Details are recommended. + +```yaml +collections: + My Watchlist: + plex_watchlist: critic_rating.desc + collection_order: custom + sync_mode: sync +``` + ## Plex Pilots Gets the first episode of every show in your library. This only works with `builder_level: episode` diff --git a/modules/builder.py b/modules/builder.py index dd9f4a37..ba80e40f 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -141,7 +141,7 @@ smart_invalid = ["collection_order", "builder_level"] smart_only = ["collection_filtering"] smart_url_invalid = ["filters", "run_again", "sync_mode", "show_filtered", "show_missing", "save_report", "smart_label"] + radarr_details + sonarr_details custom_sort_builders = [ - "plex_search", "plex_pilots", "tmdb_list", "tmdb_popular", "tmdb_now_playing", "tmdb_top_rated", + "plex_search", "plex_watchlist", "plex_pilots", "tmdb_list", "tmdb_popular", "tmdb_now_playing", "tmdb_top_rated", "tmdb_trending_daily", "tmdb_trending_weekly", "tmdb_discover", "reciperr_list", "trakt_chart", "trakt_userlist", "tvdb_list", "imdb_chart", "imdb_list", "imdb_watchlist", "stevenlu_popular", "anidb_popular", "tmdb_upcoming", "tmdb_airing_today", "tmdb_on_the_air", "trakt_list", "trakt_watchlist", "trakt_collection", "trakt_trending", "trakt_popular", "trakt_boxoffice", @@ -1275,6 +1275,10 @@ class CollectionBuilder: def _plex(self, method_name, method_data): if method_name in ["plex_all", "plex_pilots"]: self.builders.append((method_name, self.builder_level)) + elif method_name == "plex_watchlist": + if method_data not in plex.watchlist_sorts: + logger.warning(f"{self.Type} Warning: Watchlist sort: {method_data} invalid defaulting to added.asc") + self.builders.append((method_name, method_data if method_data in plex.watchlist_sorts else "added.asc")) elif method_name in ["plex_search", "plex_collectionless"]: for dict_data in util.parse(self.Type, method_name, method_data, datatype="listdict"): dict_methods = {dm.lower(): dm for dm in dict_data} @@ -1498,7 +1502,7 @@ class CollectionBuilder: logger.info(f"Builder: {method} loaded from Cache") return self.config.Cache.query_list_ids(list_key) if "plex" in method: - ids = self.library.get_rating_keys(method, value) + ids = self.library.get_rating_keys(method, value, self.playlist) elif "tautulli" in method: ids = self.library.Tautulli.get_rating_keys(value, self.playlist) elif "anidb" in method: diff --git a/modules/plex.py b/modules/plex.py index b21180ba..565cbfa8 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -18,7 +18,7 @@ from xml.etree.ElementTree import ParseError logger = util.logger -builders = ["plex_all", "plex_pilots", "plex_collectionless", "plex_search"] +builders = ["plex_all", "plex_watchlist", "plex_pilots", "plex_collectionless", "plex_search"] library_types = ["movie", "show", "artist"] search_translation = { "episode_title": "episode.title", @@ -410,6 +410,12 @@ sort_types = { "album": ("title.asc", 9, album_sorts), "track": ("title.asc", 10, track_sorts) } +watchlist_sorts = { + "added.asc": "watchlistedAt:asc", "added.desc": "watchlistedAt:desc", + "title.asc": "titleSort:asc", "title.desc": "titleSort:desc", + "release.asc": "originallyAvailableAt:asc", "release.desc": "originallyAvailableAt:desc", + "critic_rating.asc": "rating:asc", "critic_rating.desc": "rating:desc", +} class Plex(Library): def __init__(self, config, params): @@ -802,11 +808,70 @@ class Plex(Library): raise Failed(f"Collection Error: No valid Plex Collections in {collections}") return valid_collections - def get_rating_keys(self, method, data): + def get_watchlist(self, sort=None, is_playlist=False): + if is_playlist: + libtype = None + elif self.is_movie: + libtype = "movie" + else: + libtype = "show" + watchlist = self.PlexServer.myPlexAccount().watchlist(sort=watchlist_sorts[sort], libtype=libtype) + ids = [] + for item in watchlist: + tmdb_id = [] + tvdb_id = [] + imdb_id = [] + if self.config.Cache: + cache_id, _, media_type, _ = self.config.Cache.query_guid_map(item.guid) + if cache_id: + ids.append(("tmdb" if "movie" in media_type else "tvdb", cache_id)) + try: + fin = False + for guid_tag in item.guids: + url_parsed = requests.utils.urlparse(guid_tag.id) + if url_parsed.scheme == "tvdb": + if isinstance(item, Show): + ids.append(("tvdb", int(url_parsed.netloc))) + fin = True + elif url_parsed.scheme == "imdb": + imdb_id.append(url_parsed.netloc) + elif url_parsed.scheme == "tmdb": + if isinstance(item, Movie): + ids.append(("tmdb", int(url_parsed.netloc))) + fin = True + tmdb_id.append(int(url_parsed.netloc)) + if fin: + break + if fin: + continue + except requests.exceptions.ConnectionError: + continue + if imdb_id and not tmdb_id: + for imdb in imdb_id: + tmdb, tmdb_type = self.config.Convert.imdb_to_tmdb(imdb) + if tmdb: + tmdb_id.append(tmdb) + if tmdb_id and isinstance(item, Show) and not tvdb_id: + for tmdb in tmdb_id: + tvdb = self.config.Convert.tmdb_to_tvdb(tmdb) + if tvdb: + tvdb_id.append(tvdb) + if isinstance(item, Show) and tvdb_id: + ids.extend([("tvdb", t_id) for t_id in tvdb_id]) + if isinstance(item, Movie) and tmdb_id: + ids.extend([("tmdb", t_id) for t_id in tmdb_id]) + logger.debug("") + logger.debug(f"{len(ids)} Keys Found: {ids}") + return ids + + def get_rating_keys(self, method, data, is_playlist=False): items = [] if method == "plex_all": logger.info(f"Processing Plex All {data.capitalize()}s") items = self.get_all(builder_level=data) + elif method == "plex_watchlist": + logger.info(f"Processing Plex Watchlist") + return self.get_watchlist(sort=data, is_playlist=is_playlist) elif method == "plex_pilots": logger.info(f"Processing Plex Pilot {data.capitalize()}s") items = [] diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 913332ed..55d899a5 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -761,11 +761,13 @@ def run_playlists(config): logger.debug("") logger.debug(f"Builder: {method}: {value}") logger.info("") - if "plex" in method: + if method == "plex_watchlist": + ids = builder.libraries[0].get_rating_keys(method, value, True) + elif "plex" in method: ids = [] for pl_library in builder.libraries: try: - ids.extend(pl_library.get_rating_keys(method, value)) + ids.extend(pl_library.get_rating_keys(method, value, True)) except Failed as e: if builder.validate_builders: raise