mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-21 20:13:05 +00:00
[57] TVDb Filters
This commit is contained in:
parent
437f86f942
commit
265c2b8294
7 changed files with 116 additions and 52 deletions
|
@ -2,7 +2,7 @@
|
|||
Updated gitpython requirement to 3.1.42
|
||||
Updated plexapi requirement to 4.5.10
|
||||
Updated setuptools requirement to 69.1.1
|
||||
Updated tmdbapis requirement to 1.2.7
|
||||
Updated tmdbapis requirement to 1.2.9
|
||||
|
||||
# Removed Features
|
||||
Due to FlixPatrol moving a lot of their data behind a paywall and them reworking their pages to remove IMDb IDs and TMDb IDs the flixpatrol builders and default files have been removed. There currently are no plans to re-add them.
|
||||
|
@ -12,6 +12,7 @@ Added new [BoxOfficeMojo Builder](https://metamanager.wiki/en/latest/files/build
|
|||
Added `monitor_existing` to sonarr and radarr. To update the monitored status of items existing in plex to match the `monitor` declared.
|
||||
Added [Gotify](https://gotify.net/) as a notification service. Thanks @krstn420 for the initial code.
|
||||
Added [Trakt and MyAnimeList Authentication Page](https://metamanager.wiki/en/latest/config/auth/) allowing users to authenticate against those services directly from the wiki. credit to @chazlarson for developing the script
|
||||
Added TVDb filters
|
||||
|
||||
# Updates
|
||||
Reworked PMM Default Streaming [Collections](https://metamanager.wiki/en/latest/defaults/both/streaming) and [Overlays](https://metamanager.wiki/en/latest/defaults/overlays/streaming) to utilize TMDB Watch Provider data, allowing users to customize regions without relying on mdblist. This data will be more accurate and up-to-date now.
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
1.20.0-develop56
|
||||
1.20.0-develop57
|
||||
|
|
|
@ -57,9 +57,11 @@ String filters can take multiple values **only as a list**.
|
|||
### Attribute
|
||||
|
||||
| String Filter | Description | Movies | Shows | Seasons | Episodes | Artists | Albums | Track |
|
||||
|:---------------------------------------------------|:-----------------------------------------|:------------------------------------------:|:--------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------:|:-------------------------------------------:|:--------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------:|:------------------------------------------:|
|
||||
|:----------------------------------------------------|:-----------------------------------------|:------------------------------------------:|:--------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------:|:-------------------------------------------:|:--------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------:|:------------------------------------------:|
|
||||
| `title` | Uses the title attribute to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } |
|
||||
| `tmdb_title`<sup>**[2](#table-annotations)**</sup> | Uses the title from TMDb to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `tvdb_title`<sup>**[2](#table-annotations)**</sup> | Uses the title from TVDb to match | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `tvdb_status`<sup>**[2](#table-annotations)**</sup> | Uses the status from TVDb to match | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `summary` | Uses the summary attribute to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } |
|
||||
| `studio` | Uses the studio attribute to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `edition` | Uses the edition attribute to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
|
@ -111,6 +113,7 @@ Tag filters can take multiple values as a **list or a comma-separated string**.
|
|||
| `tmdb_genre`<sup>**[2](#table-annotations)**</sup> | Uses the genres from TMDb to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `tmdb_keyword`<sup>**[2](#table-annotations)**</sup> | Uses the keywords from TMDb to match | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `origin_country`<sup>**[2](#table-annotations)**</sup> | Uses TMDb origin country [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) to match<br>Example: `origin_country: us` | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `tvdb_genre`<sup>**[2](#table-annotations)**</sup> | Uses the genres from TVDb to match | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
| `imdb_keyword`<sup>**[2](#table-annotations)**</sup> | Uses the keywords from IMDb to match See [Special](#special-filters) for more attributes | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-check:{ .green } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } | :fontawesome-solid-circle-xmark:{ .red } |
|
||||
|
||||
## Boolean Filters
|
||||
|
|
|
@ -89,7 +89,7 @@ filters_by_type = {
|
|||
"show_season": ["episodes"],
|
||||
"artist_album": ["tracks"],
|
||||
"movie": ["edition", "has_edition", "stinger_rating", "has_stinger"],
|
||||
"show": ["seasons", "tmdb_status", "tmdb_type", "origin_country", "network", "first_episode_aired", "last_episode_aired", "last_episode_aired_or_never"],
|
||||
"show": ["seasons", "tmdb_status", "tmdb_type", "origin_country", "network", "first_episode_aired", "last_episode_aired", "last_episode_aired_or_never", "tvdb_title", "tvdb_status", "tvdb_genre"],
|
||||
"artist": ["albums"],
|
||||
"album": ["record_label"]
|
||||
}
|
||||
|
@ -106,15 +106,16 @@ tmdb_filters = [
|
|||
"original_language", "origin_country", "tmdb_vote_count", "tmdb_vote_average", "tmdb_year", "tmdb_keyword", "tmdb_genre",
|
||||
"first_episode_aired", "last_episode_aired", "last_episode_aired_or_never", "tmdb_status", "tmdb_type", "tmdb_title"
|
||||
]
|
||||
tvdb_filters = ["tvdb_title", "tvdb_status", "tvdb_genre"]
|
||||
imdb_filters = ["imdb_keyword"]
|
||||
string_filters = [
|
||||
"title", "summary", "studio", "edition", "record_label", "folder", "filepath", "audio_track_title", "subtitle_track_title", "tmdb_title",
|
||||
"audio_codec", "audio_profile", "video_codec", "video_profile"
|
||||
"audio_codec", "audio_profile", "video_codec", "video_profile", "tvdb_title", "tvdb_status"
|
||||
]
|
||||
string_modifiers = ["", ".not", ".is", ".isnot", ".begins", ".ends", ".regex"]
|
||||
tag_filters = [
|
||||
"actor", "collection", "content_rating", "country", "director", "network", "genre", "label", "producer", "year",
|
||||
"origin_country", "writer", "resolution", "audio_language", "subtitle_language", "tmdb_keyword", "tmdb_genre", "imdb_keyword"
|
||||
"origin_country", "writer", "resolution", "audio_language", "subtitle_language", "tmdb_keyword", "tmdb_genre", "imdb_keyword", "tvdb_genre"
|
||||
]
|
||||
tag_modifiers = ["", ".not", ".regex", ".count_gt", ".count_gte", ".count_lt", ".count_lte"]
|
||||
boolean_filters = ["has_collection", "has_edition", "has_overlay", "has_dolby_vision", "has_stinger"]
|
||||
|
@ -2886,6 +2887,13 @@ class CollectionBuilder:
|
|||
return False
|
||||
return True
|
||||
|
||||
def check_tvdb_filters(self, tvdb_item, filters_in):
|
||||
for filter_method, filter_data in filters_in:
|
||||
filter_attr, modifier, filter_final = self.library.split(filter_method)
|
||||
if self.config.TVDb.item_filter(tvdb_item, filter_attr, modifier, filter_final, filter_data) is False:
|
||||
return False
|
||||
return True
|
||||
|
||||
def check_imdb_filters(self, imdb_info, filters_in):
|
||||
for filter_method, filter_data in filters_in:
|
||||
filter_attr, modifier, filter_final = self.library.split(filter_method)
|
||||
|
@ -2943,14 +2951,18 @@ class CollectionBuilder:
|
|||
item = self.library.reload(item)
|
||||
final_return = False
|
||||
tmdb_item = None
|
||||
tvdb_item = None
|
||||
imdb_info = None
|
||||
for filter_list in self.filters:
|
||||
tmdb_f = []
|
||||
tvdb_f = []
|
||||
imdb_f = []
|
||||
plex_f = []
|
||||
for k, v in filter_list:
|
||||
if k.split(".")[0] in tmdb_filters:
|
||||
tmdb_f.append((k, v))
|
||||
elif k.split(".")[0] in tvdb_filters:
|
||||
tvdb_f.append((k, v))
|
||||
elif k.split(".")[0] in imdb_filters:
|
||||
imdb_f.append((k, v))
|
||||
else:
|
||||
|
@ -2972,6 +2984,19 @@ class CollectionBuilder:
|
|||
or_result = False
|
||||
if not tmdb_item or self.check_tmdb_filters(tmdb_item, tmdb_f, item.ratingKey in self.library.movie_rating_key_map) is False:
|
||||
or_result = False
|
||||
if tvdb_f:
|
||||
if not tvdb_item and isinstance(item, Show):
|
||||
if item.ratingKey not in self.library.show_rating_key_map:
|
||||
logger.warning(f"Filter Error: No TVDb ID found for {item.title}")
|
||||
or_result = False
|
||||
else:
|
||||
try:
|
||||
tvdb_item = self.config.TVDb.get_tvdb_obj(self.library.show_rating_key_map[item.ratingKey])
|
||||
except Failed as e:
|
||||
logger.error(e)
|
||||
or_result = False
|
||||
if not tvdb_item or self.check_tvdb_filters(tvdb_item, tvdb_f) is False:
|
||||
or_result = False
|
||||
if imdb_f:
|
||||
if not imdb_info and isinstance(item, (Movie, Show)):
|
||||
if item.ratingKey not in self.library.imdb_rating_key_map:
|
||||
|
|
|
@ -17,26 +17,13 @@ class Cache:
|
|||
logger.info(f"Initializing cache database at {self.cache_path}")
|
||||
else:
|
||||
logger.info(f"Using cache database at {self.cache_path}")
|
||||
cursor.execute("DROP TABLE IF EXISTS guids")
|
||||
cursor.execute("DROP TABLE IF EXISTS guid_map")
|
||||
cursor.execute("DROP TABLE IF EXISTS imdb_to_tvdb_map")
|
||||
cursor.execute("DROP TABLE IF EXISTS tmdb_to_tvdb_map")
|
||||
cursor.execute("DROP TABLE IF EXISTS imdb_map")
|
||||
cursor.execute("DROP TABLE IF EXISTS mdb_data")
|
||||
cursor.execute("DROP TABLE IF EXISTS mdb_data2")
|
||||
cursor.execute("DROP TABLE IF EXISTS mdb_data3")
|
||||
cursor.execute("DROP TABLE IF EXISTS mdb_data4")
|
||||
cursor.execute("DROP TABLE IF EXISTS omdb_data")
|
||||
cursor.execute("DROP TABLE IF EXISTS omdb_data2")
|
||||
cursor.execute("DROP TABLE IF EXISTS tvdb_data")
|
||||
cursor.execute("DROP TABLE IF EXISTS tvdb_data2")
|
||||
cursor.execute("DROP TABLE IF EXISTS tmdb_show_data")
|
||||
cursor.execute("DROP TABLE IF EXISTS tmdb_show_data2")
|
||||
cursor.execute("DROP TABLE IF EXISTS overlay_ratings")
|
||||
cursor.execute("DROP TABLE IF EXISTS anidb_data")
|
||||
cursor.execute("DROP TABLE IF EXISTS anidb_data2")
|
||||
cursor.execute("DROP TABLE IF EXISTS anidb_data3")
|
||||
cursor.execute("DROP TABLE IF EXISTS mal_data")
|
||||
for old_table in [
|
||||
"guids", "guid_map", "imdb_to_tvdb_map", "tmdb_to_tvdb_map", "imdb_map",
|
||||
"mdb_data", "mdb_data2", "mdb_data3", "mdb_data4", "omdb_data", "omdb_data2",
|
||||
"tvdb_data", "tvdb_data2", "tvdb_data3", "tmdb_show_data", "tmdb_show_data2",
|
||||
"overlay_ratings", "anidb_data", "anidb_data2", "anidb_data3", "mal_data"
|
||||
]:
|
||||
cursor.execute(f"DROP TABLE IF EXISTS {old_table}")
|
||||
cursor.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS guids_map (
|
||||
key INTEGER PRIMARY KEY,
|
||||
|
@ -231,11 +218,12 @@ class Cache:
|
|||
expiration_date TEXT)"""
|
||||
)
|
||||
cursor.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS tvdb_data3 (
|
||||
"""CREATE TABLE IF NOT EXISTS tvdb_data4 (
|
||||
key INTEGER PRIMARY KEY,
|
||||
tvdb_id INTEGER UNIQUE,
|
||||
type TEXT,
|
||||
title TEXT,
|
||||
status TEXT,
|
||||
summary TEXT,
|
||||
poster_url TEXT,
|
||||
background_url TEXT,
|
||||
|
@ -783,12 +771,13 @@ class Cache:
|
|||
with sqlite3.connect(self.cache_path) as connection:
|
||||
connection.row_factory = sqlite3.Row
|
||||
with closing(connection.cursor()) as cursor:
|
||||
cursor.execute("SELECT * FROM tvdb_data3 WHERE tvdb_id = ? and type = ?", (tvdb_id, "movie" if is_movie else "show"))
|
||||
cursor.execute("SELECT * FROM tvdb_data4 WHERE tvdb_id = ? and type = ?", (tvdb_id, "movie" if is_movie else "show"))
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
tvdb_dict["tvdb_id"] = int(row["tvdb_id"]) if row["tvdb_id"] else 0
|
||||
tvdb_dict["type"] = row["type"] if row["type"] else ""
|
||||
tvdb_dict["title"] = row["title"] if row["title"] else ""
|
||||
tvdb_dict["status"] = row["status"] if row["status"] else ""
|
||||
tvdb_dict["summary"] = row["summary"] if row["summary"] else ""
|
||||
tvdb_dict["poster_url"] = row["poster_url"] if row["poster_url"] else ""
|
||||
tvdb_dict["background_url"] = row["background_url"] if row["background_url"] else ""
|
||||
|
@ -804,12 +793,12 @@ class Cache:
|
|||
with sqlite3.connect(self.cache_path) as connection:
|
||||
connection.row_factory = sqlite3.Row
|
||||
with closing(connection.cursor()) as cursor:
|
||||
cursor.execute("INSERT OR IGNORE INTO tvdb_data3(tvdb_id, type) VALUES(?, ?)", (obj.tvdb_id, "movie" if obj.is_movie else "show"))
|
||||
update_sql = "UPDATE tvdb_data3 SET title = ?, summary = ?, poster_url = ?, background_url = ?, " \
|
||||
cursor.execute("INSERT OR IGNORE INTO tvdb_data4(tvdb_id, type) VALUES(?, ?)", (obj.tvdb_id, "movie" if obj.is_movie else "show"))
|
||||
update_sql = "UPDATE tvdb_data4 SET title = ?, status = ?, summary = ?, poster_url = ?, background_url = ?, " \
|
||||
"release_date = ?, genres = ?, expiration_date = ? WHERE tvdb_id = ? AND type = ?"
|
||||
tvdb_date = f"{str(obj.release_date.year).zfill(4)}-{str(obj.release_date.month).zfill(2)}-{str(obj.release_date.day).zfill(2)}" if obj.release_date else None
|
||||
cursor.execute(update_sql, (
|
||||
obj.title, obj.summary, obj.poster_url, obj.background_url, tvdb_date, "|".join(obj.genres),
|
||||
obj.title, obj.status, obj.summary, obj.poster_url, obj.background_url, tvdb_date, "|".join(obj.genres),
|
||||
expiration_date.strftime("%Y-%m-%d"), obj.tvdb_id, "movie" if obj.is_movie else "show"
|
||||
))
|
||||
|
||||
|
|
|
@ -63,6 +63,17 @@ mass_content_options = {
|
|||
"mdb_age_rating": "Use MDbList Age Rating", "mdb_age_rating0": "Use MDbList Age Rating with Zero Padding",
|
||||
"mal": "Use MyAnimeList Rating"
|
||||
}
|
||||
mass_collection_content_options = {
|
||||
"lock": "Lock Rating", "unlock": "Unlock Rating", "remove": "Remove and Lock Rating", "reset": "Remove and Unlock Rating",
|
||||
"highest": "Highest Rating in the collection", "lowest": "Lowest Rating in the collection",
|
||||
"average": "Highest Rating in the collection"
|
||||
}
|
||||
content_rating_default = {
|
||||
1: [
|
||||
|
||||
]
|
||||
|
||||
}
|
||||
mass_studio_options = {
|
||||
"lock": "Lock Rating", "unlock": "Unlock Rating", "remove": "Remove and Lock Rating", "reset": "Remove and Unlock Rating",
|
||||
"tmdb": "Use TMDb Studio", "anidb": "Use AniDB Animation Work", "mal": "Use MyAnimeList Studio"
|
||||
|
@ -113,7 +124,8 @@ reset_overlay_options = {"tmdb": "Reset to TMDb poster", "plex": "Reset to Plex
|
|||
library_operations = {
|
||||
"assets_for_all": "bool", "split_duplicates": "bool", "update_blank_track_titles": "bool", "remove_title_parentheses": "bool",
|
||||
"radarr_add_all_existing": "bool", "radarr_remove_by_tag": "str", "sonarr_add_all_existing": "bool", "sonarr_remove_by_tag": "str",
|
||||
"mass_genre_update": mass_genre_options, "mass_content_rating_update": mass_content_options, "mass_studio_update": mass_studio_options,
|
||||
"mass_content_rating_update": mass_content_options, "mass_collection_content_rating_update": "dict",
|
||||
"mass_genre_update": mass_genre_options, "mass_studio_update": mass_studio_options,
|
||||
"mass_audience_rating_update": mass_rating_options, "mass_episode_audience_rating_update": mass_episode_rating_options,
|
||||
"mass_critic_rating_update": mass_rating_options, "mass_episode_critic_rating_update": mass_episode_rating_options,
|
||||
"mass_user_rating_update": mass_rating_options, "mass_episode_user_rating_update": mass_episode_rating_options,
|
||||
|
@ -904,18 +916,18 @@ class ConfigFile:
|
|||
section_final[op] = util.check_collection_mode(config_op[op])
|
||||
elif data_type == "dict":
|
||||
input_dict = config_op[op]
|
||||
if op in ["mass_poster_update", "mass_background_update"] and input_dict and not isinstance(input_dict, dict):
|
||||
if op in ["mass_poster_update", "mass_background_update", "mass_collection_content_rating_update"] and input_dict and not isinstance(input_dict, dict):
|
||||
input_dict = {"source": input_dict}
|
||||
|
||||
if not input_dict or not isinstance(input_dict, dict):
|
||||
raise Failed(f"Config Error: {op} must be a dictionary")
|
||||
if op in ["mass_poster_update", "mass_background_update"]:
|
||||
elif op in ["mass_poster_update", "mass_background_update"]:
|
||||
section_final[op] = {
|
||||
"source": check_for_attribute(input_dict, "source", test_list=mass_image_options, default_is_none=True, save=False),
|
||||
"seasons": check_for_attribute(input_dict, "seasons", var_type="bool", default=True, save=False),
|
||||
"episodes": check_for_attribute(input_dict, "episodes", var_type="bool", default=True, save=False),
|
||||
}
|
||||
if op == "metadata_backup":
|
||||
elif op == "metadata_backup":
|
||||
default_path = os.path.join(default_dir, f"{str(library_name)}_Metadata_Backup.yml")
|
||||
if "path" not in input_dict:
|
||||
logger.warning(f"Config Warning: path attribute not found using default: {default_path}")
|
||||
|
@ -929,7 +941,7 @@ class ConfigFile:
|
|||
"sync_tags": check_for_attribute(input_dict, "sync_tags", var_type="bool", default=False, save=False),
|
||||
"add_blank_entries": check_for_attribute(input_dict, "add_blank_entries", var_type="bool", default=True, save=False)
|
||||
}
|
||||
if "mapper" in op:
|
||||
elif "mapper" in op:
|
||||
section_final[op] = {}
|
||||
for old_value, new_value in input_dict.items():
|
||||
if not old_value:
|
||||
|
@ -938,12 +950,17 @@ class ConfigFile:
|
|||
logger.warning(f"Config Warning: {op} value '{new_value}' ignored as it cannot be mapped to itself")
|
||||
else:
|
||||
section_final[op][str(old_value)] = str(new_value) if new_value else None # noqa
|
||||
if op == "delete_collections":
|
||||
elif op == "delete_collections":
|
||||
section_final[op] = {
|
||||
"managed": check_for_attribute(input_dict, "managed", var_type="bool", default_is_none=True, save=False),
|
||||
"configured": check_for_attribute(input_dict, "configured", var_type="bool", default_is_none=True, save=False),
|
||||
"less": check_for_attribute(input_dict, "less", var_type="int", default_is_none=True, save=False, int_min=1),
|
||||
}
|
||||
elif op == "mass_collection_content_rating_update":
|
||||
section_final[op] = {
|
||||
"source": check_for_attribute(input_dict, "source", test_list=mass_collection_content_options, default_is_none=True, save=False),
|
||||
"ranking": check_for_attribute(input_dict, "ranking", var_type="list", default=content_rating_default, save=False),
|
||||
}
|
||||
else:
|
||||
section_final[op] = check_for_attribute(config_op, op, var_type=data_type, default=False, save=False)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import requests, time
|
||||
import re, requests, time
|
||||
from datetime import datetime
|
||||
from lxml import html
|
||||
from lxml.etree import ParserError
|
||||
|
@ -74,6 +74,7 @@ class TVDbObj:
|
|||
self.poster_url = data["poster_url"]
|
||||
self.background_url = data["background_url"]
|
||||
self.release_date = data["release_date"]
|
||||
self.status = data["status"]
|
||||
self.genres = data["genres"].split("|")
|
||||
else:
|
||||
self.title, self.summary = parse_title_summary(lang=self._tvdb.language)
|
||||
|
@ -95,6 +96,7 @@ class TVDbObj:
|
|||
self.release_date = datetime.strptime(released, "%B %d, %Y") if released else released # noqa
|
||||
except ValueError:
|
||||
self.release_date = None
|
||||
self.status = parse_page("//strong[text()='Status']/parent::li/span/text()[normalize-space()]")
|
||||
|
||||
self.genres = parse_page("//strong[text()='Genres']/parent::li/span/a/text()[normalize-space()]", is_list=True)
|
||||
|
||||
|
@ -249,3 +251,30 @@ class TVDb:
|
|||
return self._ids_from_url(data)
|
||||
else:
|
||||
raise Failed(f"TVDb Error: Method {method} not supported")
|
||||
|
||||
def item_filter(self, item, filter_attr, modifier, filter_final, filter_data):
|
||||
if filter_attr == "tvdb_title":
|
||||
if util.is_string_filter([item.title], modifier, filter_data):
|
||||
return False
|
||||
elif filter_attr == "tvdb_status":
|
||||
if util.is_string_filter([item.status], modifier, filter_data):
|
||||
return False
|
||||
elif filter_attr == "tvdb_genre":
|
||||
attrs = item.genres
|
||||
if modifier == ".regex":
|
||||
has_match = False
|
||||
for reg in filter_data:
|
||||
for name in attrs:
|
||||
if re.compile(reg).search(name):
|
||||
has_match = True
|
||||
if has_match is False:
|
||||
return False
|
||||
elif modifier in [".count_gt", ".count_gte", ".count_lt", ".count_lte"]:
|
||||
test_number = len(attrs) if attrs else 0
|
||||
modifier = f".{modifier[7:]}"
|
||||
if test_number is None or util.is_number_filter(test_number, modifier, filter_data):
|
||||
return False
|
||||
elif (not list(set(filter_data) & set(attrs)) and modifier == "") \
|
||||
or (list(set(filter_data) & set(attrs)) and modifier == ".not"):
|
||||
return False
|
||||
return True
|
||||
|
|
Loading…
Reference in a new issue