diff --git a/VERSION b/VERSION
index 98e25a96..fbdb6630 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.16.5-develop75
+1.16.5-develop76
diff --git a/docs/metadata/details/setting.md b/docs/metadata/details/setting.md
index aa1c123e..eb34e450 100644
--- a/docs/metadata/details/setting.md
+++ b/docs/metadata/details/setting.md
@@ -2,30 +2,32 @@
All the following attributes serve various functions as how the collection/playlist functions inside of Plex Meta Manager.
-| Attribute | Description & Values |
-|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `name` | **Description:** Used to specify the name off the collection/playlist in Plex as different then the mapping name.
**Values:** Any String |
-| `limit` | **Description:** Used to specify the max number of items for the collection/playlist
**Values:** Number greater then 0 |
-| `template` | **Description:** Used to specify a template and template variables to use for this collection/playlist. See the [Templates Page](../templates) for more information.
**Values:** Dictionary |
-| `run_again` | **Description:** Used to try and add all the missing items to the collection/playlist again after the daily run.
**Default:** `false`
**Values:** `true` or `false` |
-| `sync_mode` | **Description:** Used to change how builders sync with this collection/playlist.
**Default:** `sync_mode` [settings value](../../config/settings) in the Configuration File
**Values:**
`append` | Only Add Items to the Collection |
`sync` | Add & Remove Items from the Collection |
|
-| `minimum_items` | **Description:** Minimum items that must be found to add to a collection/playlist.
**Default:** `minimum_items` [settings value](../../config/settings) in the Configuration File
**Values:** number greater then 0 |
-| `delete_below_minimum` | **Description:** Deletes the collection/playlist if below the minimum.
**Default:** `delete_below_minimum` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `delete_not_scheduled` | **Description:** Deletes the collection/playlist if its skipped because its not scheduled.
**Default:** `delete_not_scheduled` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `tmdb_region` | **Description:** Sets the region for `tmdb_popular`, `tmdb_now_playing`, `tmdb_top_rated`, and `tmdb_upcoming` |
-| `validate_builders` | **Description:** When set to false the collection/playlist will not fail if one builder fails.
**Default:** `true`
**Values:** `true` or `false` |
-| `cache_builders` | **Description:** Caches the items found by the builders for a number of days. This is useful if you run the same configuration on multiple libraries/servers in one run just set the value to `1`.
**Default:** `0`
**Values:** number 0 or greater |
-| `blank_collection` | **Description:** When set to true the collection will be created with no builders and no items added.
**Default:** `false`
**Values:** `true` or `false` |
-| `build_collection` | **Description:** When set to false the collection won't be created but items can still be added to Radarr/Sonarr. Does not work for playlists.
**Default:** `true`
**Values:** `true` or `false` |
-| `server_preroll` | **Description:** Used to set the `Movie pre-roll video` Text box in Plex under Settings -> Extras.
You can run this with a [schedule](schedule) to change the pre-rolls automatically.
**Values:** Any String |
-| `missing_only_released` | **Description:** Collection/Playlist Level `missing_only_released` toggle.
**Default:** `missing_only_released` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `only_filter_missing` | **Description:** Collection/Playlist Level `only_filter_missing` toggle.
**Default:** `only_filter_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `show_filtered` | **Description:** Collection/Playlist level `show_filtered` toggle.
**Default:** `show_filtered` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `show_missing` | **Description:** Collection/Playlist level `show_missing` toggle.
**Default:** `show_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `save_missing` | **Description:** Collection/Playlist level `save_missing` toggle.
**Default:** `save_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
-| `ignore_ids` | **Description:** Collection/Playlist level `ignore_ids` which is combined with the library and global `ignore_ids`.
**Default:** `ignore_ids` [settings value](../../config/settings) in the Configuration File
**Values:** List or comma-separated String of TMDb/TVDb IDs |
-| `ignore_imdb_ids` | **Description:** Collection/Playlist level `ignore_imdb_ids` which is combined with the library and global `ignore_imdb_ids`.
**Default:** `ignore_imdb_ids` [settings value](../../config/settings) in the Configuration File
**Values:** List or comma-separated String of IMDb IDs |
-| `name_mapping` | **Description:** Used to specify the folder name in the [Image Assets Directory](../../home/guides/assets) i.e. if your collection/playlist name contains characters that are not allowed in file paths (i.e. for windows `<`, `>`, `:`, `"`, `/`, `\`, `?`, `*` cannot be in the file path), but you want them in your name you can this to specify the name in the file system.
**Values:** Any String |
-| `test` | **Description:** When running in Test Mode (`--run-tests` [option](../../home/environmental)) only collections/playlists with `test: true` will be run.
**Default:** `false`
**Values:** `true` or `false` |
-| `changes_webhooks` | **Description:** Used to specify a collection/playlist changes webhook for just this collection/playlist.
**Values:** List of webhooks |
+| Attribute | Description & Values |
+|:-----------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `name` | **Description:** Used to specify the name off the collection/playlist in Plex as different then the mapping name.
**Values:** Any String |
+| `limit` | **Description:** Used to specify the max number of items for the collection/playlist
**Values:** Number greater then 0 |
+| `template` | **Description:** Used to specify a template and template variables to use for this collection/playlist. See the [Templates Page](../templates) for more information.
**Values:** Dictionary |
+| `run_again` | **Description:** Used to try and add all the missing items to the collection/playlist again after the daily run.
**Default:** `false`
**Values:** `true` or `false` |
+| `sync_mode` | **Description:** Used to change how builders sync with this collection/playlist.
**Default:** `sync_mode` [settings value](../../config/settings) in the Configuration File
**Values:**`append` | Only Add Items to the Collection |
`sync` | Add & Remove Items from the Collection |
|
+| `minimum_items` | **Description:** Minimum items that must be found to add to a collection/playlist.
**Default:** `minimum_items` [settings value](../../config/settings) in the Configuration File
**Values:** number greater then 0 |
+| `delete_below_minimum` | **Description:** Deletes the collection/playlist if below the minimum.
**Default:** `delete_below_minimum` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `delete_not_scheduled` | **Description:** Deletes the collection/playlist if its skipped because its not scheduled.
**Default:** `delete_not_scheduled` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `tmdb_region` | **Description:** Sets the region for `tmdb_popular`, `tmdb_now_playing`, `tmdb_top_rated`, and `tmdb_upcoming` |
+| `validate_builders` | **Description:** When set to false the collection/playlist will not fail if one builder fails.
**Default:** `true`
**Values:** `true` or `false` |
+| `cache_builders` | **Description:** Caches the items found by the builders for a number of days. This is useful if you run the same configuration on multiple libraries/servers in one run just set the value to `1`.
**Default:** `0`
**Values:** number 0 or greater |
+| `blank_collection` | **Description:** When set to true the collection will be created with no builders and no items added.
**Default:** `false`
**Values:** `true` or `false` |
+| `build_collection` | **Description:** When set to false the collection won't be created but items can still be added to Radarr/Sonarr. Does not work for playlists.
**Default:** `true`
**Values:** `true` or `false` |
+| `server_preroll` | **Description:** Used to set the `Movie pre-roll video` Text box in Plex under Settings -> Extras.
You can run this with a [schedule](schedule) to change the pre-rolls automatically.
**Values:** Any String |
+| `missing_only_released` | **Description:** Collection/Playlist Level `missing_only_released` toggle.
**Default:** `missing_only_released` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `only_filter_missing` | **Description:** Collection/Playlist Level `only_filter_missing` toggle.
**Default:** `only_filter_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `show_filtered` | **Description:** Collection/Playlist level `show_filtered` toggle.
**Default:** `show_filtered` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `show_missing` | **Description:** Collection/Playlist level `show_missing` toggle.
**Default:** `show_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `save_missing` | **Description:** Collection/Playlist level `save_missing` toggle.
**Default:** `save_missing` [settings value](../../config/settings) in the Configuration File
**Values:** `true` or `false` |
+| `ignore_ids` | **Description:** Collection/Playlist level `ignore_ids` which is combined with the library and global `ignore_ids`.
**Default:** `ignore_ids` [settings value](../../config/settings) in the Configuration File
**Values:** List or comma-separated String of TMDb/TVDb IDs |
+| `ignore_imdb_ids` | **Description:** Collection/Playlist level `ignore_imdb_ids` which is combined with the library and global `ignore_imdb_ids`.
**Default:** `ignore_imdb_ids` [settings value](../../config/settings) in the Configuration File
**Values:** List or comma-separated String of IMDb IDs |
+| `name_mapping` | **Description:** Used to specify the folder name in the [Image Assets Directory](../../home/guides/assets) i.e. if your collection/playlist name contains characters that are not allowed in file paths (i.e. for windows `<`, `>`, `:`, `"`, `/`, `\`, `?`, `*` cannot be in the file path), but you want them in your name you can this to specify the name in the file system.
**Values:** Any String |
+| `test` | **Description:** When running in Test Mode (`--run-tests` [option](../../home/environmental)) only collections/playlists with `test: true` will be run.
**Default:** `false`
**Values:** `true` or `false` |
+| `changes_webhooks` | **Description:** Used to specify a collection/playlist changes webhook for just this collection/playlist.
**Values:** List of webhooks |
+| `sync_to_trakt_list` | **Description:** Used to specify a trakt list you want the collection/playlist synced to.
**Values:** Trakt List Slug you want to sync to |
+| `sync_missing_to_trakt_list` | **Description:** Used to also sync missing items to the Trakt List specified by `sync_to_trakt_list`.
**Values:** `ture` or `false` |
diff --git a/modules/builder.py b/modules/builder.py
index 923fc737..6b9dc7a0 100644
--- a/modules/builder.py
+++ b/modules/builder.py
@@ -419,6 +419,7 @@ class CollectionBuilder:
self.url_theme = None
self.file_theme = None
self.sync_to_trakt_list = None
+ self.sync_missing_to_trakt_list = False
self.collection_poster = None
self.collection_background = None
self.exists = False
@@ -753,7 +754,7 @@ class CollectionBuilder:
self._tautulli(method_name, method_data)
elif method_name in tmdb.builders:
self._tmdb(method_name, method_data)
- elif method_name in trakt.builders or method_name == "sync_to_trakt_list":
+ elif method_name in trakt.builders or method_name in ["sync_to_trakt_list", "sync_missing_to_trakt_list"]:
self._trakt(method_name, method_data)
elif method_name in tvdb.builders:
self._tvdb(method_name, method_data)
@@ -1465,6 +1466,8 @@ class CollectionBuilder:
if method_data not in self.config.Trakt.slugs:
raise Failed(f"{self.Type} Error: {method_data} invalid. Options {', '.join(self.config.Trakt.slugs)}")
self.sync_to_trakt_list = method_data
+ elif method_name == "sync_missing_to_trakt_list":
+ self.sync_missing_to_trakt_list = util.parse(self.Type, method_name, method_data, datatype="bool", default=False)
elif method_name in trakt.builders:
if method_name in ["trakt_chart", "trakt_userlist"]:
trakt_dicts = method_data
@@ -1982,7 +1985,7 @@ class CollectionBuilder:
else:
final_values.append(value)
else:
- final_values = util.get_list(data)
+ final_values = util.get_list(data, trim=False)
search_choices, names = self.library.get_search_choices(attribute, title=not plex_search)
valid_list = []
for value in final_values:
@@ -2714,6 +2717,9 @@ class CollectionBuilder:
if new_id:
current_ids.append(new_id)
break
+ if self.sync_missing_to_trakt_list:
+ current_ids.extend([(mm, "tmdb") for mm in self.missing_movies])
+ current_ids.extend([(ms, "tvdb") for ms in self.missing_shows])
self.config.Trakt.sync_list(self.sync_to_trakt_list, current_ids)
def delete(self):
diff --git a/modules/mal.py b/modules/mal.py
index 2b2f50f2..a0f7fc84 100644
--- a/modules/mal.py
+++ b/modules/mal.py
@@ -240,13 +240,13 @@ class MyAnimeList:
mal_ids = self._ranked(mal_ranked_name[method], data)
elif method == "mal_search":
logger.info(f"Processing {data[1]}")
- mal_ids = self._pagination("anime", params=data[0], limit=data[2])
+ mal_ids = [mal_data["mal_id"] for mal_data in self._pagination("anime", params=data[0], limit=data[2])]
elif method == "mal_genre":
logger.info(f"Processing {mal_ranked_pretty[method]} ID: {data['genre_id']}")
- mal_ids = self._pagination("anime", params={"genres": data["genre_id"]}, limit=data["limit"])
+ mal_ids = [mal_data["mal_id"] for mal_data in self._pagination("anime", params={"genres": data["genre_id"]}, limit=data["limit"])]
elif method == "mal_studio":
logger.info(f"Processing {mal_ranked_pretty[method]} ID: {data['studio_id']}")
- mal_ids = self._pagination("anime", params={"producers": data["studio_id"]}, limit=data["limit"])
+ mal_ids = [mal_data["mal_id"] for mal_data in self._pagination("anime", params={"producers": data["studio_id"]}, limit=data["limit"])]
elif method == "mal_season":
logger.info(f"Processing MyAnimeList Season: {data['limit']} Anime from {data['season'].title()} {data['year']} sorted by {pretty_names[data['sort_by']]}")
mal_ids = self._season(data["season"], data["year"], data["sort_by"], data["limit"])
diff --git a/modules/trakt.py b/modules/trakt.py
index 40cfdf89..87c304ed 100644
--- a/modules/trakt.py
+++ b/modules/trakt.py
@@ -305,20 +305,48 @@ class Trakt:
def sync_list(self, slug, ids):
current_ids = self._list(slug, urlparse=False)
+ def read_result(data, obj_type, result_type, result_str=None):
+ result_str = result_str if result_str else result_type.capitalize()
+ if data[result_type][obj_type] > 0:
+ logger.info(f"{data[result_type][obj_type]} {obj_type.capitalize()} {result_str}")
+
+ def read_not_found(data, result_str):
+ not_found = []
+ for item in data["not_found"]["movies"]:
+ not_found.append((item["ids"]["tmdb"], "tmdb"))
+ for item in data["not_found"]["shows"]:
+ not_found.append((item["ids"]["tvdb"], "tvdb"))
+ for item in data["not_found"]["seasons"]:
+ not_found.append((f"{item['ids']['tvdb']}_{item['seasons'][0]['number']}", "tvdb_season"))
+ for item in data["not_found"]["episodes"]:
+ not_found.append((f"{item['ids']['tvdb']}_{item['seasons'][0]['number']}_{item['seasons'][0]['episodes'][0]['number']}", "tvdb_episode"))
+ if not_found:
+ logger.error(f"{len(not_found)} Items Unable to {result_str}: {not_found}")
+
add_ids = [id_set for id_set in ids if id_set not in current_ids]
if add_ids:
- self._request(f"/users/me/lists/{slug}/items", json=self._build_item_json(add_ids))
+ logger.info("")
+ results = self._request(f"/users/me/lists/{slug}/items", json=self._build_item_json(add_ids))
+ for object_type in ["movies", "shows", "seasons", "episodes"]:
+ read_result(results, object_type, "added")
+ read_not_found(results, "Add")
time.sleep(1)
remove_ids = [id_set for id_set in current_ids if id_set not in ids]
if remove_ids:
- self._request(f"/users/me/lists/{slug}/items/remove", json=self._build_item_json(remove_ids))
+ logger.info("")
+ results = self._request(f"/users/me/lists/{slug}/items/remove", json=self._build_item_json(remove_ids))
+ for object_type in ["movies", "shows", "seasons", "episodes"]:
+ read_result(results, object_type, "deleted", "Removed")
+ read_not_found(results, "Remove")
time.sleep(1)
trakt_ids = self._list(slug, urlparse=False, trakt_ids=True)
trakt_lookup = {f"{ty}_{i_id}": t_id for t_id, i_id, ty in trakt_ids}
rank_ids = [trakt_lookup[f"{ty}_{i_id}"] for i_id, ty in ids if f"{ty}_{i_id}" in trakt_lookup]
self._request(f"/users/me/lists/{slug}/items/reorder", json={"rank": rank_ids})
+ logger.info("")
+ logger.info("Trakt List Ordered Successfully")
def all_user_lists(self, user="me"):
try:
diff --git a/modules/util.py b/modules/util.py
index 71bcbe78..475667c6 100644
--- a/modules/util.py
+++ b/modules/util.py
@@ -127,7 +127,7 @@ def add_dict_list(keys, value, dict_map):
else:
dict_map[key] = [value]
-def get_list(data, lower=False, upper=False, split=True, int_list=False):
+def get_list(data, lower=False, upper=False, split=True, int_list=False, trim=True):
if split is True: split = ","
if data is None: return None
elif isinstance(data, list): list_data = data
@@ -135,12 +135,15 @@ def get_list(data, lower=False, upper=False, split=True, int_list=False):
elif split is False: list_data = [str(data)]
else: list_data = str(data).split(split)
- if lower is True: return [str(d).strip().lower() for d in list_data]
- elif upper is True: return [str(d).strip().upper() for d in list_data]
+ def get_str(input_data):
+ return str(input_data).strip() if trim else str(input_data)
+
+ if lower is True: return [get_str(d).lower() for d in list_data]
+ elif upper is True: return [get_str(d).upper() for d in list_data]
elif int_list is True:
- try: return [int(str(d).strip()) for d in list_data]
+ try: return [int(get_str(d)) for d in list_data]
except ValueError: return []
- else: return [d if isinstance(d, dict) else str(d).strip() for d in list_data]
+ else: return [d if isinstance(d, dict) else get_str(d) for d in list_data]
def get_int_list(data, id_type):
int_values = []