mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2025-02-24 11:27:20 +00:00
[41] add imdb_search buidler
This commit is contained in:
parent
4ef840bbab
commit
910ca94e5b
10 changed files with 586 additions and 100 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.19.1-develop40
|
||||
1.19.1-develop41
|
||||
|
|
|
@ -50,9 +50,9 @@ collections:
|
|||
|
||||
## IMDb List
|
||||
|
||||
Finds every item in an IMDb List, [Keyword Search](https://www.imdb.com/search/keyword/), [Title Search](https://www.imdb.com/search/title/), or [Topic Search](https://www.imdb.com/search/title-text/).
|
||||
Finds every item in an IMDb List or [Keyword Search](https://www.imdb.com/search/keyword/).
|
||||
|
||||
The expected input is an IMDb List URL or IMDb Search URL. Multiple values are supported as a list only a comma-separated string will not work.
|
||||
The expected input is an IMDb List URL or IMDb Keyword Search URL. Multiple values are supported as a list only a comma-separated string will not work.
|
||||
|
||||
The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order.
|
||||
|
||||
|
@ -151,4 +151,100 @@ collections:
|
|||
- ur12345678
|
||||
collection_order: custom
|
||||
sync_mode: sync
|
||||
```
|
||||
```
|
||||
|
||||
## IMDb Search
|
||||
|
||||
Finds every item using an [IMDb Advance Title Search](https://www.imdb.com/search/title/)
|
||||
|
||||
The `sync_mode: sync` and `collection_order: custom` Details are recommended since the lists are continuously updated and in a specific order.
|
||||
|
||||
| Search Parameter | Description |
|
||||
|:-------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `limit` | Specify how items you want returned by the query.<br>**Options:** Any Integer greater then `0`<br>**Default:** `100` |
|
||||
| `sort_by` | Choose from one of the many available sort options.<br>**Options:** `popularity.asc`, `popularity.desc`, `title.asc`, `title.desc`, `rating.asc`, `rating.desc`, `votes.asc`, `votes.desc`, `box_office.asc`, `box_office.desc`, `runtime.asc`, `runtime.desc`, `year.asc`, `year.desc`, `release.asc`, `release.desc`<br>**Default:** `popularity.asc` |
|
||||
| `title` | Search by title name.<br>**Options:** Any String |
|
||||
| `type` | Item must match at least one given type. Can be a comma-separated list.<br>**Options:** `movie`, `tv_series`, `short`, `tv_episode`, `tv_mini_series`, `tv_movie`, `tv_special`, `tv_short`, `video_game`, `video`, `music_video`, `podcast_series`, `podcast_episode` |
|
||||
| `type.not` | Item must not match any of the given types. Can be a comma-separated list.<br>**Options:** `movie`, `tv_series`, `short`, `tv_episode`, `tv_mini_series`, `tv_movie`, `tv_special`, `tv_short`, `video_game`, `video`, `music_video`, `podcast_series`, `podcast_episode` |
|
||||
| `release.after` | Item must have been released after the given date.<br>**Options:** `today` or Date in the format `YYYY-MM-DD` |
|
||||
| `release.before` | Item must have been released before the given date.<br>**Options:** `today` or Date in the format `YYYY-MM-DD` |
|
||||
| `rating.gte` | Item must have an IMDb Rating greater then or equal to the given number.<br>**Options:** Any Number `0.1` - `10.0`<br>**Example:** `7.5` |
|
||||
| `rating.lte` | Item must have an IMDb Rating less then or equal to the given number.<br>**Options:** Any Number `0.1` - `10.0`<br>**Example:** `7.5` |
|
||||
| `votes.gte` | Item must have a Number of Votes greater then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `votes.lte` | Item must have a Number of Votes less then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `genre` | Item must match all genres given. Can be a comma-separated list.<br>**Options:** `action`, `adventure`, `animation`, `biography`, `comedy`, `documentary`, `drama`, `crime`, `family`, `history`, `news`, `short`, `western`, `sport`, `reality-tv`, `horror`, `fantasy`, `film-noir`, `music`, `romance`, `talk-show`, `thriller`, `war`, `sci-fi`, `musical`, `mystery`, `game-show` |
|
||||
| `genre.any` | Item must match at least one given genre. Can be a comma-separated list.<br>**Options:** `action`, `adventure`, `animation`, `biography`, `comedy`, `documentary`, `drama`, `crime`, `family`, `history`, `news`, `short`, `western`, `sport`, `reality-tv`, `horror`, `fantasy`, `film-noir`, `music`, `romance`, `talk-show`, `thriller`, `war`, `sci-fi`, `musical`, `mystery`, `game-show` |
|
||||
| `genre.not` | Item must not match any og the given genres. Can be a comma-separated list.<br>**Options:** `action`, `adventure`, `animation`, `biography`, `comedy`, `documentary`, `drama`, `crime`, `family`, `history`, `news`, `short`, `western`, `sport`, `reality-tv`, `horror`, `fantasy`, `film-noir`, `music`, `romance`, `talk-show`, `thriller`, `war`, `sci-fi`, `musical`, `mystery`, `game-show` |
|
||||
| `event` | Item must have been nominated for a category at the event given. Can be a comma-separated list.<br>**Options:** `cannes`, `choice`, `spirit`, `sundance`, `bafta`, `oscar`, `emmy`, `golden`, `oscar_picture`, `oscar_director`, `national_film_board_preserved`, `razzie`, or any [IMDb Event ID](https://www.imdb.com/event/all/) (ex. `ev0050888`) |
|
||||
| `event.winning` | Item must have won a category at the event given. Can be a comma-separated list.<br>**Options:** `cannes`, `choice`, `spirit`, `sundance`, `bafta`, `oscar`, `emmy`, `golden`, `oscar_picture`, `oscar_director`, `national_film_board_preserved`, `razzie`, or any [IMDb Event ID](https://www.imdb.com/event/all/) (ex. `ev0050888`) |
|
||||
| `imdb_top` | Item must be in the top number of given Movies.<br>**Options:** Any Integer greater then `0` |
|
||||
| `imdb_bottom` | Item must be in the bottom number of given Movies.<br>**Options:** Any Integer greater then `0` |
|
||||
| `company` | Item must have been released by any company given. Can be a comma-separated list.<br>**Options:** `fox`, `dreamworks`, `mgm`, `paramount`, `sony`, `universal`, `disney`, `warner`, or any IMDb Company ID (ex. `co0023400`) |
|
||||
| `content_rating` | Item must have the given content rating. Can be a list.<br>**Options:** Dictionary with two attributes `rating` and `region`<br>`rating`: Any String to match the content rating<br>`region`: [2 Digit ISO 3166 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `country` | Item must match with every given country. Can be a comma-separated list.<br>**Options:** [2 Digit ISO 3166 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `country.any` | Item must match at least one given country. Can be a comma-separated list.<br>**Options:** [2 Digit ISO 3166 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `country.not` | Item must not match any given country. Can be a comma-separated list.<br>**Options:** [2 Digit ISO 3166 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `country.origin` | Item must match any given country as the origin country. Can be a comma-separated list.<br>**Options:** [2 Digit ISO 3166 Country Code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `keyword` | Item must match with every given keyword. Can be a comma-separated list.<br>**Options:** Any String |
|
||||
| `keyword.any` | Item must match at least one given keyword. Can be a comma-separated list.<br>**Options:** Any String |
|
||||
| `keyword.not` | Item must not match any given keyword. Can be a comma-separated list.<br>**Options:** Any String |
|
||||
| `series` | Item must match with every given series. Can be a comma-separated list.<br>**Options:** Any IMDb ID (ex. `tt0096697`) |
|
||||
| `series.any` | Item must match at least one given series. Can be a comma-separated list.<br>**Options:** Any IMDb ID (ex. `tt0096697`) |
|
||||
| `series.not` | Item must not match any given series. Can be a comma-separated list.<br>**Options:** Any IMDb ID (ex. `tt0096697`) |
|
||||
| `list` | Item must be on every given list. Can be a comma-separated list.<br>**Options:** Any IMDb List ID (ex. `ls000024621`) |
|
||||
| `list.any` | Item must be on at least one given lists. Can be a comma-separated list.<br>**Options:** Any IMDb List ID (ex. `ls000024621`) |
|
||||
| `list.not` | Item must not be on any given lists. Can be a comma-separated list.<br>**Options:** Any IMDb List ID (ex. `ls000024621`) |
|
||||
| `language` | Item must match any given language. Can be a comma-separated list.<br>**Options:** [ISO 639-2 Language Codes](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) |
|
||||
| `language.any` | Item must match at least one given language. Can be a comma-separated list.<br>**Options:** [ISO 639-2 Language Codes](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) |
|
||||
| `language.not` | Item must not match any given language. Can be a comma-separated list.<br>**Options:** [ISO 639-2 Language Codes](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) |
|
||||
| `language.primary` | Item must match any given language as the primary language. Can be a comma-separated list.<br>**Options:** [ISO 639-2 Language Codes](https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) |
|
||||
| `popularity.gte` | Item must have a Popularity greater then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `popularity.lte` | Item must have a Popularity less then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `cast` | Item must have all the given cast members. Can be a comma-separated list.<br>**Options:** Any IMDb Person ID (ex. `nm0000138`) |
|
||||
| `cast.any` | Item must have any of the given cast members. Can be a comma-separated list.<br>**Options:** Any IMDb Person ID (ex. `nm0000138`) |
|
||||
| `cast.not` | Item must not have any of the given cast members. Can be a comma-separated list.<br>**Options:** Any IMDb Person ID (ex. `nm0000138`) |
|
||||
| `runtime.gte` | Item must have a Runtime greater then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `runtime.lte` | Item must have a Runtime less then or equal to the given number.<br>**Options:** Any Integer greater then `0`<br>**Example:** `1000` |
|
||||
| `adult` | Include adult titles in the search results.<br>**Options:** `true`/`false` |
|
||||
|
||||
### Examples
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
IMDb Popular:
|
||||
imdb_search:
|
||||
type: movie
|
||||
sort_by: popularity.asc
|
||||
limit: 50
|
||||
collection_order: custom
|
||||
sync_mode: sync
|
||||
```
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
Top Action:
|
||||
imdb_search:
|
||||
type: movie
|
||||
release.after: 1990-01-01
|
||||
rating.gte: 5
|
||||
votes.gte: 100000
|
||||
genre: action
|
||||
sort_by: rating.desc
|
||||
limit: 100
|
||||
```
|
||||
|
||||
You can also find episodes using `imdb_search` like so.
|
||||
|
||||
```yaml
|
||||
collections:
|
||||
The Simpsons Top 100 Episodes:
|
||||
collection_order: custom
|
||||
builder_level: episode
|
||||
sync_mode: sync
|
||||
imdb_search:
|
||||
type: tv_episode
|
||||
series: tt0096697
|
||||
sort: rating.desc
|
||||
limit: 100
|
||||
summary: The top 100 Simpsons episodes by IMDb user rating
|
||||
```
|
||||
|
|
|
@ -435,45 +435,45 @@ The `sync_mode: sync` and `collection_order: custom` Details are recommended sin
|
|||
|
||||
### Discover Movies Parameters
|
||||
|
||||
| Movie Parameters | Description |
|
||||
|:--------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `limit` | Specify how many movies you want returned by the query.<br>**Type:** Integer<br>**Default:** 100 |
|
||||
| `region` | Specify a [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) to filter release dates. Must be uppercase. Will use the `region` specified in the [TMDb Config](../config/tmdb.md) by default.<br>**Type:** `^[A-Z]{2}$` |
|
||||
| `sort_by` | Choose from one of the many available sort options.<br>**Type:** Any [sort options](#sort-options) below<br>**Default:** `popularity.desc` |
|
||||
| `certification_country` | Used in conjunction with the certification parameter, use this to specify a country with a valid certification.<br>**Type:** String |
|
||||
| `certification` | Filter results with a valid certification from the `certification_country` parameter.<br>**Type:** String |
|
||||
| `certification.lte` | Filter and only include movies that have a certification that is less than or equal to the specified value.<br>**Type:** String |
|
||||
| `certification.gte` | Filter and only include movies that have a certification that is greater than or equal to the specified value.<br>**Type:** String |
|
||||
| `include_adult` | A filter and include or exclude adult movies.<br>**Type:** Boolean |
|
||||
| `include_video` | A filter and include or exclude videos.<br>**Type:** Boolean |
|
||||
| `primary_release_year` | A filter to limit the results to a specific primary release year.<br>**Type:** Year: YYYY |
|
||||
| `primary_release_date.gte` | Filter and only include movies that have a primary release date that is greater or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `primary_release_date.lte` | Filter and only include movies that have a primary release date that is less than or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `release_date.gte` | Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `release_date.lte` | Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `with_release_type` | Specify a comma (AND) or pipe (OR) separated value to filter release types by.<br>**Type:** String<br>**Values:** `1`: Premiere, `2`: Theatrical (limited), `3`: Theatrical, `4`: Digital, `5`: Physical, `6`: TV |
|
||||
| `year` | A filter to limit the results to a specific year (looking at all release dates).<br>**Type:** Year: `YYYY` |
|
||||
| `vote_count.gte` | Filter and only include movies that have a vote count that is greater or equal to the specified value.<br>**Type:** Integer |
|
||||
| `vote_count.lte` | Filter and only include movies that have a vote count that is less than or equal to the specified value.<br>**Type:** Integer |
|
||||
| `vote_average.gte` | Filter and only include movies that have a rating that is greater or equal to the specified value.<br>**Type:** Number |
|
||||
| `vote_average.lte` | Filter and only include movies that have a rating that is less than or equal to the specified value.<br>**Type:** Number |
|
||||
| `with_cast` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as an actor.<br>**Type:** String |
|
||||
| `with_crew` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as a crew member.<br>**Type:** String |
|
||||
| `with_people` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as either an actor or a crew member.<br>**Type:** String |
|
||||
| `with_companies` | A comma-separated list of production company ID's. Only include movies that have one of the ID's added as a production company.<br>**Type:** String |
|
||||
| `without_companies` | Filter the results to exclude the specific production companies you specify here. AND / OR filters are supported.<br>**Type:** String |
|
||||
| `with_genres` | Comma-separated value of genre ids that you want to include in the results.<br>**Type:** String |
|
||||
| `without_genres` | Comma-separated value of genre ids that you want to exclude from the results.<br>**Type:** String |
|
||||
| `with_keywords` | A comma-separated list of keyword ID's. Only includes movies that have one of the ID's added as a keyword.<br>**Type:** String |
|
||||
| `without_keywords` | Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic.<br>**Type:** String |
|
||||
| `with_runtime.gte` | Filter and only include movies that have a runtime that is greater or equal to a value.<br>**Type:** Integer |
|
||||
| `with_runtime.lte` | Filter and only include movies that have a runtime that is less than or equal to a value.<br>**Type:** Integer |
|
||||
| `with_original_language` | Specify an ISO 639-1 string to filter results by their original language value.<br>**Type:** String |
|
||||
| `with_title_translation` | Specify a language/country string to filter the results by if the item has a type of title translation.<br>**Type:** String<br>**Values:** `ar-AE`, `ar-SA`, `bg-BG`, `bn-BD`, `ca-ES`, `ch-GU`, `cs-CZ`, `da-DK`, `de-DE`, `el-GR`, `en-US`, `eo-EO`, `es-ES`, `es-MX`, `eu-ES`, `fa-IR`, `fi-FI`, `fr-CA`, `fr-FR`, `he-IL`, `hi-IN`, `hu-HU`, `id-ID`, `it-IT`, `ja-JP`, `ka-GE`, `kn-IN`, `ko-KR`, `lt-LT`, `ml-IN`, `nb-NO`, `nl-NL`, `no-NO`, `pl-PL`, `pt-BR`, `pt-PT`, `ro-RO`, `ru-RU`, `sk-SK`, `sl-SI`, `sr-RS`, `sv-SE`, `ta-IN`, `te-IN`, `th-TH`, `tr-TR`, `uk-UA`, `vi-VN`, `zh-CN`, `zh-TW` |
|
||||
| `with_overview_translation` | Specify a language/country string to filter the results by if the item has a type of overview translation.<br>**Type:** String<br>**Values:** `ar-AE`, `ar-SA`, `bg-BG`, `bn-BD`, `ca-ES`, `ch-GU`, `cs-CZ`, `da-DK`, `de-DE`, `el-GR`, `en-US`, `eo-EO`, `es-ES`, `es-MX`, `eu-ES`, `fa-IR`, `fi-FI`, `fr-CA`, `fr-FR`, `he-IL`, `hi-IN`, `hu-HU`, `id-ID`, `it-IT`, `ja-JP`, `ka-GE`, `kn-IN`, `ko-KR`, `lt-LT`, `ml-IN`, `nb-NO`, `nl-NL`, `no-NO`, `pl-PL`, `pt-BR`, `pt-PT`, `ro-RO`, `ru-RU`, `sk-SK`, `sl-SI`, `sr-RS`, `sv-SE`, `ta-IN`, `te-IN`, `th-TH`, `tr-TR`, `uk-UA`, `vi-VN`, `zh-CN`, `zh-TW` |
|
||||
| `with_watch_providers` | A comma or pipe separated list of watch provider ID's. Combine this filter with `watch_region` in order to filter your results by a specific watch provider in a specific region.<br>**Type:** String |
|
||||
| `watch_region` | An [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). Combine this filter with `with_watch_providers` in order to filter your results by a specific watch provider in a specific region.<br>**Type:** String<br>**Values:** [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `with_watch_monetization_types` | In combination with `watch_region`, you can filter by monetization type.<br>**Type:** String<br>**Values:** `flatrate`, `free`, `ads`, `rent`, `buy` |
|
||||
| Movie Parameters | Description |
|
||||
|:--------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `limit` | Specify how many movies you want returned by the query.<br>**Type:** Integer<br>**Default:** 100 |
|
||||
| `region` | Specify a [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) to filter release dates. Must be uppercase. Will use the `region` specified in the [TMDb Config](../config/tmdb.md) by default.<br>**Type:** `^[A-Z]{2}$` |
|
||||
| `sort_by` | Choose from one of the many available sort options.<br>**Type:** Any [sort options](#sort-options) below<br>**Default:** `popularity.desc` |
|
||||
| `certification_country` | Used in conjunction with the certification parameter, use this to specify a country with a valid certification.<br>**Type:** String |
|
||||
| `certification` | Filter results with a valid certification from the `certification_country` parameter.<br>**Type:** String |
|
||||
| `certification.lte` | Filter and only include movies that have a certification that is less than or equal to the specified value.<br>**Type:** String |
|
||||
| `certification.gte` | Filter and only include movies that have a certification that is greater than or equal to the specified value.<br>**Type:** String |
|
||||
| `include_adult` | A filter and include or exclude adult movies.<br>**Type:** Boolean |
|
||||
| `include_video` | A filter and include or exclude videos.<br>**Type:** Boolean |
|
||||
| `primary_release_year` | A filter to limit the results to a specific primary release year.<br>**Type:** Year: YYYY |
|
||||
| `primary_release_date.gte` | Filter and only include movies that have a primary release date that is greater or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `primary_release_date.lte` | Filter and only include movies that have a primary release date that is less than or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `release_date.gte` | Filter and only include movies that have a release date (looking at all release dates) that is greater or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `release_date.lte` | Filter and only include movies that have a release date (looking at all release dates) that is less than or equal to the specified value.<br>**Type:** Date: `MM/DD/YYYY` |
|
||||
| `with_release_type` | Specify a comma (AND) or pipe (OR) separated value to filter release types by.<br>**Type:** String<br>**Values:** `1`: Premiere, `2`: Theatrical (limited), `3`: Theatrical, `4`: Digital, `5`: Physical, `6`: TV |
|
||||
| `year` | A filter to limit the results to a specific year (looking at all release dates).<br>**Type:** Year: `YYYY` |
|
||||
| `vote_count.gte` | Filter and only include movies that have a vote count that is greater or equal to the specified value.<br>**Type:** Integer |
|
||||
| `vote_count.lte` | Filter and only include movies that have a vote count that is less than or equal to the specified value.<br>**Type:** Integer |
|
||||
| `vote_average.gte` | Filter and only include movies that have a rating that is greater or equal to the specified value.<br>**Type:** Number |
|
||||
| `vote_average.lte` | Filter and only include movies that have a rating that is less than or equal to the specified value.<br>**Type:** Number |
|
||||
| `with_cast` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as an actor.<br>**Type:** String |
|
||||
| `with_crew` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as a crew member.<br>**Type:** String |
|
||||
| `with_people` | A comma-separated list of person ID's. Only include movies that have one of the ID's added as either an actor or a crew member.<br>**Type:** String |
|
||||
| `with_companies` | A comma-separated list of production company ID's. Only include movies that have one of the ID's added as a production company.<br>**Type:** String |
|
||||
| `without_companies` | Filter the results to exclude the specific production companies you specify here. AND / OR filters are supported.<br>**Type:** String |
|
||||
| `with_genres` | Comma-separated value of genre ids that you want to include in the results.<br>**Type:** String |
|
||||
| `without_genres` | Comma-separated value of genre ids that you want to exclude from the results.<br>**Type:** String |
|
||||
| `with_keywords` | A comma-separated list of keyword ID's. Only includes movies that have one of the ID's added as a keyword.<br>**Type:** String |
|
||||
| `without_keywords` | Exclude items with certain keywords. You can comma and pipe separate these values to create an 'AND' or 'OR' logic.<br>**Type:** String |
|
||||
| `with_runtime.gte` | Filter and only include movies that have a runtime that is greater or equal to a value.<br>**Type:** Integer |
|
||||
| `with_runtime.lte` | Filter and only include movies that have a runtime that is less than or equal to a value.<br>**Type:** Integer |
|
||||
| `with_original_language` | Specify an ISO 639-1 string to filter results by their original language value.<br>**Type:** String |
|
||||
| `with_title_translation` | Specify a language/country string to filter the results by if the item has a type of title translation.<br>**Type:** String<br>**Values:** `ar-AE`, `ar-SA`, `bg-BG`, `bn-BD`, `ca-ES`, `ch-GU`, `cs-CZ`, `da-DK`, `de-DE`, `el-GR`, `en-US`, `eo-EO`, `es-ES`, `es-MX`, `eu-ES`, `fa-IR`, `fi-FI`, `fr-CA`, `fr-FR`, `he-IL`, `hi-IN`, `hu-HU`, `id-ID`, `it-IT`, `ja-JP`, `ka-GE`, `kn-IN`, `ko-KR`, `lt-LT`, `ml-IN`, `nb-NO`, `nl-NL`, `no-NO`, `pl-PL`, `pt-BR`, `pt-PT`, `ro-RO`, `ru-RU`, `sk-SK`, `sl-SI`, `sr-RS`, `sv-SE`, `ta-IN`, `te-IN`, `th-TH`, `tr-TR`, `uk-UA`, `vi-VN`, `zh-CN`, `zh-TW` |
|
||||
| `with_overview_translation` | Specify a language/country string to filter the results by if the item has a type of overview translation.<br>**Type:** String<br>**Values:** `ar-AE`, `ar-SA`, `bg-BG`, `bn-BD`, `ca-ES`, `ch-GU`, `cs-CZ`, `da-DK`, `de-DE`, `el-GR`, `en-US`, `eo-EO`, `es-ES`, `es-MX`, `eu-ES`, `fa-IR`, `fi-FI`, `fr-CA`, `fr-FR`, `he-IL`, `hi-IN`, `hu-HU`, `id-ID`, `it-IT`, `ja-JP`, `ka-GE`, `kn-IN`, `ko-KR`, `lt-LT`, `ml-IN`, `nb-NO`, `nl-NL`, `no-NO`, `pl-PL`, `pt-BR`, `pt-PT`, `ro-RO`, `ru-RU`, `sk-SK`, `sl-SI`, `sr-RS`, `sv-SE`, `ta-IN`, `te-IN`, `th-TH`, `tr-TR`, `uk-UA`, `vi-VN`, `zh-CN`, `zh-TW` |
|
||||
| `with_watch_providers` | A comma or pipe separated list of watch provider ID's. Combine this filter with `watch_region` in order to filter your results by a specific watch provider in a specific region.<br>**Type:** String |
|
||||
| `watch_region` | An [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes). Combine this filter with `with_watch_providers` in order to filter your results by a specific watch provider in a specific region.<br>**Type:** String<br>**Values:** [ISO 3166-1 code](https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes) |
|
||||
| `with_watch_monetization_types` | In combination with `watch_region`, you can filter by monetization type.<br>**Type:** String<br>**Values:** `flatrate`, `free`, `ads`, `rent`, `buy` |
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -137,7 +137,10 @@ class AniList:
|
|||
ani_attr = attr_translation[attr] if attr in attr_translation else attr
|
||||
final = ani_attr if attr in no_mod_searches else f"{ani_attr}_{mod_translation[mod]}"
|
||||
if attr in ["start", "end"]:
|
||||
value = int(util.validate_date(value, f"anilist_search {key}", return_as="%Y%m%d"))
|
||||
try:
|
||||
value = int(util.validate_date(value, return_as="%Y%m%d"))
|
||||
except Failed as e:
|
||||
raise Failed(f"Collection Error: anilist_search {key}: {e}")
|
||||
elif attr in ["format", "status", "genre", "tag", "tag_category"]:
|
||||
temp_value = [self.options[attr.replace('_', ' ').title()][v.lower().replace(' / ', '-').replace(' ', '-')] for v in value]
|
||||
if attr in ["format", "status"]:
|
||||
|
|
|
@ -1419,8 +1419,9 @@ class CollectionBuilder:
|
|||
dict_methods = {dm.lower(): dm for dm in dict_data}
|
||||
new_dictionary = {}
|
||||
for search_method, search_data in dict_data.items():
|
||||
search_attr, modifier = os.path.splitext(str(search_method).lower())
|
||||
if search_method not in anilist.searches:
|
||||
lower_method = str(search_method).lower()
|
||||
search_attr, modifier = os.path.splitext(lower_method)
|
||||
if lower_method not in anilist.searches:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {search_method} attribute not supported")
|
||||
elif search_attr == "season":
|
||||
new_dictionary[search_attr] = util.parse(self.Type, search_attr, search_data, parent=method_name, default=current_season, options=util.seasons)
|
||||
|
@ -1440,16 +1441,16 @@ class CollectionBuilder:
|
|||
elif search_attr == "source":
|
||||
new_dictionary[search_attr] = util.parse(self.Type, search_attr, search_data, options=anilist.media_source, parent=method_name)
|
||||
elif search_attr in ["episodes", "duration", "score", "popularity"]:
|
||||
new_dictionary[search_method] = util.parse(self.Type, search_method, search_data, datatype="int", parent=method_name)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="int", parent=method_name)
|
||||
elif search_attr in ["format", "status", "genre", "tag", "tag_category"]:
|
||||
new_dictionary[search_method] = self.config.AniList.validate(search_attr.replace("_", " ").title(), util.parse(self.Type, search_method, search_data))
|
||||
new_dictionary[lower_method] = self.config.AniList.validate(search_attr.replace("_", " ").title(), util.parse(self.Type, search_method, search_data))
|
||||
elif search_attr in ["start", "end"]:
|
||||
new_dictionary[search_method] = util.validate_date(search_data, f"{method_name} {search_method} attribute", return_as="%m/%d/%Y")
|
||||
new_dictionary[search_attr] = util.parse(self.Type, search_attr, search_data, datatype="date", parent=method_name, date_return="%m/%d/%Y")
|
||||
elif search_attr == "min_tag_percent":
|
||||
new_dictionary[search_attr] = util.parse(self.Type, search_attr, search_data, datatype="int", parent=method_name, minimum=0, maximum=100)
|
||||
elif search_attr == "search":
|
||||
new_dictionary[search_attr] = str(search_data)
|
||||
elif search_method not in ["sort_by", "limit"]:
|
||||
elif lower_method not in ["sort_by", "limit"]:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {search_method} attribute not supported")
|
||||
if len(new_dictionary) == 0:
|
||||
raise Failed(f"{self.Type} Error: {method_name} must have at least one valid search option")
|
||||
|
@ -1498,6 +1499,129 @@ class CollectionBuilder:
|
|||
elif method_name == "imdb_watchlist":
|
||||
for imdb_user in self.config.IMDb.validate_imdb_watchlists(self.Type, method_data, self.language):
|
||||
self.builders.append((method_name, imdb_user))
|
||||
elif method_name == "imdb_search":
|
||||
for dict_data in util.parse(self.Type, method_name, method_data, datatype="listdict"):
|
||||
dict_methods = {dm.lower(): dm for dm in dict_data}
|
||||
new_dictionary = {"limit": util.parse(self.Type, "limit", dict_data, datatype="int", methods=dict_methods, minimum=0, default=100, parent=method_name)}
|
||||
for search_method, search_data in dict_data.items():
|
||||
lower_method = str(search_method).lower()
|
||||
search_attr, modifier = os.path.splitext(lower_method)
|
||||
if search_data is None:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {search_method} attribute is blank")
|
||||
elif lower_method not in imdb.imdb_search_attributes:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {search_method} attribute not supported")
|
||||
elif search_attr == "sort_by":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, parent=method_name, options=imdb.sort_options)
|
||||
elif search_attr == "title":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, parent=method_name)
|
||||
elif search_attr == "type":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name, options=imdb.title_type_options)
|
||||
elif search_attr == "release":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="date", parent=method_name, date_return="%Y-%m-%d")
|
||||
elif search_attr == "rating":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="float", parent=method_name, minimum=0.1, maximum=10)
|
||||
elif search_attr in ["votes", "imdb_top", "imdb_bottom", "popularity", "runtime"]:
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="int", parent=method_name, minimum=0)
|
||||
elif search_attr == "genre":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name, options=imdb.genre_options)
|
||||
elif search_attr == "event":
|
||||
events = []
|
||||
for event in util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name):
|
||||
if event in imdb.event_options:
|
||||
events.append(event)
|
||||
else:
|
||||
res = re.search(r'(ev\d+)', event)
|
||||
if res:
|
||||
events.append(res.group(1))
|
||||
else:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {search_data} must match pattern ev\d+ e.g. ev0000292 or be one of {', '.join([e for e in imdb.event_options])}")
|
||||
if events:
|
||||
new_dictionary[lower_method] = events
|
||||
elif search_attr == "company":
|
||||
companies = []
|
||||
for company in util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name):
|
||||
if company in imdb.company_options:
|
||||
companies.append(company)
|
||||
else:
|
||||
res = re.search(r'(co\d+)', company)
|
||||
if res:
|
||||
companies.append(res.group(1))
|
||||
else:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {search_data} must match pattern co\d+ e.g. co0098836 or be one of {', '.join([e for e in imdb.company_options])}")
|
||||
if companies:
|
||||
new_dictionary[lower_method] = companies
|
||||
elif search_attr == "content_rating":
|
||||
final_list = []
|
||||
for content in util.get_list(search_data):
|
||||
if content:
|
||||
final_dict = {"region": "US", "rating": None}
|
||||
if not isinstance(content, dict):
|
||||
final_dict["rating"] = str(content)
|
||||
else:
|
||||
if "rating" not in content or not content["rating"]:
|
||||
raise Failed(f"{method_name} {search_method} attribute: rating attribute is required")
|
||||
final_dict["rating"] = str(content["rating"])
|
||||
if "region" not in content or not content["region"]:
|
||||
logger.warning(f"{method_name} {search_method} attribute: region attribute not found defaulting to 'US'")
|
||||
elif len(str(content["region"])) != 2:
|
||||
logger.warning(f"{method_name} {search_method} attribute: region attribute: {str(content['region'])} must be only 2 characters defaulting to 'US'")
|
||||
else:
|
||||
final_dict["region"] = str(content["region"]).upper()
|
||||
final_list.append(final_dict)
|
||||
if final_list:
|
||||
new_dictionary[lower_method] = final_list
|
||||
elif search_attr == "country":
|
||||
countries = []
|
||||
for country in util.parse(self.Type, search_method, search_data, datatype="upperlist", parent=method_name):
|
||||
if country:
|
||||
if len(str(country)) != 2:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {country} must be only 2 characters i.e. 'US'")
|
||||
countries.append(str(country))
|
||||
if countries:
|
||||
new_dictionary[lower_method] = countries
|
||||
elif search_attr == "keyword":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="strlist", parent=method_name)
|
||||
elif search_attr == "language":
|
||||
new_dictionary[lower_method] = util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name)
|
||||
elif search_attr == "cast":
|
||||
casts = []
|
||||
for cast in util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name):
|
||||
res = re.search(r'(nm\d+)', cast)
|
||||
if res:
|
||||
casts.append(res.group(1))
|
||||
else:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {search_data} must match pattern nm\d+ e.g. nm00988366")
|
||||
if casts:
|
||||
new_dictionary[lower_method] = casts
|
||||
elif search_attr == "series":
|
||||
series = []
|
||||
for show in util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name):
|
||||
res = re.search(r'(tt\d+)', show)
|
||||
if res:
|
||||
series.append(res.group(1))
|
||||
else:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {search_data} must match pattern tt\d+ e.g. tt00988366")
|
||||
if series:
|
||||
new_dictionary[lower_method] = series
|
||||
elif search_attr == "list":
|
||||
lists = []
|
||||
for new_list in util.parse(self.Type, search_method, search_data, datatype="lowerlist", parent=method_name):
|
||||
res = re.search(r'(ls\d+)', new_list)
|
||||
if res:
|
||||
lists.append(res.group(1))
|
||||
else:
|
||||
raise Failed(f"{method_name} {search_method} attribute: {search_data} must match pattern ls\d+ e.g. ls000024621")
|
||||
if lists:
|
||||
new_dictionary[lower_method] = lists
|
||||
elif search_attr == "adult":
|
||||
if util.parse(self.Type, search_method, search_data, datatype="bool", parent=method_name):
|
||||
new_dictionary[lower_method] = True
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {search_method} attribute not supported")
|
||||
if len(new_dictionary) > 1:
|
||||
self.builders.append((method_name, new_dictionary))
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} had no valid fields")
|
||||
|
||||
def _letterboxd(self, method_name, method_data):
|
||||
if method_name.startswith("letterboxd_list"):
|
||||
|
@ -1682,56 +1806,57 @@ class CollectionBuilder:
|
|||
dict_methods = {dm.lower(): dm for dm in dict_data}
|
||||
new_dictionary = {"limit": util.parse(self.Type, "limit", dict_data, datatype="int", methods=dict_methods, default=100, parent=method_name)}
|
||||
for discover_method, discover_data in dict_data.items():
|
||||
discover_attr, modifier = os.path.splitext(str(discover_method).lower())
|
||||
lower_method = str(discover_method).lower()
|
||||
discover_attr, modifier = os.path.splitext(lower_method)
|
||||
if discover_data is None:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute is blank")
|
||||
elif discover_method not in tmdb.discover_all:
|
||||
elif discover_method.lower() not in tmdb.discover_all:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute not supported")
|
||||
elif self.library.is_movie and discover_attr in tmdb.discover_tv_only:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute only works for show libraries")
|
||||
elif self.library.is_show and discover_attr in tmdb.discover_movie_only:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute only works for movie libraries")
|
||||
elif discover_attr == "region":
|
||||
new_dictionary[discover_attr] = util.parse(self.Type, discover_attr, discover_data, parent=method_name, regex=("^[A-Z]{2}$", "US"))
|
||||
new_dictionary[discover_attr] = util.parse(self.Type, discover_method, discover_data, parent=method_name, regex=("^[A-Z]{2}$", "US"))
|
||||
elif discover_attr == "sort_by":
|
||||
options = tmdb.discover_movie_sort if self.library.is_movie else tmdb.discover_tv_sort
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, parent=method_name, options=options)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, parent=method_name, options=options)
|
||||
elif discover_attr == "certification_country":
|
||||
if "certification" in dict_data or "certification.lte" in dict_data or "certification.gte" in dict_data:
|
||||
new_dictionary[discover_method] = discover_data
|
||||
new_dictionary[lower_method] = discover_data
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_attr} attribute: must be used with either certification, certification.lte, or certification.gte")
|
||||
elif discover_attr == "certification":
|
||||
if "certification_country" in dict_data:
|
||||
new_dictionary[discover_method] = discover_data
|
||||
new_dictionary[lower_method] = discover_data
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute: must be used with certification_country")
|
||||
elif discover_attr == "watch_region":
|
||||
if "with_watch_providers" in dict_data or "without_watch_providers" in dict_data or "with_watch_monetization_types" in dict_data:
|
||||
new_dictionary[discover_method] = discover_data
|
||||
new_dictionary[lower_method] = discover_data
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute: must be used with either with_watch_providers, without_watch_providers, or with_watch_monetization_types")
|
||||
elif discover_attr == "with_watch_monetization_types":
|
||||
if "watch_region" in dict_data:
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, parent=method_name, options=tmdb.discover_monetization_types)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, parent=method_name, options=tmdb.discover_monetization_types)
|
||||
else:
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute: must be used with watch_region")
|
||||
elif discover_attr in tmdb.discover_booleans:
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, datatype="bool", parent=method_name)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="bool", parent=method_name)
|
||||
elif discover_attr == "vote_average":
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_method, discover_data, datatype="float", parent=method_name)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="float", parent=method_name)
|
||||
elif discover_attr == "with_status":
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, datatype="int", parent=method_name, minimum=0, maximum=5)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="int", parent=method_name, minimum=0, maximum=5)
|
||||
elif discover_attr == "with_type":
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, datatype="int", parent=method_name, minimum=0, maximum=6)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="int", parent=method_name, minimum=0, maximum=6)
|
||||
elif discover_attr in tmdb.discover_dates:
|
||||
new_dictionary[discover_method] = util.validate_date(discover_data, f"{method_name} {discover_method} attribute", return_as="%m/%d/%Y")
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="date", parent=method_name, date_return="%m/%d/%Y")
|
||||
elif discover_attr in tmdb.discover_years:
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_attr, discover_data, datatype="int", parent=method_name, minimum=1800, maximum=self.current_year + 1)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="int", parent=method_name, minimum=1800, maximum=self.current_year + 1)
|
||||
elif discover_attr in tmdb.discover_ints:
|
||||
new_dictionary[discover_method] = util.parse(self.Type, discover_method, discover_data, datatype="int", parent=method_name)
|
||||
new_dictionary[lower_method] = util.parse(self.Type, discover_method, discover_data, datatype="int", parent=method_name)
|
||||
elif discover_attr in tmdb.discover_strings:
|
||||
new_dictionary[discover_method] = discover_data
|
||||
new_dictionary[lower_method] = discover_data
|
||||
elif discover_attr != "limit":
|
||||
raise Failed(f"{self.Type} Error: {method_name} {discover_method} attribute not supported")
|
||||
if len(new_dictionary) > 1:
|
||||
|
@ -2449,10 +2574,10 @@ class CollectionBuilder:
|
|||
logger.error(error)
|
||||
return valid_list
|
||||
elif attribute in date_attributes and modifier in [".before", ".after"]:
|
||||
if data == "today":
|
||||
return datetime.strftime(datetime.now(), "%Y-%m-%d")
|
||||
else:
|
||||
return util.validate_date(data, final, return_as="%Y-%m-%d")
|
||||
try:
|
||||
return util.validate_date(datetime.now() if data == "today" else data, return_as="%Y-%m-%d")
|
||||
except Failed as e:
|
||||
raise Failed(f"{self.Type} Error: {final}: {e}")
|
||||
elif attribute in date_attributes and modifier in ["", ".not"]:
|
||||
search_mod = "d"
|
||||
if plex_search and data and str(data)[-1] in ["s", "m", "h", "d", "w", "o", "y"]:
|
||||
|
|
282
modules/imdb.py
282
modules/imdb.py
|
@ -5,7 +5,7 @@ from urllib.parse import urlparse, parse_qs
|
|||
|
||||
logger = util.logger
|
||||
|
||||
builders = ["imdb_list", "imdb_id", "imdb_chart", "imdb_watchlist"]
|
||||
builders = ["imdb_list", "imdb_id", "imdb_chart", "imdb_watchlist", "imdb_search"]
|
||||
movie_charts = ["box_office", "popular_movies", "top_movies", "top_english", "top_indian", "lowest_rated"]
|
||||
show_charts = ["popular_shows", "top_shows"]
|
||||
charts = {
|
||||
|
@ -18,11 +18,62 @@ charts = {
|
|||
"top_indian": "Top Rated Indian Movies",
|
||||
"lowest_rated": "Lowest Rated Movies"
|
||||
}
|
||||
imdb_search_attributes = [
|
||||
"sort_by", "title", "type", "type.not", "release.after", "release.before", "rating.gte", "rating.lte",
|
||||
"votes.gte", "votes.lte", "genre", "genre.any", "genre.not", "event", "event.winning", "series", "series.any", "series.not",
|
||||
"imdb_top", "imdb_bottom", "company", "content_rating", "country", "country.any", "country.not", "country.origin",
|
||||
"keyword", "keyword.any", "keyword.not", "language", "language.any", "language.not", "language.primary",
|
||||
"popularity.gte", "popularity.lte", "cast", "cast.any", "cast.not", "runtime.gte", "runtime.lte", "adult",
|
||||
]
|
||||
sort_by_options = {
|
||||
"popularity": "POPULARITY",
|
||||
"title": "TITLE_REGIONAL",
|
||||
"rating": "USER_RATING",
|
||||
"votes": "USER_RATING_COUNT",
|
||||
"box_office": "BOX_OFFICE_GROSS_DOMESTIC",
|
||||
"runtime": "RUNTIME",
|
||||
"year": "YEAR",
|
||||
"release": "RELEASE_DATE",
|
||||
}
|
||||
sort_options = [f"{a}.{d}"for a in sort_by_options for d in ["asc", "desc"]]
|
||||
title_type_options = {
|
||||
"movie": "movie", "tv_series": "tvSeries", "short": "short", "tv_episode": "tvEpisode", "tv_mini_series": "tvMiniSeries",
|
||||
"tv_movie": "tvMovie", "tv_special": "tvSpecial", "tv_short": "tvShort", "video_game": "videoGame", "video": "video",
|
||||
"music_video": "musicVideo", "podcast_series": "podcastSeries", "podcast_episode": "podcastEpisode"
|
||||
}
|
||||
genre_options = {a.lower(): a for a in [
|
||||
"Action", "Adventure", "Animation", "Biography", "Comedy", "Documentary", "Drama", "Crime", "Family", "History",
|
||||
"News", "Short", "Western", "Sport", "Reality-TV", "Horror", "Fantasy", "Film-Noir", "Music", "Romance",
|
||||
"Talk-Show", "Thriller", "War", "Sci-Fi", "Musical", "Mystery", "Game-Show"
|
||||
]}
|
||||
company_options = {
|
||||
"fox": ["co0000756", "co0176225", "co0201557", "co0017497"],
|
||||
"dreamworks": ["co0067641", "co0040938", "co0252576", "co0003158"],
|
||||
"mgm": ["co0007143", "co0026841"],
|
||||
"paramount": ["co0023400"],
|
||||
"sony": ["co0050868", "co0026545", "co0121181"],
|
||||
"universal": ["co0005073", "co0055277", "co0042399"],
|
||||
"disney": ["co0008970", "co0017902", "co0098836", "co0059516", "co0092035", "co0049348"],
|
||||
"warner": ["co0002663", "co0005035", "co0863266", "co0072876", "co0080422", "co0046718"],
|
||||
}
|
||||
event_options = {
|
||||
"cannes": {"eventId": "ev0000147"},
|
||||
"choice": {"eventId": "ev0000133"},
|
||||
"spirit": {"eventId": "ev0000349"},
|
||||
"sundance": {"eventId": "ev0000631"},
|
||||
"bafta": {"eventId": "ev0000123"},
|
||||
"oscar": {"eventId": "ev0000003"},
|
||||
"emmy": {"eventId": "ev0000223"},
|
||||
"golden": {"eventId": "ev0000292"},
|
||||
"oscar_picture": {"eventId": "ev0000003", "searchAwardCategoryId": "bestPicture"},
|
||||
"oscar_director": {"eventId": "ev0000003", "searchAwardCategoryId": "bestDirector"},
|
||||
"national_film_board_preserved": {"eventId": "ev0000468"},
|
||||
"razzie": {"eventId": "ev0000558"},
|
||||
}
|
||||
base_url = "https://www.imdb.com"
|
||||
graphql_url = "https://api.graphql.imdb.com/"
|
||||
urls = {
|
||||
"lists": f"{base_url}/list/ls",
|
||||
"searches": f"{base_url}/search/title/",
|
||||
"title_text_searches": f"{base_url}/search/title-text/",
|
||||
"keyword_searches": f"{base_url}/search/keyword/",
|
||||
"filmography_searches": f"{base_url}/filmosearch/"
|
||||
}
|
||||
|
@ -42,6 +93,9 @@ class IMDb:
|
|||
response = self.config.get_html(url, headers=headers, params=params)
|
||||
return response.xpath(xpath) if xpath else response
|
||||
|
||||
def _graph_request(self, json_data):
|
||||
return self.config.post_json(graphql_url, headers={"content-type": "application/json"}, json=json_data)
|
||||
|
||||
def validate_imdb_lists(self, err_type, imdb_lists, language):
|
||||
valid_lists = []
|
||||
for imdb_dict in util.get_list(imdb_lists, split=False):
|
||||
|
@ -102,12 +156,6 @@ class IMDb:
|
|||
if imdb_url.startswith(urls["lists"]):
|
||||
xpath_total = "//div[@class='desc lister-total-num-results']/text()"
|
||||
per_page = 100
|
||||
elif imdb_url.startswith(urls["searches"]):
|
||||
xpath_total = "//div[@class='desc']/span/text()"
|
||||
per_page = 250
|
||||
elif imdb_url.startswith(urls["title_text_searches"]):
|
||||
xpath_total = "//div[@class='desc']/span/text()"
|
||||
per_page = 50
|
||||
else:
|
||||
xpath_total = "//div[@class='desc']/text()"
|
||||
per_page = 50
|
||||
|
@ -135,7 +183,6 @@ class IMDb:
|
|||
params.pop("page", None) # noqa
|
||||
logger.trace(f"URL: {imdb_base}")
|
||||
logger.trace(f"Params: {params}")
|
||||
search_url = imdb_base.startswith(urls["searches"])
|
||||
if limit < 1 or total < limit:
|
||||
limit = total
|
||||
remainder = limit % item_count
|
||||
|
@ -145,15 +192,9 @@ class IMDb:
|
|||
for i in range(1, num_of_pages + 1):
|
||||
start_num = (i - 1) * item_count + 1
|
||||
logger.ghost(f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}")
|
||||
if search_url:
|
||||
params["count"] = remainder if i == num_of_pages else item_count # noqa
|
||||
params["start"] = start_num # noqa
|
||||
elif imdb_base.startswith(urls["title_text_searches"]):
|
||||
params["start"] = start_num # noqa
|
||||
else:
|
||||
params["page"] = i # noqa
|
||||
params["page"] = i # noqa
|
||||
ids_found = self._request(imdb_base, language=language, xpath="//div[contains(@class, 'lister-item-image')]//a/img//@data-tconst", params=params)
|
||||
if not search_url and i == num_of_pages:
|
||||
if i == num_of_pages:
|
||||
ids_found = ids_found[:remainder]
|
||||
imdb_ids.extend(ids_found)
|
||||
time.sleep(2)
|
||||
|
@ -162,6 +203,206 @@ class IMDb:
|
|||
return imdb_ids
|
||||
raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
|
||||
|
||||
def _search_json(self, data):
|
||||
out = {
|
||||
"locale": "en-US",
|
||||
"first": data["limit"] if "limit" in data and data["limit"] < 250 else 250,
|
||||
"titleTypeConstraint": {"anyTitleTypeIds": [title_type_options[t] for t in data["type"]] if "type" in data else []},
|
||||
}
|
||||
sort = data["sort_by"] if "sort_by" in data else "popularity.asc"
|
||||
sort_by, sort_order = sort.split(".")
|
||||
out["sortBy"] = sort_by_options[sort_by]
|
||||
out["sortOrder"] = sort_order.upper()
|
||||
|
||||
if "type.not" in data:
|
||||
out["titleTypeConstraint"]["excludeTitleTypeIds"] = [title_type_options[t] for t in data["type.not"]]
|
||||
|
||||
if "release.after" in data or "release.before" in data:
|
||||
num_range = {}
|
||||
if "release.after" in data:
|
||||
num_range["start"] = data["release.after"]
|
||||
if "release.before" in data:
|
||||
num_range["end"] = data["release.before"]
|
||||
out["releaseDateConstraint"] = {"releaseDateRange": num_range}
|
||||
|
||||
if "title" in data:
|
||||
out["titleTextConstraint"] = {"searchTerm": data["title"]}
|
||||
|
||||
if any([a in data for a in ["rating.gte", "rating.lte", "votes.gte", "votes.lte"]]):
|
||||
out["userRatingsConstraint"] = {}
|
||||
num_range = {}
|
||||
if "rating.gte" in data:
|
||||
num_range["min"] = data["rating.gte"]
|
||||
if "rating.lte" in data:
|
||||
num_range["max"] = data["rating.lte"]
|
||||
out["userRatingsConstraint"]["aggregateRatingRange"] = num_range
|
||||
num_range = {}
|
||||
if "votes.gte" in data:
|
||||
num_range["min"] = data["votes.gte"]
|
||||
if "votes.lte" in data:
|
||||
num_range["max"] = data["votes.lte"]
|
||||
out["userRatingsConstraint"]["ratingsCountRange"] = num_range
|
||||
|
||||
if any([a in data for a in ["genre", "genre.any", "genre.not"]]):
|
||||
out["genreConstraint"] = {}
|
||||
if "genre" in data:
|
||||
out["genreConstraint"]["allGenreIds"] = [genre_options[g] for g in data["genre"]]
|
||||
if "genre.any" in data:
|
||||
out["genreConstraint"]["anyGenreIds"] = [genre_options[g] for g in data["genre.any"]]
|
||||
if "genre.not" in data:
|
||||
out["genreConstraint"]["excludeGenreIds"] = [genre_options[g] for g in data["genre.not"]]
|
||||
|
||||
if "event" in data or "event.winning" in data:
|
||||
input_list = []
|
||||
if "event" in data:
|
||||
input_list.extend([event_options[a] if a in event_options else {"eventId": a} for a in data["event"]])
|
||||
if "event.winning" in data:
|
||||
for a in data["event.winning"]:
|
||||
award_dict = event_options[a] if a in event_options else {"eventId": a}
|
||||
award_dict["winnerFilter"] = "WINNER_ONLY"
|
||||
input_list.append(award_dict)
|
||||
out["awardConstraint"] = {"allEventNominations": input_list}
|
||||
|
||||
if any([a in data for a in ["imdb_top", "imdb_bottom", "popularity.gte", "popularity.lte"]]):
|
||||
ranges = []
|
||||
if "imdb_top" in data:
|
||||
ranges.append({"rankRange": {"max": data["imdb_top"]}, "rankedTitleListType": "TOP_RATED_MOVIES"})
|
||||
if "imdb_bottom" in data:
|
||||
ranges.append({"rankRange": {"max": data["imdb_bottom"]}, "rankedTitleListType": "LOWEST_RATED_MOVIES"})
|
||||
if "popularity.gte" in data or "popularity.lte" in data:
|
||||
num_range = {}
|
||||
if "popularity.lte" in data:
|
||||
num_range["max"] = data["popularity.lte"]
|
||||
if "popularity.gte" in data:
|
||||
num_range["min"] = data["popularity.gte"]
|
||||
ranges.append({"rankRange": num_range, "rankedTitleListType": "TITLE_METER"})
|
||||
out["rankedTitleListConstraint"] = {"allRankedTitleLists": ranges}
|
||||
|
||||
if any([a in data for a in ["series", "series.any", "series.not"]]):
|
||||
out["episodicConstraint"] = {}
|
||||
if "series" in data:
|
||||
out["episodicConstraint"]["allSeriesIds"] = data["series"]
|
||||
if "series.any" in data:
|
||||
out["episodicConstraint"]["anySeriesIds"] = data["series.any"]
|
||||
if "series.not" in data:
|
||||
out["episodicConstraint"]["excludeSeriesIds"] = data["series.not"]
|
||||
|
||||
if any([a in data for a in ["list", "list.any", "list.not"]]):
|
||||
out["listConstraint"] = {}
|
||||
if "list" in data:
|
||||
out["listConstraint"]["inAllLists"] = data["list"]
|
||||
if "list.any" in data:
|
||||
out["listConstraint"]["inAnyList"] = data["list.any"]
|
||||
if "list.not" in data:
|
||||
out["listConstraint"]["notInAnyList"] = data["list.not"]
|
||||
|
||||
if "company" in data:
|
||||
company_ids = []
|
||||
for c in data["company"]:
|
||||
if c in company_options:
|
||||
company_ids.extend(company_options[c])
|
||||
else:
|
||||
company_ids.append(c)
|
||||
out["creditedCompanyConstraint"] = {"anyCompanyIds": company_ids}
|
||||
|
||||
if "content_rating" in data:
|
||||
out["certificateConstraint"] = {"anyRegionCertificateRatings": data["content_rating"]}
|
||||
|
||||
if any([a in data for a in ["country", "country.any", "country.not", "country.origin"]]):
|
||||
out["originCountryConstraint"] = {}
|
||||
if "country" in data:
|
||||
out["originCountryConstraint"]["allCountries"] = data["country"]
|
||||
if "country.any" in data:
|
||||
out["originCountryConstraint"]["anyCountries"] = data["country.any"]
|
||||
if "country.not" in data:
|
||||
out["originCountryConstraint"]["excludeCountries"] = data["country.not"]
|
||||
if "country.origin" in data:
|
||||
out["originCountryConstraint"]["anyPrimaryCountries"] = data["country.origin"]
|
||||
|
||||
if any([a in data for a in ["keyword", "keyword.any", "keyword.not"]]):
|
||||
out["keywordConstraint"] = {}
|
||||
if "keyword" in data:
|
||||
out["keywordConstraint"]["allKeywords"] = data["keyword"]
|
||||
if "keyword.any" in data:
|
||||
out["keywordConstraint"]["anyKeywords"] = data["keyword.any"]
|
||||
if "keyword.not" in data:
|
||||
out["keywordConstraint"]["excludeKeywords"] = data["keyword.not"]
|
||||
|
||||
if any([a in data for a in ["language", "language.any", "language.not", "language.primary"]]):
|
||||
out["languageConstraint"] = {}
|
||||
if "language" in data:
|
||||
out["languageConstraint"]["allLanguages"] = data["language"]
|
||||
if "language.any" in data:
|
||||
out["languageConstraint"]["anyLanguages"] = data["language.any"]
|
||||
if "language.not" in data:
|
||||
out["languageConstraint"]["excludeLanguages"] = data["language.not"]
|
||||
if "language.primary" in data:
|
||||
out["languageConstraint"]["anyPrimaryLanguages"] = data["language.primary"]
|
||||
|
||||
if any([a in data for a in ["cast", "cast.any", "cast.not"]]):
|
||||
out["creditedNameConstraint"] = {}
|
||||
if "cast" in data:
|
||||
out["creditedNameConstraint"]["allNameIds"] = data["cast"]
|
||||
if "cast.any" in data:
|
||||
out["creditedNameConstraint"]["anyNameIds"] = data["cast.any"]
|
||||
if "cast.not" in data:
|
||||
out["creditedNameConstraint"]["excludeNameIds"] = data["cast.not"]
|
||||
|
||||
if "runtime.gte" in data or "runtime.lte" in data:
|
||||
num_range = {}
|
||||
if "runtime.gte" in data:
|
||||
num_range["min"] = data["runtime.gte"]
|
||||
if "runtime.lte" in data:
|
||||
num_range["max"] = data["runtime.lte"]
|
||||
out["runtimeConstraint"] = {"runtimeRangeMinutes": num_range}
|
||||
|
||||
if "adult" in data and data["adult"]:
|
||||
out["explicitContentConstraint"] = {"explicitContentFilter": "INCLUDE_ADULT"}
|
||||
|
||||
logger.trace(out)
|
||||
return {
|
||||
"operationName": "AdvancedTitleSearch",
|
||||
"variables": out,
|
||||
"extensions": {
|
||||
"persistedQuery": {
|
||||
"version": 1,
|
||||
"sha256Hash": "7327d144ec84b57c93f761affe0d0609b0d495f85e8e47fdc76291679850cfda"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def _search(self, data):
|
||||
json_obj = self._search_json(data)
|
||||
item_count = 250
|
||||
imdb_ids = []
|
||||
logger.ghost("Parsing Page 1")
|
||||
response_json = self._graph_request(json_obj)
|
||||
total = response_json["data"]["advancedTitleSearch"]["total"]
|
||||
limit = data["limit"] if "limit" in data else 0
|
||||
if limit < 1 or total < limit:
|
||||
limit = total
|
||||
remainder = limit % item_count
|
||||
if remainder == 0:
|
||||
remainder = item_count
|
||||
num_of_pages = math.ceil(int(limit) / item_count)
|
||||
end_cursor = response_json["data"]["advancedTitleSearch"]["pageInfo"]["endCursor"]
|
||||
imdb_ids.extend([n["node"]["title"]["id"] for n in response_json["data"]["advancedTitleSearch"]["edges"]])
|
||||
if num_of_pages > 1:
|
||||
for i in range(2, num_of_pages + 1):
|
||||
start_num = (i - 1) * item_count + 1
|
||||
logger.ghost(f"Parsing Page {i}/{num_of_pages} {start_num}-{limit if i == num_of_pages else i * item_count}")
|
||||
json_obj["variables"]["after"] = end_cursor
|
||||
response_json = self._graph_request(json_obj)
|
||||
end_cursor = response_json["data"]["advancedTitleSearch"]["pageInfo"]["endCursor"]
|
||||
ids_found = [n["node"]["title"]["id"] for n in response_json["data"]["advancedTitleSearch"]["edges"]]
|
||||
if i == num_of_pages:
|
||||
ids_found = ids_found[:remainder]
|
||||
imdb_ids.extend(ids_found)
|
||||
logger.exorcise()
|
||||
if len(imdb_ids) > 0:
|
||||
return imdb_ids
|
||||
raise Failed("IMDb Error: No IMDb IDs Found")
|
||||
|
||||
def keywords(self, imdb_id, language, ignore_cache=False):
|
||||
imdb_keywords = {}
|
||||
expired = None
|
||||
|
@ -238,6 +479,11 @@ class IMDb:
|
|||
elif method == "imdb_watchlist":
|
||||
logger.info(f"Processing IMDb Watchlist: {data}")
|
||||
return [(_i, "imdb") for _i in self._watchlist(data, language)]
|
||||
elif method == "imdb_search":
|
||||
logger.info(f"Processing IMDb Search:")
|
||||
for k, v in data.items():
|
||||
logger.info(f" {k}: {v}")
|
||||
return [(_i, "imdb") for _i in self._search(data)]
|
||||
else:
|
||||
raise Failed(f"IMDb Error: Method {method} not supported")
|
||||
|
||||
|
|
|
@ -1631,7 +1631,10 @@ class MetadataFile(DataFile):
|
|||
current = str(getattr(current_item, key, ""))
|
||||
final_value = None
|
||||
if var_type == "date":
|
||||
final_value = util.validate_date(value, name, return_as="%Y-%m-%d")
|
||||
try:
|
||||
final_value = util.validate_date(value, return_as="%Y-%m-%d")
|
||||
except Failed as ei:
|
||||
raise Failed(f"{self.type_str} Error: {name} {ei}")
|
||||
current = current[:-9]
|
||||
elif var_type == "float":
|
||||
try:
|
||||
|
|
|
@ -857,7 +857,7 @@ class Operations:
|
|||
|
||||
if self.library.radarr_remove_by_tag:
|
||||
logger.info("")
|
||||
logger.separator(f"Radarr Remove {len(self.library.sonarr_remove_by_tag)} Movies with Tags: {', '.join(self.library.sonarr_remove_by_tag)}", space=False, border=False)
|
||||
logger.separator(f"Radarr Remove {len(self.library.radarr_remove_by_tag)} Movies with Tags: {', '.join(self.library.radarr_remove_by_tag)}", space=False, border=False)
|
||||
logger.info("")
|
||||
self.library.Radarr.remove_all_with_tags(self.library.radarr_remove_by_tag)
|
||||
if self.library.sonarr_remove_by_tag:
|
||||
|
|
|
@ -135,7 +135,7 @@ class TMDbMovie(TMDBObj):
|
|||
raise Failed(f"TMDb Error: No Movie found for TMDb ID {self.tmdb_id}")
|
||||
except TMDbException as e:
|
||||
logger.stacktrace()
|
||||
raise Failed(f"TMDb Error: Unexpected Error with TMDb ID {self.tmdb_id}: {e}")
|
||||
raise TMDbException(f"TMDb Error: Unexpected Error with TMDb ID {self.tmdb_id}: {e}")
|
||||
|
||||
|
||||
class TMDbShow(TMDBObj):
|
||||
|
@ -172,7 +172,7 @@ class TMDbShow(TMDBObj):
|
|||
raise Failed(f"TMDb Error: No Show found for TMDb ID {self.tmdb_id}")
|
||||
except TMDbException as e:
|
||||
logger.stacktrace()
|
||||
raise Failed(f"TMDb Error: Unexpected Error with TMDb ID {self.tmdb_id}: {e}")
|
||||
raise TMDbException(f"TMDb Error: Unexpected Error with TMDb ID {self.tmdb_id}: {e}")
|
||||
|
||||
class TMDb:
|
||||
def __init__(self, config, params):
|
||||
|
@ -344,7 +344,10 @@ class TMDb:
|
|||
limit = int(attrs.pop("limit"))
|
||||
for date_attr in date_methods:
|
||||
if date_attr in attrs:
|
||||
attrs[date_attr] = util.validate_date(attrs[date_attr], f"tmdb_discover attribute {date_attr}", return_as="%Y-%m-%d")
|
||||
try:
|
||||
attrs[date_attr] = util.validate_date(attrs[date_attr], return_as="%Y-%m-%d")
|
||||
except Failed as e:
|
||||
raise Failed(f"Collection Error: tmdb_discover attribute {date_attr}: {e}")
|
||||
if is_movie and region and "region" not in attrs:
|
||||
attrs["region"] = region
|
||||
logger.trace(f"Params: {attrs}")
|
||||
|
|
|
@ -262,14 +262,14 @@ def get_int_list(data, id_type):
|
|||
except Failed as e: logger.error(e)
|
||||
return int_values
|
||||
|
||||
def validate_date(date_text, method, return_as=None):
|
||||
def validate_date(date_text, return_as=None):
|
||||
if isinstance(date_text, datetime):
|
||||
date_obg = date_text
|
||||
else:
|
||||
try:
|
||||
date_obg = datetime.strptime(str(date_text), "%Y-%m-%d" if "-" in str(date_text) else "%m/%d/%Y")
|
||||
except ValueError:
|
||||
raise Failed(f"Collection Error: {method}: {date_text} must match pattern YYYY-MM-DD (e.g. 2020-12-25) or MM/DD/YYYY (e.g. 12/25/2020)")
|
||||
raise Failed(f"{date_text} must match pattern YYYY-MM-DD (e.g. 2020-12-25) or MM/DD/YYYY (e.g. 12/25/2020)")
|
||||
return datetime.strftime(date_obg, return_as) if return_as else date_obg
|
||||
|
||||
def validate_regex(data, col_type, validate=True):
|
||||
|
@ -513,7 +513,10 @@ def is_date_filter(value, modifier, data, final, current_time):
|
|||
or (modifier == ".not" and value and value >= threshold_date):
|
||||
return True
|
||||
elif modifier in [".before", ".after"]:
|
||||
filter_date = validate_date(data, final)
|
||||
try:
|
||||
filter_date = validate_date(data)
|
||||
except Failed as e:
|
||||
raise Failed(f"Collection Error: {final}: {e}")
|
||||
if (modifier == ".before" and value >= filter_date) or (modifier == ".after" and value <= filter_date):
|
||||
return True
|
||||
elif modifier == ".regex":
|
||||
|
@ -741,21 +744,23 @@ def parse_and_or(error, attribute, data, test_list):
|
|||
out += ")"
|
||||
return out, final
|
||||
|
||||
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):
|
||||
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, date_return=None):
|
||||
display = f"{parent + ' ' if parent else ''}{attribute} attribute"
|
||||
if options is None and translation is not None:
|
||||
options = [o for o in translation]
|
||||
value = data[methods[attribute]] if methods and attribute in methods else data
|
||||
|
||||
if datatype in ["list", "commalist", "strlist", "lowerlist"]:
|
||||
if datatype in ["list", "commalist", "strlist", "lowerlist", "upperlist"]:
|
||||
final_list = []
|
||||
if value:
|
||||
if datatype in ["commalist", "strlist"] and isinstance(value, dict):
|
||||
if isinstance(value, dict):
|
||||
raise Failed(f"{error} Error: {display} {value} must be a list or string")
|
||||
if datatype == "commalist":
|
||||
value = get_list(value)
|
||||
if datatype == "lowerlist":
|
||||
value = get_list(value, lower=True)
|
||||
if datatype == "upperlist":
|
||||
value = get_list(value, upper=True)
|
||||
if not isinstance(value, list):
|
||||
value = [value]
|
||||
for v in value:
|
||||
|
@ -840,11 +845,16 @@ def parse(error, attribute, data, datatype=None, methods=None, parent=None, defa
|
|||
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 datatype == "date":
|
||||
try:
|
||||
return validate_date(datetime.now() if data == "today" else data, return_as=date_return)
|
||||
except Failed as e:
|
||||
message = f"{e}"
|
||||
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])}"
|
||||
else:
|
||||
return translation[value] if translation is not None else value
|
||||
return translation[str(value).lower()] if translation is not None else value
|
||||
|
||||
if default is None:
|
||||
raise Failed(f"{error} Error: {message}")
|
||||
|
|
Loading…
Add table
Reference in a new issue