From c5cd72c7774c78b79b4d26221d9b9adda0ef4148 Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Sun, 27 Mar 2022 02:26:08 -0400 Subject: [PATCH] [34] trakt builder overhaul --- VERSION | 2 +- docs/metadata/builders/trakt.md | 174 +++++++++++--------------------- docs/metadata/dynamic.md | 5 +- modules/builder.py | 31 ++++-- modules/imdb.py | 10 +- modules/meta.py | 33 +++--- modules/trakt.py | 108 ++++++++++++-------- 7 files changed, 174 insertions(+), 189 deletions(-) diff --git a/VERSION b/VERSION index 5479faaa..7fc35385 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.2-develop33 +1.16.2-develop34 diff --git a/docs/metadata/builders/trakt.md b/docs/metadata/builders/trakt.md index 8792eff0..8d619c96 100644 --- a/docs/metadata/builders/trakt.md +++ b/docs/metadata/builders/trakt.md @@ -4,31 +4,14 @@ You can find items using the features of [Trakt.tv](https://trakt.tv/) (Trakt). [Configuring Trakt](../../config/trakt) in the config is required for any of these builders. -| Attribute | Description | Works with Movies | Works with Shows | Works with Playlists and Custom Sort | -|:---------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------:|:----------------:|:------------------------------------:| -| [`trakt_list`](#trakt-list) | Finds every item in the Trakt List | ✅ | ✅ | ✅ | -| [`trakt_list_details`](#trakt-list) | Finds every item in the Trakt List and updates the collection summary with the list description | ✅ | ✅ | ✅ | -| [`trakt_watchlist`](#trakt-user-watchlist) | Finds every item in a Users Watchlist | ✅ | ✅ | ✅ | -| [`trakt_collection`](#trakt-user-collection) | Finds every item in a Users Collection | ✅ | ✅ | ✅ | -| [`trakt_trending`](#trakt-trending) | Finds the movies/shows in Trakt's Trending [Movies](https://trakt.tv/movies/trending)/[Shows](https://trakt.tv/shows/trending) list | ✅ | ✅ | ✅ | -| [`trakt_popular`](#trakt-popular) | Finds the movies/shows in Trakt's Popular [Movies](https://trakt.tv/movies/popular)/[Shows](https://trakt.tv/shows/popular) list | ✅ | ✅ | ✅ | -| [`trakt_recommended_personal`](#trakt-recommended) | Finds the movies/shows in Trakt's Personal Recommendations for your User [Movies](https://trakt.docs.apiary.io/#reference/recommendations/movies/get-movie-recommendations)/[Shows](https://trakt.docs.apiary.io/#reference/recommendations/shows/get-show-recommendations) | ✅ | ✅ | ✅ | -| [`trakt_recommended_daily`](#trakt-recommended) | Finds the movies/shows in Trakt's Daily Recommended [Movies](https://trakt.tv/movies/recommended/daily)/[Shows](https://trakt.tv/shows/recommended/daily) list | ✅ | ✅ | ✅ | -| [`trakt_recommended_weekly`](#trakt-recommended) | Finds the movies/shows in Trakt's Weekly Recommended [Movies](https://trakt.tv/movies/recommended/weekly)/[Shows](https://trakt.tv/shows/recommended/weekly) list | ✅ | ✅ | ✅ | -| [`trakt_recommended_monthly`](#trakt-recommended) | Finds the movies/shows in Trakt's Monthly Recommended [Movies](https://trakt.tv/movies/recommended/monthly)/[Shows](https://trakt.tv/shows/recommended/monthly) list | ✅ | ✅ | ✅ | -| [`trakt_recommended_yearly`](#trakt-recommended) | Finds the movies/shows in Trakt's Yearly Recommended [Movies](https://trakt.tv/movies/recommended/yearly)/[Shows](https://trakt.tv/shows/recommended/yearly) list | ✅ | ✅ | ✅ | -| [`trakt_recommended_all`](#trakt-recommended) | Finds the movies/shows in Trakt's All-Time Recommended [Movies](https://trakt.tv/movies/recommended/all)/[Shows](https://trakt.tv/shows/recommended/all) list | ✅ | ✅ | ✅ | -| [`trakt_watched_daily`](#trakt-watched) | Finds the movies/shows in Trakt's Daily Watched [Movies](https://trakt.tv/movies/watched/daily)/[Shows](https://trakt.tv/shows/watched/daily) list | ✅ | ✅ | ✅ | -| [`trakt_watched_weekly`](#trakt-watched) | Finds the movies/shows in Trakt's Weekly Watched [Movies](https://trakt.tv/movies/watched/weekly)/[Shows](https://trakt.tv/shows/watched/weekly) list | ✅ | ✅ | ✅ | -| [`trakt_watched_monthly`](#trakt-watched) | Finds the movies/shows in Trakt's Monthly Watched [Movies](https://trakt.tv/movies/watched/monthly)/[Shows](https://trakt.tv/shows/watched/monthly) list | ✅ | ✅ | ✅ | -| [`trakt_watched_yearly`](#trakt-watched) | Finds the movies/shows in Trakt's Yearly Watched [Movies](https://trakt.tv/movies/watched/yearly)/[Shows](https://trakt.tv/shows/watched/yearly) list | ✅ | ✅ | ✅ | -| [`trakt_watched_all`](#trakt-watched) | Finds the movies/shows in Trakt's All-Time Watched [Movies](https://trakt.tv/movies/watched/all)/[Shows](https://trakt.tv/shows/watched/all) list | ✅ | ✅ | ✅ | -| [`trakt_collected_daily`](#trakt-collected) | Finds the movies/shows in Trakt's Daily Collected [Movies](https://trakt.tv/movies/collected/daily)/[Shows](https://trakt.tv/shows/collected/daily) list | ✅ | ✅ | ✅ | -| [`trakt_collected_weekly`](#trakt-collected) | Finds the movies/shows in Trakt's Weekly Collected [Movies](https://trakt.tv/movies/collected/weekly)/[Shows](https://trakt.tv/shows/collected/weekly) list | ✅ | ✅ | ✅ | -| [`trakt_collected_monthly`](#trakt-collected) | Finds the movies/shows in Trakt's Monthly Collected [Movies](https://trakt.tv/movies/collected/monthly)/[Shows](https://trakt.tv/shows/collected/monthly) list | ✅ | ✅ | ✅ | -| [`trakt_collected_yearly`](#trakt-collected) | Finds the movies/shows in Trakt's Yearly Collected [Movies](https://trakt.tv/movies/collected/yearly)/[Shows](https://trakt.tv/shows/collected/yearly) list | ✅ | ✅ | ✅ | -| [`trakt_collected_all`](#trakt-collected) | Finds the movies/shows in Trakt's All-Time Collected [Movies](https://trakt.tv/movies/collected/all)/[Shows](https://trakt.tv/shows/collected/all) list | ✅ | ✅ | ✅ | -| [`trakt_boxoffice`](#trakt-box-office) | Finds the 10 movies in Trakt's Top Box Office [Movies](https://trakt.tv/movies/boxoffice) list | ✅ | ❌ | ✅ | +| Attribute | Description | Works with Movies | Works with Shows | Works with Playlists and Custom Sort | +|:--------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------:|:----------------:|:------------------------------------:| +| [`trakt_list`](#trakt-list) | Finds every movie/show in the Trakt List | ✅ | ✅ | ✅ | +| [`trakt_list_details`](#trakt-list) | Finds every movie/show in the Trakt List and updates the collection summary with the list description | ✅ | ✅ | ✅ | +| [`trakt_chart`](#trakt-chart) | Finds the movies/shows in the Trakt Chart | ✅ | ✅ | ✅ | +| [`trakt_userlist`](#trakt-userlist) | Finds every movie/show in the Trakt Userlist | ✅ | ✅ | ✅ | +| [`trakt_recommendations`](#trakt-recommendations) | Finds the movies/shows in Trakt's Personal Recommendations for your User [Movies](https://trakt.docs.apiary.io/#reference/recommendations/movies/get-movie-recommendations)/[Shows](https://trakt.docs.apiary.io/#reference/recommendations/shows/get-show-recommendations) | ✅ | ✅ | ✅ | +| [`trakt_boxoffice`](#trakt-box-office) | Finds the 10 movies in Trakt's Top Box Office [Movies](https://trakt.tv/movies/boxoffice) list | ✅ | ❌ | ✅ | ## Trakt List @@ -38,6 +21,8 @@ The expected input is a Trakt List URL. Multiple values are supported only as a The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +**Trakt Lists cannot be sorted through the API, but the list is always returned to the default list order if you own the list.** + ```yaml collections: Christmas: @@ -65,131 +50,92 @@ collections: sync_mode: sync ``` -## Trakt User Watchlist +## Trakt Chart -Finds every item in a Users Watchlist. +Finds the movies/shows in the Trakt Chart. The options are detailed below. -The expected input is a user's Trakt Username or `me`. Multiple values are supported as either a list or a comma-separated string. +The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +| Attribute | Description & Values | +|:--------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `chart` | **Description:** Which Trakt chart to query
**Values:**
`trending`Trakt's Trending [Movies](https://trakt.tv/movies/trending)/[Shows](https://trakt.tv/shows/trending) list
`popular`Trakt's Popular [Movies](https://trakt.tv/movies/popular)/[Shows](https://trakt.tv/shows/popular) list
`recommended`Trakt's Recommended [Movies](https://trakt.tv/movies/recommended)/[Shows](https://trakt.tv/shows/recommended) list
`watched`Trakt's Watched [Movies](https://trakt.tv/movies/watched)/[Shows](https://trakt.tv/shows/watched) list
`collected`Trakt's Collected [Movies](https://trakt.tv/movies/collected)/[Shows](https://trakt.tv/shows/collected) list
| +| `time_period` | **Description:** Time Period for the chart. Does not work with `trending` or `popular` chart types.
**Default:** `weekly`
**Values:** `daily`, `weekly`, `monthly`, `yearly`, or `all` | +| `limit` | **Description:** Don't return more then this number
**Default:** `10`
**Values:** Number of Items to query. | -```yaml -collections: - Trakt Watchlist: - trakt_watchlist: me - collection_order: custom - sync_mode: sync -``` - -## Trakt User Collection +These are the links to the trakt charts that is looked at by time period. -Finds every item in a Users Collection. - -The expected input is a user's Trakt Username or `me`. Multiple values are supported as either a list or a comma-separated string. - -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. - -```yaml -collections: - Trakt Collection: - trakt_collection: - - me - - traktbuddy - sync_mode: sync -``` - -## Trakt Trending - -Finds the movies/shows in Trakt's Trending [Movies](https://trakt.tv/movies/trending)/[Shows](https://trakt.tv/shows/trending) list. - -The expected input is a single integer value of how many movies/shows to query. - -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +| Period | Collected | Recommended | Watched | +|:---------|:-----------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------------------:| +| Daily | [Movies](https://trakt.tv/movies/collected/daily)/[Shows](https://trakt.tv/shows/collected/daily) | [Movies](https://trakt.tv/movies/recommended/daily)/[Shows](https://trakt.tv/shows/recommended/daily) | [Movies](https://trakt.tv/movies/watched/daily)/[Shows](https://trakt.tv/shows/watched/daily) | +| Weekly | [Movies](https://trakt.tv/movies/collected/weekly)/[Shows](https://trakt.tv/shows/collected/weekly) | [Movies](https://trakt.tv/movies/recommended/weekly)/[Shows](https://trakt.tv/shows/recommended/weekly) | [Movies](https://trakt.tv/movies/watched/weekly)/[Shows](https://trakt.tv/shows/watched/weekly) | +| Monthly | [Movies](https://trakt.tv/movies/collected/monthly)/[Shows](https://trakt.tv/shows/collected/monthly) | [Movies](https://trakt.tv/movies/recommended/monthly)/[Shows](https://trakt.tv/shows/recommended/monthly) | [Movies](https://trakt.tv/movies/watched/monthly)/[Shows](https://trakt.tv/shows/watched/monthly) | +| Yearly | [Movies](https://trakt.tv/movies/collected/yearly)/[Shows](https://trakt.tv/shows/collected/yearly) | [Movies](https://trakt.tv/movies/recommended/yearly)/[Shows](https://trakt.tv/shows/recommended/yearly) | [Movies](https://trakt.tv/movies/watched/yearly)/[Shows](https://trakt.tv/shows/watched/yearly) | +| All-Time | [Movies](https://trakt.tv/movies/collected/all)/[Shows](https://trakt.tv/shows/collected/all) | [Movies](https://trakt.tv/movies/recommended/all)/[Shows](https://trakt.tv/shows/recommended/all) | [Movies](https://trakt.tv/movies/watched/all)/[Shows](https://trakt.tv/shows/watched/all) | ```yaml collections: Trakt Trending: - trakt_trending: 30 + trakt_chart: + chart: trending + limit: 30 collection_order: custom sync_mode: sync ``` -## Trakt Popular - -Finds the movies/shows in Trakt's Popular [Movies](https://trakt.tv/movies/popular)/[Shows](https://trakt.tv/shows/popular) list. - -The expected input is a single integer value of how many movies/shows to query. - -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +You can use multiple charts in one builder using a list. ```yaml collections: - Trakt Popular: - trakt_popular: 30 + Trakt Trending & Popular: + trakt_chart: + - chart: trending + limit: 30 + - chart: popular + limit: 30 collection_order: custom sync_mode: sync ``` -## Trakt Recommended +## Trakt Userlist -Finds the movies/shows in Trakt's Recommended lists. +Finds every movie/show in the Trakt Userlist. -| Builder | Period | Movie List | Show List | -|:-----------------------------|:--------:|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------:| -| `trakt_recommended_personal` | Custom | [Movies](https://trakt.docs.apiary.io/#reference/recommendations/movies/get-movie-recommendations) | [Shows](https://trakt.docs.apiary.io/#reference/recommendations/shows/get-show-recommendations) | -| `trakt_recommended_daily` | Daily | [Daily Movies](https://trakt.tv/movies/recommended/daily) | [Daily Shows](https://trakt.tv/shows/recommended/daily) | -| `trakt_recommended_weekly` | Weekly | [Weekly Movies](https://trakt.tv/movies/recommended/weekly) | [Weekly Shows](https://trakt.tv/shows/recommended/weekly) | -| `trakt_recommended_monthly` | Monthly | [Monthly Movies](https://trakt.tv/movies/recommended/monthly) | [Monthly Shows](https://trakt.tv/shows/recommended/monthly) | -| `trakt_recommended_yearly` | Yearly | [Yearly Movies](https://trakt.tv/movies/recommended/yearly) | [Yearly Shows](https://trakt.tv/shows/recommended/yearly) | -| `trakt_recommended_all` | All-Time | [All-Time Movies](https://trakt.tv/movies/recommended/all) | [All-Time Shows](https://trakt.tv/shows/recommended/all) | +The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. -The expected input is a single integer value of how many movies/shows to query. - -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +| Attribute | Description & Values | +|:-----------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `userlist` | **Description:** Which Trakt userlist to query
**Values:**
`trending`Trakt's Trending [Movies](https://trakt.tv/movies/trending)/[Shows](https://trakt.tv/shows/trending) list
`popular`Trakt's Popular [Movies](https://trakt.tv/movies/popular)/[Shows](https://trakt.tv/shows/popular) list
`recommended`Trakt's Recommended [Movies](https://trakt.tv/movies/recommended)/[Shows](https://trakt.tv/shows/recommended) list
`watched`Trakt's Watched [Movies](https://trakt.tv/movies/watched)/[Shows](https://trakt.tv/shows/watched) list
`collected`Trakt's Collected [Movies](https://trakt.tv/movies/collected)/[Shows](https://trakt.tv/shows/collected) list
| +| `user` | **Description:** The User who's user lists you want to query.
**Default:** `me`
**Values:** Username of User or `me` for the authenticated user. | +| `sort_by` | **Description:** How to sort the results
**Default:** `rank`
**Values:** `rank`, `added`, `released`, `title` | ```yaml collections: - Trakt Recommended: - trakt_recommended_weekly: 30 + Trakt Watchlist: + trakt_userlist: + userlist: watchlist + user: me + sort_by: released collection_order: custom sync_mode: sync ``` -## Trakt Watched - -Finds the movies/shows in Trakt's Watched lists. - -| Builder | Period | Movie List | Show List | -|:------------------------|:--------:|:---------------------------------------------------------:|:-------------------------------------------------------:| -| `trakt_watched_daily` | Daily | [Daily Movies](https://trakt.tv/movies/watched/daily) | [Daily Shows](https://trakt.tv/shows/watched/daily) | -| `trakt_watched_weekly` | Weekly | [Weekly Movies](https://trakt.tv/movies/watched/weekly) | [Weekly Shows](https://trakt.tv/shows/watched/weekly) | -| `trakt_watched_monthly` | Monthly | [Monthly Movies](https://trakt.tv/movies/watched/monthly) | [Monthly Shows](https://trakt.tv/shows/watched/monthly) | -| `trakt_watched_yearly` | Yearly | [Yearly Movies](https://trakt.tv/movies/watched/yearly) | [Yearly Shows](https://trakt.tv/shows/watched/yearly) | -| `trakt_watched_all` | All-Time | [All-Time Movies](https://trakt.tv/movies/watched/all) | [All-Time Shows](https://trakt.tv/shows/watched/all) | - -The expected input is a single integer value of how many movies/shows to query. - -The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order. +You can use multiple charts in one builder using a list. ```yaml collections: - Trakt Watched: - trakt_watched_weekly: 30 + Trakt Watchlist: + trakt_userlist: + - userlist: watched + user: me + - userlist: collected + user: me collection_order: custom sync_mode: sync ``` -## Trakt Collected +## Trakt Recommendations -Finds the movies/shows in Trakt's Collected [Movies](https://trakt.tv/movies/collected/weekly)/[Shows](https://trakt.tv/shows/collected/weekly) list. - -| Builder | Period | Movie List | Show List | -|:--------------------------|:--------:|:-----------------------------------------------------------:|:---------------------------------------------------------:| -| `trakt_collected_daily` | Daily | [Daily Movies](https://trakt.tv/movies/collected/daily) | [Daily Shows](https://trakt.tv/shows/collected/daily) | -| `trakt_collected_weekly` | Weekly | [Weekly Movies](https://trakt.tv/movies/collected/weekly) | [Weekly Shows](https://trakt.tv/shows/collected/weekly) | -| `trakt_collected_monthly` | Monthly | [Monthly Movies](https://trakt.tv/movies/collected/monthly) | [Monthly Shows](https://trakt.tv/shows/collected/monthly) | -| `trakt_collected_yearly` | Yearly | [Yearly Movies](https://trakt.tv/movies/collected/yearly) | [Yearly Shows](https://trakt.tv/shows/collected/yearly) | -| `trakt_collected_all` | All-Time | [All-Time Movies](https://trakt.tv/movies/collected/all) | [All-Time Shows](https://trakt.tv/shows/collected/all) | +Finds the movies/shows in Trakt's Recommendations for [Movies](https://trakt.docs.apiary.io/#reference/recommendations/movies/get-movie-recommendations)/[Shows](https://trakt.docs.apiary.io/#reference/recommendations/shows/get-show-recommendations) The expected input is a single integer value of how many movies/shows to query. @@ -197,8 +143,8 @@ The `sync_mode: sync` and `collection_order: custom` Details are recommended sin ```yaml collections: - Trakt Collected: - trakt_collected_weekly: 30 + Trakt Recommendations: + trakt_recommendations: 30 collection_order: custom sync_mode: sync ``` diff --git a/docs/metadata/dynamic.md b/docs/metadata/dynamic.md index 9e21c1cc..398d098d 100644 --- a/docs/metadata/dynamic.md +++ b/docs/metadata/dynamic.md @@ -120,7 +120,7 @@ Depending on the `type` of dynamic collection, `data` is used to specify the opt | [`genre`](#genre) | Create a collection for each genre found in the library | ❌ | ✅ | ✅ | ✅ | ✅ | | [`content_rating`](#content-rating) | Create a collection for each content rating found in the library | ❌ | ✅ | ✅ | ❌ | ✅ | | [`year`](#year) | Create a collection for each year found in the library | ❌ | ✅ | ✅ | ❌ | ❌ | -| [`decade`](#decade) | Create a collection for each decade found in the library | ❌ | ✅ | ❌ | ❌ | ❌ | +| [`decade`](#decade) | Create a collection for each decade found in the library | ❌ | ✅ | ✅ | ❌ | ❌ | | [`country`](#country) | Create a collection for each country found in the library | ❌ | ✅ | ❌ | ✅ | ✅ | | [`resolution`](#resolution) | Create a collection for each resolution found in the library | ❌ | ✅ | ✅ | ❌ | ❌ | | [`subtitle_language`](#subtitle-language) | Create a collection for each subtitle language found in the library | ❌ | ✅ | ✅ | ❌ | ❌ | @@ -1585,7 +1585,6 @@ Defines how multiple `keys` can be combined under a parent key. For example, the `addons` attribute can be used to combine multiple `keys`, i.e. merging "MTV2", "MTV3" and "MTV (UK)" into the "MTV" collection. - ```yaml dynamic_collections: networks: @@ -1597,6 +1596,8 @@ dynamic_collections: - MTV (UK) ``` +You can also define custom keys under addons if the main key doesn't exist as a key it will be considered a custom key combining all keys into one key. + ## Template Name of the template to use for these dynamic collections. Each `type` has its own default template, but if you want to define and use your own template you can. diff --git a/modules/builder.py b/modules/builder.py index b0139667..67c40ccf 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -47,6 +47,7 @@ method_alias = { "trakt_recommended": "trakt_recommended_weekly", "trakt_watched": "trakt_watched_weekly", "trakt_collected": "trakt_collected_weekly", "collection_changes_webhooks": "changes_webhooks", "radarr_add": "radarr_add_missing", "sonarr_add": "sonarr_add_missing", + "trakt_recommended_personal": "trakt_recommendations" } filter_translation = { "record_label": "studio", @@ -745,7 +746,7 @@ class CollectionBuilder: elif method_name == "tvdb_description": self.summaries[method_name] = self.config.TVDb.get_list_description(method_data) elif method_name == "trakt_description": - self.summaries[method_name] = self.config.Trakt.list_description(self.config.Trakt.validate_trakt(method_data, self.library.is_movie)[0]) + self.summaries[method_name] = self.config.Trakt.list_description(self.config.Trakt.validate_list(method_data, self.library.is_movie)[0]) elif method_name == "letterboxd_description": self.summaries[method_name] = self.config.Letterboxd.get_list_description(method_data, self.language) elif method_name == "icheckmovies_description": @@ -1068,7 +1069,7 @@ class CollectionBuilder: else: raise Failed(f"{self.Type} Error: imdb_id {value} must begin with tt") elif method_name == "imdb_list": - for imdb_dict in self.config.IMDb.validate_imdb_lists(method_data, self.language): + for imdb_dict in self.config.IMDb.validate_imdb_lists(self.Type, method_data, self.language): self.builders.append((method_name, imdb_dict)) elif method_name == "imdb_chart": for value in util.get_list(method_data): @@ -1265,21 +1266,37 @@ class CollectionBuilder: def _trakt(self, method_name, method_data): if method_name.startswith("trakt_list"): - trakt_lists = self.config.Trakt.validate_trakt(method_data, self.library.is_movie) + trakt_lists = self.config.Trakt.validate_list(method_data) for trakt_list in trakt_lists: self.builders.append(("trakt_list", trakt_list)) if method_name.endswith("_details"): self.summaries[method_name] = self.config.Trakt.list_description(trakt_lists[0]) - elif method_name in ["trakt_watchlist", "trakt_collection"]: - for trakt_list in self.config.Trakt.validate_trakt(method_data, self.library.is_movie, trakt_type=method_name[6:]): - self.builders.append((method_name, trakt_list)) elif method_name == "trakt_boxoffice": if util.parse(self.Type, method_name, method_data, datatype="bool", default=False): self.builders.append((method_name, 10)) else: raise Failed(f"{self.Type} Error: {method_name} must be set to true") + elif method_name == "trakt_recommendations": + self.builders.append((method_name, util.parse(self.Type, method_name, method_data, datatype="int", default=10, maximum=100))) elif method_name in trakt.builders: - self.builders.append((method_name, util.parse(self.Type, method_name, method_data, datatype="int", default=10))) + if method_name in ["trakt_chart", "trakt_userlist"]: + trakt_dicts = method_data + final_method = method_name + elif method_name in ["trakt_watchlist", "trakt_collection"]: + trakt_dicts = [] + for trakt_user in util.get_list(method_data, split=False): + trakt_dicts.append({"userlist": "watchlist" if "trakt_watchlist" else "collected", "user": trakt_user}) + final_method = "trakt_userlist" + else: + terms = method_name.split("_") + trakt_dicts = { + "chart": terms[1], + "amount": util.parse(self.Type, method_name, method_data, datatype="int", default=10), + "time_period": terms[2] if len(terms) > 2 else None + } + final_method = "trakt_chart" + for trakt_dict in self.config.Trakt.validate_chart(final_method, trakt_dicts, self.language): + self.builders.append((method_name, trakt_dict)) def _tvdb(self, method_name, method_data): values = util.get_list(method_data) diff --git a/modules/imdb.py b/modules/imdb.py index 10b1cafe..48bc8f7a 100644 --- a/modules/imdb.py +++ b/modules/imdb.py @@ -31,16 +31,16 @@ class IMDb: def __init__(self, config): self.config = config - def validate_imdb_lists(self, imdb_lists, language): + def validate_imdb_lists(self, err_type, imdb_lists, language): valid_lists = [] for imdb_dict in util.get_list(imdb_lists, split=False): if not isinstance(imdb_dict, dict): imdb_dict = {"url": imdb_dict} dict_methods = {dm.lower(): dm for dm in imdb_dict} if "url" not in dict_methods: - raise Failed(f"Collection Error: imdb_list url attribute not found") + raise Failed(f"{err_type} Error: imdb_list url attribute not found") elif imdb_dict[dict_methods["url"]] is None: - raise Failed(f"Collection Error: imdb_list url attribute is blank") + raise Failed(f"{err_type} Error: imdb_list url attribute is blank") else: imdb_url = imdb_dict[dict_methods["url"]].strip() if not imdb_url.startswith(tuple([v for k, v in urls.items()])): @@ -50,7 +50,7 @@ class IMDb: list_count = None if "limit" in dict_methods: if imdb_dict[dict_methods["limit"]] is None: - logger.warning(f"Collection Warning: imdb_list limit attribute is blank using 0 as default") + logger.warning(f"{err_type} Warning: imdb_list limit attribute is blank using 0 as default") else: try: value = int(str(imdb_dict[dict_methods["limit"]])) @@ -59,7 +59,7 @@ class IMDb: except ValueError: pass if list_count is None: - logger.warning(f"Collection Warning: imdb_list limit attribute must be an integer 0 or greater using 0 as default") + logger.warning(f"{err_type} Warning: imdb_list limit attribute must be an integer 0 or greater using 0 as default") if list_count is None: list_count = 0 valid_lists.append({"url": imdb_url, "limit": list_count}) diff --git a/modules/meta.py b/modules/meta.py index 506aa9de..f3796332 100644 --- a/modules/meta.py +++ b/modules/meta.py @@ -266,19 +266,14 @@ class MetadataFile(DataFile): auto_type = dynamic[methods["type"]].lower() og_exclude = util.parse("Config", "exclude", dynamic, parent=map_name, methods=methods, datatype="strlist") if "exclude" in methods else [] include = util.parse("Config", "include", dynamic, parent=map_name, methods=methods, datatype="strlist") if "include" in methods else [] - custom_keys = util.parse("Config", "custom_keys", dynamic, parent=map_name, methods=methods, datatype="dictliststr") if "custom_keys" in methods else {} if og_exclude and include: raise Failed(f"Config Error: {map_name} cannot have both include and exclude attributes") addons = util.parse("Config", "addons", dynamic, parent=map_name, methods=methods, datatype="dictliststr") if "addons" in methods else {} exclude = [str(e) for e in og_exclude] - for custom_key, combined_keys in custom_keys.items(): - for combined_key in combined_keys: - if combined_key not in exclude: - exclude.append(combined_key) for k, v in addons.items(): if k in v: - logger.warning(f"Config Warning: {k} cannot be an addon for itself") - exclude.extend([y for y in v if y != k]) + raise Failed(f"Config Warning: {k} cannot be an addon for itself") + exclude.extend([y for y in v if y != k and y not in exclude]) default_title_format = "<>" default_template = None auto_list = {} @@ -409,9 +404,9 @@ class MetadataFile(DataFile): elif auto_type == "trakt_user_lists": dynamic_data = util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="list") for option in dynamic_data: - _check_dict(self.config.Trakt.get_user_lists(option)) + _check_dict(self.config.Trakt.all_user_lists(option)) elif auto_type == "trakt_liked_lists": - _check_dict(self.config.Trakt.get_liked_lists()) + _check_dict(self.config.Trakt.all_liked_lists()) elif auto_type == "tmdb_popular_people": dynamic_data = util.parse("Config", "data", dynamic, parent=map_name, methods=methods, datatype="int", minimum=1) _check_dict(self.config.TMDb.get_popular_people(dynamic_data)) @@ -421,15 +416,16 @@ class MetadataFile(DataFile): _check_dict(self.config.Trakt.get_people(option)) else: raise Failed(f"Config Error: {map_name} type attribute {dynamic[methods['type']]} invalid") - for custom_key, combined_keys in custom_keys.items(): - if custom_key in all_keys: - raise Failed(f"Config Error: Custom key: {custom_key} cannot be an actual key") - final_keys = [ck for ck in combined_keys if ck in all_keys] - if final_keys: - if include: - include.append(custom_key) - auto_list[custom_key] = custom_key - addons[custom_key] = final_keys + for add_key, combined_keys in addons.items(): + if add_key not in all_keys: + final_keys = [ck for ck in combined_keys if ck in all_keys] + if final_keys: + if include: + include.append(add_key) + auto_list[add_key] = add_key + addons[add_key] = final_keys + else: + logger.warning(f"Config Error: {add_key} Custom Key must have at least one Key") title_format = default_title_format if "title_format" in methods: title_format = util.parse("Config", "title_format", dynamic, parent=map_name, methods=methods, default=default_title_format) @@ -466,7 +462,6 @@ class MetadataFile(DataFile): logger.debug(f"Data: {dynamic_data}") logger.debug(f"Exclude: {exclude}") logger.debug(f"Addons: {addons}") - logger.debug(f"Custom Keys: {custom_keys}") logger.debug(f"Template: {template_name}") logger.debug(f"Template Variables: {template_variables}") logger.debug(f"Remove Prefix: {remove_prefix}") diff --git a/modules/trakt.py b/modules/trakt.py index 1167220d..6de5d222 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -8,15 +8,17 @@ logger = util.logger redirect_uri = "urn:ietf:wg:oauth:2.0:oob" base_url = "https://api.trakt.tv" builders = [ + "trakt_list", "trakt_list_details", "trakt_chart", "trakt_userlist", "trakt_boxoffice", "trakt_recommendations", "trakt_collected_daily", "trakt_collected_weekly", "trakt_collected_monthly", "trakt_collected_yearly", "trakt_collected_all", - "trakt_recommended_personal", "trakt_recommended_daily", "trakt_recommended_weekly", "trakt_recommended_monthly", "trakt_recommended_yearly", "trakt_recommended_all", + "trakt_recommended_daily", "trakt_recommended_weekly", "trakt_recommended_monthly", "trakt_recommended_yearly", "trakt_recommended_all", "trakt_watched_daily", "trakt_watched_weekly", "trakt_watched_monthly", "trakt_watched_yearly", "trakt_watched_all", - "trakt_collection", "trakt_list", "trakt_list_details", "trakt_popular", "trakt_trending", "trakt_watchlist", "trakt_boxoffice" + "trakt_collection", "trakt_popular", "trakt_trending", "trakt_watchlist" ] sorts = [ "rank", "added", "title", "released", "runtime", "popularity", "percentage", "votes", "random", "my_rating", "watched", "collected" ] +periods = ["daily", "weekly", "monthly", "yearly", "all"] id_translation = {"movie": "movie", "show": "show", "season": "show", "episode": "show", "person": "person", "list": "list"} id_types = { "movie": ("tmdb", "TMDb ID"), @@ -194,16 +196,16 @@ class Trakt: logger.error(f"Trakt Error: No {id_display} found for {name}") return ids - def get_user_lists(self, data): + def all_user_lists(self, user): try: - items = self._request(f"/users/{data}/lists") + items = self._request(f"/users/{user}/lists") except Failed: - raise Failed(f"Trakt Error: User {data} not found") + raise Failed(f"Trakt Error: User {user} not found") if len(items) == 0: - raise Failed(f"Trakt Error: User {data} has no lists") - return {self.build_user_url(data, i["ids"]["slug"]): i["name"] for i in items} + raise Failed(f"Trakt Error: User {user} has no lists") + return {self.build_user_url(user, i["ids"]["slug"]): i["name"] for i in items} - def get_liked_lists(self): + def all_liked_lists(self): items = self._request(f"/users/likes/lists") if len(items) == 0: raise Failed(f"Trakt Error: No Liked lists found") @@ -212,7 +214,7 @@ class Trakt: def build_user_url(self, user, name): return f"{base_url.replace('api.', '')}/users/{user}/lists/{name}" - def _user_list(self, data): + def _list(self, data): try: items = self._request(f"{requests.utils.urlparse(data).path}/items") except Failed: @@ -221,70 +223,94 @@ class Trakt: raise Failed(f"Trakt Error: List {data} is empty") return self._parse(items) - def _user_items(self, list_type, data, is_movie): + def _userlist(self, list_type, user, is_movie, sort_by=None): try: - items = self._request(f"/users/{data}/{list_type}/{'movies' if is_movie else 'shows'}") + url_end = "movies" if is_movie else "shows" + if sort_by: + url_end = f"{url_end}/{sort_by}" + items = self._request(f"/users/{user}/{list_type}/{url_end}") except Failed: - raise Failed(f"Trakt Error: User {data} not found") + raise Failed(f"Trakt Error: User {user} not found") if len(items) == 0: - raise Failed(f"Trakt Error: {data}'s {list_type.capitalize()} is empty") + raise Failed(f"Trakt Error: {user}'s {list_type.capitalize()} is empty") return self._parse(items, item_type="movie" if is_movie else "show") - def _user_recommendations(self, amount, is_movie): + def _recommendations(self, limit, is_movie): media_type = "Movie" if is_movie else "Show" try: - items = self._request(f"/recommendations/{'movies' if is_movie else 'shows'}/?limit={amount}") + items = self._request(f"/recommendations/{'movies' if is_movie else 'shows'}/?limit={limit}") except Failed: raise Failed(f"Trakt Error: failed to fetch {media_type} Recommendations") if len(items) == 0: raise Failed(f"Trakt Error: no {media_type} Recommendations were found") return self._parse(items, typeless=True, item_type="movie" if is_movie else "show") - def _pagenation(self, pagenation, amount, is_movie): - items = self._request(f"/{'movies' if is_movie else 'shows'}/{pagenation}?limit={amount}") - return self._parse(items, typeless=pagenation == "popular", item_type="movie" if is_movie else "show") + def _charts(self, chart_type, limit, is_movie, time_period=None): + chart_url = f"{chart_type}/{time_period}" if time_period else chart_type + items = self._request(f"/{'movies' if is_movie else 'shows'}/{chart_url}?limit={limit}") + return self._parse(items, typeless=chart_type == "popular", item_type="movie" if is_movie else "show") def get_people(self, data): - return {str(i[0][0]): i[0][1] for i in self._user_list(data) if i[1] == "tmdb_person"} + return {str(i[0][0]): i[0][1] for i in self._list(data) if i[1] == "tmdb_person"} - def validate_trakt(self, trakt_lists, is_movie, trakt_type="list"): + def validate_list(self, trakt_lists): values = util.get_list(trakt_lists, split=False) trakt_values = [] for value in values: if isinstance(value, dict): raise Failed("Trakt Error: List cannot be a dictionary") try: - if trakt_type == "list": - self._user_list(value) - else: - self._user_items(trakt_type, value, is_movie) + self._list(value) trakt_values.append(value) except Failed as e: logger.error(e) if len(trakt_values) == 0: - if trakt_type == "watchlist": - raise Failed(f"Trakt Error: No valid Trakt Watchlists in {values}") - elif trakt_type == "collection": - raise Failed(f"Trakt Error: No valid Trakt Collections in {values}") - else: - raise Failed(f"Trakt Error: No valid Trakt Lists in {values}") + raise Failed(f"Trakt Error: No valid Trakt Lists in {values}") return trakt_values + def validate_chart(self, method_name, err_type, data, is_movie): + valid_dicts = [] + for trakt_dict in util.get_list(data, split=False): + if not isinstance(trakt_dict, dict): + raise Failed(f"{err_type} Error: {method_name} must be a dictionary") + dict_methods = {dm.lower(): dm for dm in trakt_dict} + try: + if method_name == "trakt_chart": + chart = util.parse(err_type, "chart", trakt_dict, methods=dict_methods, parent=method_name, options=["recommended", "watched", "collected", "trending", "popular"]) + limit = util.parse(err_type, "limit", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", default=10) + time_period = None + if chart in ["recommended", "watched", "collected"] and "time_period" in dict_methods: + time_period = util.parse(err_type, "time_period", trakt_dict, methods=dict_methods, parent=method_name, default="weekly", options=periods) + valid_dicts.append({"chart": chart, "limit": limit, "time_period": time_period}) + else: + userlist = util.parse(err_type, "userlist", trakt_dict, methods=dict_methods, parent=method_name, options=["recommended", "watched", "collected", "watchlist"]) + user = util.parse(err_type, "user", trakt_dict, methods=dict_methods, parent=method_name, default="me") + sort_by = None + if userlist in ["recommended", "watchlist"] and "sort" in dict_methods: + sort_by = util.parse(err_type, "sort_by", trakt_dict, methods=dict_methods, parent=method_name, default="rank", options=["rank", "added", "released", "title"]) + self._userlist("collection" if userlist == "collected" else userlist, user, is_movie, sort_by=sort_by) + valid_dicts.append({"userlist": userlist, "user": user, "sort_by": sort_by}) + except Failed as e: + logger.error(e) + if len(valid_dicts) == 0: + raise Failed(f"Trakt Error: No valid Trakt {method_name[6:].capitalize()}") + return valid_dicts + def get_trakt_ids(self, method, data, is_movie): pretty = method.replace("_", " ").title() media_type = "Movie" if is_movie else "Show" - if method in ["trakt_collection", "trakt_watchlist"]: - logger.info(f"Processing {pretty} {media_type}s for {data}") - return self._user_items(method[6:], data, is_movie) - elif method == "trakt_list": + if method == "trakt_list": logger.info(f"Processing {pretty}: {data}") - return self._user_list(data) - elif method == "trakt_recommended_personal": - logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}") - return self._user_recommendations(data, is_movie) - elif method in builders: + return self._list(data) + elif method == "trakt_recommendations": logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}") - terms = method.split("_") - return self._pagenation(f"{terms[1]}{f'/{terms[2]}' if len(terms) > 2 else ''}", data, is_movie) + return self._recommendations(data, is_movie) + elif method == "trakt_chart": + chart_title = data["chart"] if data["time_period"] else f"{data['chart']} {data['time_period'].capitalize()}" + logger.info(f"Processing {pretty}: {chart_title} {data['chart'].capitalize()} {media_type}{'' if data == 1 else 's'}") + return self._charts(data["chart"], data["limit"], is_movie, time_period=data["time_period"]) + elif method == "trakt_userlist": + logger.info(f"Processing {pretty} {media_type}s from {data['user']}'s {data['userlist'].capitalize()}") + return self._userlist(data["userlist"], data["user"], is_movie, sort_by=data["sort_by"]) else: raise Failed(f"Trakt Error: Method {method} not supported")