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