From 01fe35d32673290fb773dd5d8afb0308b09bb577 Mon Sep 17 00:00:00 2001 From: meisnate12 Date: Mon, 28 Mar 2022 12:15:34 -0400 Subject: [PATCH] [37] #751 add trakt filters --- VERSION | 2 +- docs/metadata/builders/trakt.md | 20 +++-- docs/metadata/filters.md | 9 ++- modules/builder.py | 2 +- modules/trakt.py | 136 +++++++++++++++++++++++++++----- modules/util.py | 34 +++++--- 6 files changed, 161 insertions(+), 42 deletions(-) diff --git a/VERSION b/VERSION index 6dc0611a..6dbe536a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.16.2-develop36 +1.16.2-develop37 diff --git a/docs/metadata/builders/trakt.md b/docs/metadata/builders/trakt.md index 8d619c96..73e054c2 100644 --- a/docs/metadata/builders/trakt.md +++ b/docs/metadata/builders/trakt.md @@ -56,11 +56,21 @@ Finds the movies/shows in the Trakt Chart. The options are detailed below. 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. | +| 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. | +| `query` | **Description:** Search titles and descriptions for this
**Values:** Any String. | +| `year` | **Description:** Search for the specified years only
**Values:** 4 digit year or range of 4 digit years. i.e. `1950` or `1950-1959` | +| `genres` | **Description:** Search for the specified genres only
**Values:** Comma separated string or list of genres
**Movie Genres:** `action`, `adventure`, `animation`, `anime`, `comedy`, `crime`, `documentary`, `drama`, `family`, `fantasy`, `history`, `holiday`, `horror`, `music`, `musical`, `mystery`, `none`, `romance`, `science-fiction`, `short`, `sporting-event`, `superhero`, `suspense`, `thriller`, `war`, `western`
**Show Genres:** `action`, `adventure`, `animation`, `anime`, `biography`, `children`, `comedy`, `crime`, `documentary`, `drama`, `family`, `fantasy`, `game-show`, `history`, `holiday`, `home-and-garden`, `horror`, `mini-series`, `music`, `musical`, `mystery`, `news`, `none`, `reality`, `romance`, `science-fiction`, `short`, `soap`, `special-interest`, `sporting-event`, `superhero`, `suspense`, `talk-show`, `thriller`, `war`, `western` | +| `languages` | **Description:** Search for the specified languages only
**Values:** Comma separated string or list of languages
**Movie Languages:** `ab`, `af`, `ak`, `sq`, `am`, `ar`, `an`, `hy`, `as`, `av`, `ay`, `az`, `bm`, `ba`, `eu`, `be`, `bn`, `bi`, `nb`, `bs`, `bg`, `my`, `ca`, `km`, `ch`, `ce`, `ny`, `zh`, `kw`, `co`, `cr`, `hr`, `cs`, `da`, `dv`, `nl`, `dz`, `en`, `eo`, `et`, `fo`, `fj`, `fi`, `fr`, `ff`, `gd`, `gl`, `lg`, `ka`, `de`, `el`, `gn`, `gu`, `ht`, `ha`, `he`, `hi`, `hu`, `is`, `ig`, `id`, `ie`, `iu`, `ik`, `ga`, `it`, `ja`, `jv`, `kl`, `kn`, `ks`, `kk`, `rw`, `ky`, `kg`, `ko`, `ku`, `lo`, `la`, `lv`, `li`, `ln`, `lt`, `lb`, `mk`, `mg`, `ms`, `ml`, `mt`, `mi`, `mr`, `mh`, `mn`, `nv`, `ne`, `se`, `no`, `nn`, `oc`, `oj`, `or`, `om`, `os`, `pi`, `pa`, `fa`, `pl`, `pt`, `ps`, `qu`, `ro`, `rm`, `rn`, `ru`, `sm`, `sg`, `sa`, `sc`, `sr`, `sn`, `ii`, `sd`, `si`, `sk`, `sl`, `so`, `st`, `es`, `su`, `sw`, `ss`, `sv`, `tl`, `ty`, `tg`, `ta`, `tt`, `te`, `th`, `bo`, `ti`, `to`, `ts`, `tn`, `tr`, `tk`, `tw`, `ug`, `uk`, `ur`, `uz`, `vi`, `cy`, `fy`, `wo`, `xh`, `yi`, `yo`, `za`, `zu`
**Show Languages:** `ab`, `af`, `sq`, `am`, `ar`, `hy`, `eu`, `be`, `bn`, `nb`, `bs`, `bg`, `ca`, `km`, `zh`, `hr`, `cs`, `da`, `dv`, `nl`, `en`, `et`, `fi`, `fr`, `gl`, `ka`, `de`, `el`, `gu`, `he`, `hi`, `hu`, `is`, `id`, `ga`, `it`, `ja`, `kn`, `ko`, `lo`, `la`, `lv`, `lt`, `lb`, `mk`, `ms`, `ml`, `mt`, `mi`, `mr`, `ne`, `se`, `no`, `nn`, `pa`, `fa`, `pl`, `pt`, `ro`, `ru`, `sr`, `si`, `sk`, `sl`, `es`, `sv`, `tl`, `ta`, `te`, `th`, `tr`, `tw`, `uk`, `ur`, `uz`, `vi`, `cy` | +| `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`
**Movie Genres:**
**Show Genres:** | +| `ratings` | **Description:** Search for the specified rating range
**Values:** range of int i.e. `80-100`
**Movie Genres:**
**Show Genres:** | +| `networks` | **Description:** Search for the specified networks only **Only works with shows**
**Values:** Comma separated string or list of networks | +| `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/docs/metadata/filters.md b/docs/metadata/filters.md index 8bf25940..131801f1 100644 --- a/docs/metadata/filters.md +++ b/docs/metadata/filters.md @@ -89,10 +89,11 @@ Boolean Filters have no modifiers. ### Attribute -| Boolean Filters | Description | Movies | Shows | Seasons | Episodes | Artists | Albums | Track | -|:-----------------|:----------------------------------------------------------|:-------:|:-------:|:--------:|:--------:|:--------:|:--------:|:--------:| -| `has_collection` | Matches every item that has or does not have a collection | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | -| `has_overlay` | Matches every item that has or does not have an overlay | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Boolean Filters | Description | Movies | Shows | Seasons | Episodes | Artists | Albums | Track | +|:--------------------|:------------------------------------------------------------|:-------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| +| `has_collection` | Matches every item that has or does not have a collection | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | +| `has_dolby_vision` | Matches every item that has or does not have a dolby vision | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | +| `has_overlay` | Matches every item that has or does not have an overlay | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ## Date Filters diff --git a/modules/builder.py b/modules/builder.py index 79731417..15cc2ecf 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -1295,7 +1295,7 @@ class CollectionBuilder: "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): + for trakt_dict in self.config.Trakt.validate_chart(self.Type, final_method, trakt_dicts, self.library.is_movie): self.builders.append((method_name, trakt_dict)) def _tvdb(self, method_name, method_data): diff --git a/modules/trakt.py b/modules/trakt.py index 6de5d222..b0c5218d 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -18,6 +18,11 @@ sorts = [ "rank", "added", "title", "released", "runtime", "popularity", "percentage", "votes", "random", "my_rating", "watched", "collected" ] +status = ["returning", "production", "planned", "canceled", "ended"] +status_translation = { + "returning": "returning series", "production": "in production", + "planned": "planned", "canceled": "canceled", "ended": "ended" +} periods = ["daily", "weekly", "monthly", "yearly", "all"] id_translation = {"movie": "movie", "show": "show", "season": "show", "episode": "show", "person": "person", "list": "list"} id_types = { @@ -41,6 +46,62 @@ class Trakt: if not self._save(self.authorization): if not self._refresh(): self._authorization() + self._movie_genres = None + self._show_genres = None + self._movie_languages = None + self._show_languages = None + self._movie_countries = None + self._show_countries = None + self._movie_certifications = None + self._show_certifications = None + + @property + def movie_genres(self): + if not self._movie_genres: + self._movie_genres = [g["slug"] for g in self._request("/genres/movies")] + return self._movie_genres + + @property + def show_genres(self): + if not self._show_genres: + self._show_genres = [g["slug"] for g in self._request("/genres/shows")] + return self._show_genres + + @property + def movie_languages(self): + if not self._movie_languages: + self._movie_languages = [g["code"] for g in self._request("/languages/movies")] + return self._movie_languages + + @property + def show_languages(self): + if not self._show_languages: + self._show_languages = [g["code"] for g in self._request("/languages/shows")] + return self._show_languages + + @property + def movie_countries(self): + if not self._movie_countries: + self._movie_countries = [g["code"] for g in self._request("/countries/movies")] + return self._movie_countries + + @property + def show_countries(self): + if not self._show_countries: + self._show_countries = [g["code"] for g in self._request("/countries/shows")] + return self._show_countries + + @property + def movie_certifications(self): + if not self._movie_certifications: + self._movie_certifications = [g["slug"] for g in self._request("/certifications/movies")["us"]] + return self._movie_certifications + + @property + def show_certifications(self): + if not self._show_certifications: + self._show_certifications = [g["slug"] for g in self._request("/certifications/shows")["us"]] + return self._show_certifications def _authorization(self): if self.pin: @@ -114,7 +175,7 @@ class Trakt: return True return False - def _request(self, url): + def _request(self, url, params=None): headers = { "Content-Type": "application/json", "Authorization": f"Bearer {self.authorization['access_token']}", @@ -122,17 +183,20 @@ class Trakt: "trakt-api-key": self.client_id } output_json = [] + if params is None: + params = {} pages = 1 current = 1 if self.config.trace_mode: logger.debug(f"URL: {base_url}{url}") while current <= pages: if pages == 1: - response = self.config.get(f"{base_url}{url}", headers=headers) - if "X-Pagination-Page-Count" in response.headers and "?" not in url: + response = self.config.get(f"{base_url}{url}", headers=headers, params=params) + if "X-Pagination-Page-Count" in response.headers and not params: pages = int(response.headers["X-Pagination-Page-Count"]) else: - response = self.config.get(f"{base_url}{url}?page={current}", headers=headers) + params["page"] = current + response = self.config.get(f"{base_url}{url}", headers=headers, params=params) if response.status_code == 200: json_data = response.json() if self.config.trace_mode: @@ -153,9 +217,8 @@ class Trakt: def convert(self, external_id, from_source, to_source, media_type): path = f"/search/{from_source}/{external_id}" - if from_source in ["tmdb", "tvdb"]: - path = f"{path}?type={media_type}" - lookup = self._request(path) + params = {"type": media_type} if from_source in ["tmdb", "tvdb"] else None + lookup = self._request(path, params=params) if lookup and media_type in lookup[0] and to_source in lookup[0][media_type]["ids"]: return lookup[0][media_type]["ids"][to_source] raise Failed(f"Trakt Error: No {to_source.upper().replace('B', 'b')} ID found for {from_source.upper().replace('B', 'b')} ID: {external_id}") @@ -238,16 +301,16 @@ class Trakt: 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={limit}") + items = self._request(f"/recommendations/{'movies' if is_movie else 'shows'}", params={"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 _charts(self, chart_type, limit, is_movie, time_period=None): + def _charts(self, chart_type, is_movie, params, 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}") + items = self._request(f"/{'movies' if is_movie else 'shows'}/{chart_url}", params=params) return self._parse(items, typeless=chart_type == "popular", item_type="movie" if is_movie else "show") def get_people(self, data): @@ -268,7 +331,7 @@ class Trakt: 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): + def validate_chart(self, err_type, method_name, data, is_movie): valid_dicts = [] for trakt_dict in util.get_list(data, split=False): if not isinstance(trakt_dict, dict): @@ -276,12 +339,39 @@ class Trakt: 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}) + final_dict = {} + final_dict["chart"] = util.parse(err_type, "chart", trakt_dict, methods=dict_methods, parent=method_name, options=["recommended", "watched", "collected", "trending", "popular"]) + final_dict["limit"] = util.parse(err_type, "limit", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", default=10) + final_dict["time_period"] = None + if final_dict["chart"] in ["recommended", "watched", "collected"] and "time_period" in dict_methods: + final_dict["time_period"] = util.parse(err_type, "time_period", trakt_dict, methods=dict_methods, parent=method_name, default="weekly", options=periods) + if "query" in dict_methods: + final_dict["query"] = util.parse(err_type, "query", trakt_dict, methods=dict_methods, parent=method_name) + if "year" in dict_methods: + try: + if trakt_dict[dict_methods["year"]] and len(str(trakt_dict[dict_methods["year"]])) == 4: + final_dict["year"] = util.parse(err_type, "year", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=1000, maximum=3000) + else: + final_dict["year"] = util.parse(err_type, "year", trakt_dict, methods=dict_methods, parent=method_name, datatype="int", minimum=1000, maximum=3000, range_split="-") + except Failed: + raise Failed(f"{err_type} Error: trakt_chart year attribute must be either a 4 digit year or a range of two 4 digit year with a '-' i.e. 1950 or 1950-1959") + if "runtimes" in dict_methods: + 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 "genres" in dict_methods: + final_dict["genres"] = util.parse(err_type, "genres", trakt_dict, methods=dict_methods, parent=method_name, datatype="list", options=self.movie_genres if is_movie else self.show_genres) + if "languages" in dict_methods: + final_dict["languages"] = util.parse(err_type, "languages", trakt_dict, methods=dict_methods, parent=method_name, datatype="list", options=self.movie_languages if is_movie else self.show_languages) + if "countries" in dict_methods: + final_dict["countries"] = util.parse(err_type, "countries", trakt_dict, methods=dict_methods, parent=method_name, datatype="list", 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="list", 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="list") + 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="list", options=status) + valid_dicts.append(final_dict) 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") @@ -306,9 +396,15 @@ class Trakt: logger.info(f"Processing {pretty}: {data} {media_type}{'' if data == 1 else 's'}") 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"]) + 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", "year", "runtimes", "ratings", "genres", "languages", "countries", "certifications", "networks", "status"]: + 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] + params[attr] = ",".join(values) if isinstance(values, list) else values + return self._charts(data["chart"], is_movie, params, 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"]) diff --git a/modules/util.py b/modules/util.py index dc46f746..9a733b64 100644 --- a/modules/util.py +++ b/modules/util.py @@ -420,7 +420,15 @@ def schedule_check(attribute, data, current_time, run_hour): elif skip_collection: raise NotScheduled(schedule_str) -def parse(error, attribute, data, datatype=None, methods=None, parent=None, default=None, options=None, translation=None, minimum=1, maximum=None, regex=None): +def check_int(value, datatype="int", minimum=1, maximum=None): + try: + value = int(str(value)) if datatype == "int" else float(str(value)) + if (maximum is None and minimum <= value) or (maximum is not None and minimum <= value <= maximum): + return value + except ValueError: + pass + +def parse(error, attribute, data, datatype=None, methods=None, parent=None, default=None, options=None, translation=None, minimum=1, maximum=None, regex=None, range_split=None): display = f"{parent + ' ' if parent else ''}{attribute} attribute" if options is None and translation is not None: options = [o for o in translation] @@ -500,17 +508,21 @@ def parse(error, attribute, data, datatype=None, methods=None, parent=None, defa else: message = f"{display} must be either true or false" elif datatype in ["int", "float"]: - try: - value = int(str(value)) if datatype == "int" else float(str(value)) - if (maximum is None and minimum <= value) or (maximum is not None and minimum <= value <= maximum): - return value - except ValueError: - pass - pre = f"{display} {value} must be {'an integer' if datatype == 'int' else 'a number'}" - if maximum is None: - message = f"{pre} {minimum} or greater" + 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]) + if start and end and start < end: + return f"{start}{range_split}{end}" else: - message = f"{pre} between {minimum} and {maximum}" + value = check_int(value, datatype=datatype, minimum=minimum, maximum=maximum) + if value: + return value + message = f"{display} {value} must {'each ' if range_split else ''}be {'an integer' if datatype == 'int' else 'a number'}" + message = f"{message} {minimum} or greater" if maximum is None else f"{message} between {minimum} and {maximum}" + if range_split: + message = f"{message} separated by a {range_split}" elif (translation is not None and str(value).lower() not in translation) or \ (options is not None and translation is None and str(value).lower() not in options): message = f"{display} {value} must be in {', '.join([str(o) for o in options])}"