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