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 = []