diff --git a/CHANGELOG b/CHANGELOG index 9d89cbf2..a2fd83a6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,9 +1,11 @@ # Requirements Update (requirements will need to be reinstalled) Updated lxml requirement to 5.1.0 +Updated gitpython requirement to 3.1.41 # New Features # Updates +Added new [`trakt_chart` attributes](https://metamanager.wiki/en/latest/files/builders/trakt/#trakt-chart) `network_ids`, `studio_ids`, `votes`, `tmdb_ratings`, `tmdb_votes`, `imdb_ratings`, `imdb_votes`, `rt_meters`, `rt_user_meters`, `metascores` and removed the deprecated `network` attribute # Defaults diff --git a/VERSION b/VERSION index 3e7c8bf9..b6732497 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.20.0-develop7 +1.20.0-develop8 diff --git a/docs/files/builders/trakt.md b/docs/files/builders/trakt.md index f170fe89..7673a31a 100644 --- a/docs/files/builders/trakt.md +++ b/docs/files/builders/trakt.md @@ -76,8 +76,17 @@ The `sync_mode: sync` and `collection_order: custom` Setting are recommended sin | `countries` | **Description:** Search for the specified countries only
**Values:** Comma separated string or list of countries
**Movie Countries:** `af`, `al`, `dz`, `as`, `ad`, `ao`, `ai`, `aq`, `ag`, `ar`, `am`, `aw`, `au`, `at`, `az`, `bs`, `bh`, `bd`, `bb`, `by`, `be`, `bz`, `bj`, `bm`, `bt`, `bo`, `ba`, `bw`, `bv`, `br`, `io`, `bn`, `bg`, `bf`, `bi`, `cv`, `kh`, `cm`, `ca`, `ky`, `cf`, `td`, `cl`, `cn`, `cx`, `co`, `km`, `cg`, `cd`, `ck`, `cr`, `hr`, `cu`, `cy`, `cz`, `ci`, `dk`, `dj`, `dm`, `do`, `ec`, `eg`, `sv`, `gq`, `er`, `ee`, `sz`, `et`, `fk`, `fo`, `fj`, `fi`, `fr`, `gf`, `pf`, `tf`, `ga`, `gm`, `ge`, `de`, `gh`, `gi`, `gr`, `gl`, `gd`, `gp`, `gu`, `gt`, `gn`, `gw`, `gy`, `ht`, `va`, `hn`, `hk`, `hu`, `is`, `in`, `id`, `ir`, `iq`, `ie`, `il`, `it`, `jm`, `jp`, `jo`, `kz`, `ke`, `ki`, `kp`, `kr`, `kw`, `kg`, `la`, `lv`, `lb`, `ls`, `lr`, `ly`, `li`, `lt`, `lu`, `mo`, `mg`, `mw`, `my`, `mv`, `ml`, `mt`, `mh`, `mq`, `mr`, `mu`, `yt`, `mx`, `md`, `mc`, `mn`, `me`, `ms`, `ma`, `mz`, `mm`, `na`, `nr`, `np`, `nl`, `nc`, `nz`, `ni`, `ne`, `ng`, `nf`, `mk`, `mp`, `no`, `om`, `pk`, `pw`, `ps`, `pa`, `pg`, `py`, `pe`, `ph`, `pn`, `pl`, `pt`, `pr`, `qa`, `ro`, `ru`, `rw`, `re`, `sh`, `kn`, `lc`, `vc`, `ws`, `sm`, `st`, `sa`, `sn`, `rs`, `sc`, `sl`, `sg`, `sk`, `si`, `sb`, `so`, `za`, `ss`, `es`, `lk`, `sd`, `sr`, `se`, `ch`, `sy`, `tw`, `tj`, `tz`, `th`, `tl`, `tg`, `tk`, `to`, `tt`, `tn`, `tr`, `tm`, `tc`, `tv`, `ug`, `ua`, `ae`, `gb`, `us`, `um`, `uy`, `uz`, `vu`, `ve`, `vn`, `vg`, `vi`, `wf`, `eh`, `ye`, `zm`, `zw`
**Show Countries:** `af`, `ad`, `ar`, `am`, `au`, `at`, `bd`, `by`, `be`, `bz`, `ba`, `bw`, `br`, `io`, `bg`, `kh`, `ca`, `td`, `cl`, `cn`, `co`, `hr`, `cu`, `cy`, `cz`, `dk`, `do`, `ec`, `eg`, `ee`, `sz`, `fi`, `fr`, `ge`, `de`, `gr`, `hn`, `hk`, `hu`, `is`, `in`, `id`, `ir`, `iq`, `ie`, `il`, `it`, `jp`, `jo`, `kz`, `kp`, `kr`, `kw`, `la`, `lv`, `lb`, `lt`, `lu`, `my`, `mv`, `mt`, `mx`, `md`, `mc`, `me`, `ma`, `np`, `nl`, `nz`, `ng`, `mk`, `mp`, `no`, `pk`, `pa`, `py`, `pe`, `ph`, `pl`, `pt`, `pr`, `qa`, `ro`, `ru`, `sa`, `sn`, `rs`, `sg`, `sk`, `si`, `za`, `es`, `lk`, `se`, `ch`, `sy`, `tw`, `th`, `tg`, `tn`, `tr`, `ua`, `ae`, `gb`, `us`, `uy`, `ve`, `vn` | | `certifications` | **Description:** Search for the specified certifications only
**Values:** Comma separated string or list of certifications
**Movie Certifications:** `g`, `pg`, `pg-13`, `r`, `nr`
**Show Certifications:** `tv-y`, `tv-y7`, `tv-g`, `tv-pg`, `tv-14`, `tv-ma`, `nr` | | `runtimes` | **Description:** Search for the specified runtime range
**Values:** range of int i.e. `0-60` | -| `ratings` | **Description:** Search for the specified rating range
**Values:** range of int i.e. `80-100` | -| `networks` | **Description:** Search for the specified networks only **Only works with shows**
**Values:** Comma separated string or list of networks | +| `ratings` | **Description:** Search for the specified Trakt rating range
**Values:** range of int from `0-100` i.e. `80-100` | +| `votes` | **Description:** Search for the specified Trakt vote count range
**Values:** range of int from `0-100000` i.e. `80-100` | +| `tmdb_ratings` | **Description:** Search for the specified TMDb rating range
**Values:** range of float from `0.0-10.0` i.e. `8.5-10.0` | +| `tmdb_votes` | **Description:** Search for the specified TMDb vote count range
**Values:** range of int from `0-100000` i.e. `8.5-10.0` | +| `imdb_ratings` | **Description:** Search for the specified IMDb rating range
**Values:** range of float from `0.0-10.0` i.e. `80-100` | +| `imdb_votes` | **Description:** Search for the specified IMDb vote count range
**Values:** range of int from `0-3000000` i.e. `80-100` | +| `rt_meters` | **Description:** Search for the specified Rotten Tomatoes tomatometer range
**Values:** range of int from `0-100` i.e. `80-100` | +| `rt_user_meters` | **Description:** Search for the specified Rotten Tomatoes audience score range
**Values:** range of int from `0-100` i.e. `80-100` | +| `metascores` | **Description:** Search for the specified Metacritic score range
**Values:** range of int from `0-100` i.e. `80-100` | +| `studio_ids` | **Description:** Search for the specified Studio IDs only
**Values:** Comma separated string or list of Studio IDs | +| `network_ids` | **Description:** Search for the specified Network IDs only **Only works with shows**
**Values:** Comma separated string or list of Network IDs | | `status` | **Description:** Search for the specified status only **Only works with shows**
**Values:** Comma separated string or list of statuses
**Status:** `returning`, `production`, `planned`, `canceled`, `ended` | These are the links to the trakt charts that is looked at by time period. diff --git a/modules/trakt.py b/modules/trakt.py index 0ea74bea..e82d2000 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -129,14 +129,14 @@ class Trakt: raise Failed("Input Timeout: Trakt pin required.") if not pin: raise Failed("Trakt Error: Trakt pin required.") - json = { + json_data = { "code": pin, "client_id": self.client_id, "client_secret": self.client_secret, "redirect_uri": redirect_uri, "grant_type": "authorization_code" } - response = self.config.post(f"{base_url}/oauth/token", json=json, headers={"Content-Type": "application/json"}) + response = self.config.post(f"{base_url}/oauth/token", json=json_data, headers={"Content-Type": "application/json"}) if response.status_code != 200: raise Failed(f"Trakt Error: ({response.status_code}) {response.reason}") #raise Failed("Trakt Error: Invalid trakt pin. If you're sure you typed it in correctly your client_id or client_secret may be invalid") @@ -164,14 +164,14 @@ class Trakt: def _refresh(self): if self.authorization and "refresh_token" in self.authorization and self.authorization["refresh_token"]: logger.info("Refreshing Access Token...") - json = { + json_data = { "refresh_token": self.authorization["refresh_token"], "client_id": self.client_id, "client_secret": self.client_secret, "redirect_uri": redirect_uri, "grant_type": "refresh_token" } - response = self.config.post(f"{base_url}/oauth/token", json=json, headers={"Content-Type": "application/json"}) + response = self.config.post(f"{base_url}/oauth/token", json=json_data, headers={"Content-Type": "application/json"}) if response.status_code != 200: return False return self._save(response.json()) @@ -197,7 +197,7 @@ class Trakt: return False @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_failed) - def _request(self, url, params=None, json=None): + def _request(self, url, params=None, json_data=None): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {self.authorization['access_token']}", @@ -212,26 +212,26 @@ class Trakt: logger.trace(f"URL: {base_url}{url}") if params: logger.trace(f"Params: {params}") - if json: - logger.trace(f"JSON: {json}") + if json_data: + logger.trace(f"JSON: {json_data}") while current <= pages: if pages > 1: params["page"] = current - if json is not None: - response = self.config.post(f"{base_url}{url}", json=json, headers=headers) + if json_data is not None: + response = self.config.post(f"{base_url}{url}", json=json_data, headers=headers) else: response = self.config.get(f"{base_url}{url}", headers=headers, params=params) if pages == 1 and "X-Pagination-Page-Count" in response.headers and not params: pages = int(response.headers["X-Pagination-Page-Count"]) if response.status_code >= 400: raise Failed(f"({response.status_code}) {response.reason}") - json_data = response.json() + response_json = response.json() logger.trace(f"Headers: {response.headers}") - logger.trace(f"Response: {json_data}") - if isinstance(json_data, dict): - return json_data + logger.trace(f"Response: {response_json}") + if isinstance(response_json, dict): + return response_json else: - output_json.extend(json_data) + output_json.extend(response_json) current += 1 return output_json @@ -335,7 +335,7 @@ class Trakt: add_ids = [id_set for id_set in ids if id_set not in current_ids] if add_ids: logger.info("") - results = self._request(f"/users/me/lists/{slug}/items", json=self._build_item_json(add_ids)) + results = self._request(f"/users/me/lists/{slug}/items", json_data=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") @@ -344,7 +344,7 @@ class Trakt: remove_ids = [id_set for id_set in current_ids if id_set not in ids] if remove_ids: logger.info("") - results = self._request(f"/users/me/lists/{slug}/items/remove", json=self._build_item_json(remove_ids)) + results = self._request(f"/users/me/lists/{slug}/items/remove", json_data=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") @@ -353,7 +353,7 @@ class Trakt: 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}) + self._request(f"/users/me/lists/{slug}/items/reorder", json_data={"rank": rank_ids}) logger.info("") logger.info("Trakt List Ordered Successfully") @@ -461,6 +461,22 @@ class Trakt: final_dict["runtimes"] = util.parse(err_type, "runtimes", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", range_split="-") if "ratings" in dict_methods: final_dict["ratings"] = util.parse(err_type, "ratings", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100, range_split="-") + if "votes" in dict_methods: + final_dict["votes"] = util.parse(err_type, "votes", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100000, range_split="-") + if "tmdb_ratings" in dict_methods: + final_dict["tmdb_ratings"] = util.parse(err_type, "tmdb_ratings", trakt_dict, methods=dict_methods, parent=method_name, datatype="float", minimum=0, maximum=10, range_split="-") + if "tmdb_votes" in dict_methods: + final_dict["tmdb_votes"] = util.parse(err_type, "tmdb_votes", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100000, range_split="-") + if "imdb_ratings" in dict_methods: + final_dict["imdb_ratings"] = util.parse(err_type, "imdb_ratings", trakt_dict, methods=dict_methods, parent=method_name, datatype="float", minimum=0, maximum=10, range_split="-") + if "imdb_votes" in dict_methods: + final_dict["imdb_votes"] = util.parse(err_type, "imdb_votes", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=3000000, range_split="-") + if "rt_meters" in dict_methods: + final_dict["rt_meters"] = util.parse(err_type, "rt_meters", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100, range_split="-") + if "rt_user_meters" in dict_methods: + final_dict["rt_user_meters"] = util.parse(err_type, "rt_user_meters", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100, range_split="-") + if "metascores" in dict_methods: + final_dict["metascores"] = util.parse(err_type, "metascores", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=0, maximum=100, range_split="-") if "genres" in dict_methods: final_dict["genres"] = util.parse(err_type, "genres", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist", options=self.movie_genres if is_movie else self.show_genres) if "languages" in dict_methods: @@ -469,8 +485,10 @@ class Trakt: final_dict["countries"] = util.parse(err_type, "countries", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist", options=self.movie_countries if is_movie else self.show_countries) if "certifications" in dict_methods: final_dict["certifications"] = util.parse(err_type, "certifications", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist", options=self.movie_certifications if is_movie else self.show_certifications) - if "networks" in dict_methods and not is_movie: - final_dict["networks"] = util.parse(err_type, "networks", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist") + if "studio_ids" in dict_methods and not is_movie: + final_dict["studio_ids"] = util.parse(err_type, "studio_ids", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist") + if "network_ids" in dict_methods and not is_movie: + final_dict["network_ids"] = util.parse(err_type, "network_ids", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist") if "status" in dict_methods and not is_movie: final_dict["status"] = util.parse(err_type, "status", trakt_dict, methods=dict_methods, parent=method_name, datatype="commalist", options=status) valid_dicts.append(final_dict) @@ -501,7 +519,7 @@ class Trakt: params = {"limit": data["limit"]} chart_limit = f"{data['limit']} {data['time_period'].capitalize()}" if data["time_period"] else data["limit"] logger.info(f"Processing {pretty}: {chart_limit} {data['chart'].capitalize()} {media_type}{'' if data == 1 else 's'}") - for attr in ["query", "years", "runtimes", "ratings", "genres", "languages", "countries", "certifications", "networks", "status"]: + for attr in ["query", "years", "runtimes", "ratings", "genres", "languages", "countries", "certifications", "network_ids", "studio_ids", "status", "votes", "tmdb_ratings", "tmdb_votes", "imdb_ratings", "imdb_votes", "rt_meters", "rt_user_meters", "metascores"]: if attr in data: logger.info(f"{attr:>22}: {','.join(data[attr]) if isinstance(data[attr], list) else data[attr]}") values = [status_translation[v] for v in data[attr]] if attr == "status" else data[attr] diff --git a/modules/util.py b/modules/util.py index 839b99dc..924359f5 100644 --- a/modules/util.py +++ b/modules/util.py @@ -847,8 +847,8 @@ def parse(error, attribute, data, datatype=None, methods=None, parent=None, defa if range_split: range_values = str(value).split(range_split) if len(range_values) == 2: - start = check_int(range_values[0]) - end = check_int(range_values[1]) + start = check_int(range_values[0], datatype=datatype, minimum=minimum, maximum=maximum) + end = check_int(range_values[1], datatype=datatype, minimum=minimum, maximum=maximum) if start and end and start < end: return f"{start}{range_split}{end}" else: diff --git a/requirements.txt b/requirements.txt index 0d4d7ce3..d862ae78 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ arrapi==1.4.7 -GitPython==3.1.40 +GitPython==3.1.41 lxml==5.1.0 num2words==0.5.13 pathvalidate==3.2.0