diff --git a/VERSION b/VERSION
index 1741532c..10599f2a 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.16.1-develop24
+1.16.1-develop25
diff --git a/docs/config/operations.md b/docs/config/operations.md
index 5b663c5c..86f58f7a 100644
--- a/docs/config/operations.md
+++ b/docs/config/operations.md
@@ -25,6 +25,7 @@ The available attributes for the operations attribute are as follows
| `mass_content_rating_update` | Updates every item's content rating in the library to the chosen site's genres
**Values:**
`mdb` | Use MdbList for Content Ratings |
`mdb_commonsense` | Use Commonsense Rating through MDbList for Content Ratings |
`omdb` | Use IMDb through OMDb for Content Ratings |
|
| `mass_originally_available_update` | Updates every item's originally available date in the library to the chosen site's date
**Values:** `tmdb` | Use TMDb Release Date |
`tvdb` | Use TVDb Release Date |
`omdb` | Use IMDb Release Date through OMDb |
`mdb` | Use MdbList Release Date |
`anidb` | Use AniDB Release Date |
|
| `mass_audience_rating_update`/
`mass_critic_rating_update` | Updates every item's audience/critic rating in the library to the chosen site's rating
**Values:** `tmdb` | Use TMDb Rating |
`omdb` | Use IMDbRating through OMDb |
`mdb` | Use MdbList Score |
`mdb_imdb` | Use IMDb Rating through MDbList |
`mdb_metacritic` | Use Metacritic Rating through MDbList |
`mdb_metacriticuser` | Use Metacritic User Rating through MDbList |
`mdb_trakt` | Use Trakt Rating through MDbList |
`mdb_tomatoes` | Use Rotten Tomatoes Rating through MDbList |
`mdb_tomatoesaudience` | Use Rotten Tomatoes Audience Rating through MDbList |
`mdb_tmdb` | Use TMDb Rating through MDbList |
`mdb_letterboxd` | Use Letterboxd Rating through MDbList |
`anidb_rating` | Use AniDB Rating |
`anidb_average` | Use AniDB Average |
|
+| `mass_imdb_parental_labels` | Updates every item's labels in the library to match the IMDb Parental Guide
**Values** `with_none` or `without_none` |
| `mass_trakt_rating_update` | Updates every movie/show's user rating in the library to match your custom rating on Trakt if there is one
**Values:** `true` or `false` |
| `mass_collection_mode` | Updates every Collection in your library to the specified Collection Mode
**Values:** `default`: Library default
`hide`: Hide Collection
`hide_items`: Hide Items in this Collection
`show_items`: Show this Collection and its Items`default` | Library default |
`hide` | Hide Collection |
`hide_items` | Hide Items in this Collection |
`show_items` | Show this Collection and its Items |
|
| `update_blank_track_titles ` | Search though every track in a music library and replace any blank track titles with the tracks sort title
**Values:** `true` or `false` |
diff --git a/modules/cache.py b/modules/cache.py
index d78b07d0..b7e8772a 100644
--- a/modules/cache.py
+++ b/modules/cache.py
@@ -203,6 +203,17 @@ class Cache:
media_id TEXT,
media_type TEXT)"""
)
+ cursor.execute(
+ """CREATE TABLE IF NOT EXISTS imdb_parental (
+ key INTEGER PRIMARY KEY,
+ imdb_id TEXT,
+ nudity TEXT,
+ violence TEXT,
+ profanity TEXT,
+ alcohol TEXT,
+ frightening TEXT,
+ expiration_date TEXT)"""
+ )
cursor.execute("SELECT count(name) FROM sqlite_master WHERE type='table' AND name='image_map'")
if cursor.fetchone()[0] > 0:
cursor.execute(f"SELECT DISTINCT library FROM image_map")
@@ -695,3 +706,33 @@ class Cache:
connection.row_factory = sqlite3.Row
with closing(connection.cursor()) as cursor:
cursor.execute(f"DELETE FROM list_ids WHERE list_key = ?", (list_key,))
+
+ def query_imdb_parental(self, imdb_id, expiration):
+ imdb_dict = {}
+ expired = None
+ with sqlite3.connect(self.cache_path) as connection:
+ connection.row_factory = sqlite3.Row
+ with closing(connection.cursor()) as cursor:
+ cursor.execute("SELECT * FROM imdb_parental WHERE imdb_id = ?", (imdb_id,))
+ row = cursor.fetchone()
+ if row:
+ imdb_dict["nudity"] = row["nudity"] if row["nudity"] else "None"
+ imdb_dict["violence"] = row["violence"] if row["violence"] else "None"
+ imdb_dict["profanity"] = row["profanity"] if row["profanity"] else "None"
+ imdb_dict["alcohol"] = row["alcohol"] if row["alcohol"] else "None"
+ imdb_dict["frightening"] = row["frightening"] if row["frightening"] else "None"
+ datetime_object = datetime.strptime(row["expiration_date"], "%Y-%m-%d")
+ time_between_insertion = datetime.now() - datetime_object
+ expired = time_between_insertion.days > expiration
+ return imdb_dict, expired
+
+ def update_imdb_parental(self, expired, imdb_id, parental, expiration):
+ expiration_date = datetime.now() if expired is True else (datetime.now() - timedelta(days=random.randint(1, expiration)))
+ 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 imdb_parental(imdb_id) VALUES(?)", (imdb_id,))
+ update_sql = "UPDATE imdb_parental SET nudity = ?, violence = ?, profanity = ?, alcohol = ?, " \
+ "frightening = ?, expiration_date = ? WHERE imdb_id = ?"
+ cursor.execute(update_sql, (parental["nudity"], parental["violence"], parental["profanity"], parental["alcohol"],
+ parental["frightening"], expiration_date.strftime("%Y-%m-%d"), imdb_id))
\ No newline at end of file
diff --git a/modules/config.py b/modules/config.py
index a19e1366..b6fe822d 100644
--- a/modules/config.py
+++ b/modules/config.py
@@ -34,6 +34,7 @@ sync_modes = {"append": "Only Add Items to the Collection or Playlist", "sync":
mass_genre_options = {"tmdb": "Use TMDb Metadata", "omdb": "Use IMDb Metadata through OMDb", "tvdb": "Use TVDb Metadata", "anidb": "Use AniDB Tag Metadata"}
mass_content_options = {"omdb": "Use IMDb Metadata through OMDb", "mdb": "Use MdbList Metadata", "mdb_commonsense": "Use Commonsense Rating through MDbList"}
mass_available_options = {"tmdb": "Use TMDb Metadata", "omdb": "Use IMDb Metadata through OMDb", "mdb": "Use MdbList Metadata", "tvdb": "Use TVDb Metadata", "anidb": "Use AniDB Metadata"}
+imdb_label_options = {"with_none": "Add IMDb Parental Labels including None", "without_none": "Add IMDb Parental Labels including None"}
mass_rating_options = {
"tmdb": "Use TMDb Rating",
"omdb": "Use IMDb Rating through OMDb",
@@ -604,7 +605,8 @@ class ConfigFile:
"genre_collections": None,
"update_blank_track_titles": None,
"mass_content_rating_update": None,
- "mass_originally_available_update": None
+ "mass_originally_available_update": None,
+ "mass_imdb_parental_labels": None
}
display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"]
@@ -675,6 +677,8 @@ class ConfigFile:
params["mass_content_rating_update"] = check_for_attribute(lib["operations"], "mass_content_rating_update", test_list=mass_content_options, default_is_none=True, save=False)
if "mass_originally_available_update" in lib["operations"]:
params["mass_originally_available_update"] = check_for_attribute(lib["operations"], "mass_originally_available_update", test_list=mass_available_options, default_is_none=True, save=False)
+ if "mass_imdb_parental_labels" in lib["operations"]:
+ params["mass_imdb_parental_labels"] = check_for_attribute(lib["operations"], "mass_imdb_parental_labels", test_list=imdb_label_options, default_is_none=True, save=False)
if "mass_trakt_rating_update" in lib["operations"]:
params["mass_trakt_rating_update"] = check_for_attribute(lib["operations"], "mass_trakt_rating_update", var_type="bool", default=False, save=False)
if "split_duplicates" in lib["operations"]:
diff --git a/modules/imdb.py b/modules/imdb.py
index 39e8dbd3..2fb73750 100644
--- a/modules/imdb.py
+++ b/modules/imdb.py
@@ -128,6 +128,24 @@ class IMDb:
return imdb_ids
raise Failed(f"IMDb Error: No IMDb IDs Found at {imdb_url}")
+ def parental_guide(self, imdb_id, ignore_cache=False):
+ parental_dict = {}
+ expired = None
+ if self.config.Cache and not ignore_cache:
+ parental_dict, expired = self.config.Cache.query_imdb_parental(imdb_id, self.config.expiration)
+ if parental_dict and expired is False:
+ return parental_dict
+ response = self.config.get_html(f"https://www.imdb.com/title/{imdb_id}/parentalguide")
+ for ptype in ["nudity", "violence", "profanity", "alcohol", "frightening"]:
+ results = response.xpath(f"//section[@id='advisory-{ptype}']//span[contains(@class,'ipl-status-pill')]/text()")
+ if results:
+ parental_dict[ptype] = results[0].strip()
+ else:
+ raise Failed(f"IMDb Error: No Item Found for IMDb ID: {imdb_id}")
+ if self.config.Cache and not ignore_cache:
+ self.config.Cache.update_imdb_parental(expired, imdb_id, parental_dict, self.config.expiration)
+ return parental_dict
+
def _ids_from_chart(self, chart):
if chart == "box_office":
url = "chart/boxoffice"
diff --git a/modules/library.py b/modules/library.py
index d0c41f42..a4be913d 100644
--- a/modules/library.py
+++ b/modules/library.py
@@ -73,6 +73,7 @@ class Library(ABC):
self.mass_critic_rating_update = params["mass_critic_rating_update"]
self.mass_content_rating_update = params["mass_content_rating_update"]
self.mass_originally_available_update = params["mass_originally_available_update"]
+ self.mass_imdb_parental_labels = params["mass_imdb_parental_labels"]
self.mass_trakt_rating_update = params["mass_trakt_rating_update"]
self.radarr_add_all_existing = params["radarr_add_all_existing"]
self.radarr_remove_by_tag = params["radarr_remove_by_tag"]
@@ -95,7 +96,7 @@ class Library(ABC):
self.status = {}
self.items_library_operation = True if self.assets_for_all or self.mass_genre_update or self.mass_audience_rating_update \
- or self.mass_critic_rating_update or self.mass_content_rating_update or self.mass_originally_available_update or self.mass_trakt_rating_update \
+ or self.mass_critic_rating_update or self.mass_content_rating_update or self.mass_originally_available_update or self.mass_imdb_parental_labels or self.mass_trakt_rating_update \
or self.genre_mapper or self.content_rating_mapper or self.tmdb_collections or self.radarr_add_all_existing or self.sonarr_add_all_existing else False
self.library_operation = True if self.items_library_operation or self.delete_unmanaged_collections or self.delete_collections_with_less \
or self.radarr_remove_by_tag or self.sonarr_remove_by_tag or self.mass_collection_mode \
diff --git a/plex_meta_manager.py b/plex_meta_manager.py
index 491e0d2b..dc7603dc 100644
--- a/plex_meta_manager.py
+++ b/plex_meta_manager.py
@@ -417,6 +417,7 @@ def library_operations(config, library):
logger.debug(f"Mass Critic Rating Update: {library.mass_critic_rating_update}")
logger.debug(f"Mass Content Rating Update: {library.mass_content_rating_update}")
logger.debug(f"Mass Originally Available Update: {library.mass_originally_available_update}")
+ logger.debug(f"Mass IMDb Parental Labels: {library.mass_imdb_parental_labels}")
logger.debug(f"Mass Trakt Rating Update: {library.mass_trakt_rating_update}")
logger.debug(f"Mass Collection Mode Update: {library.mass_collection_mode}")
logger.debug(f"Split Duplicates: {library.split_duplicates}")
@@ -486,6 +487,13 @@ def library_operations(config, library):
except Failed:
pass
+ if library.mass_imdb_parental_labels:
+ try:
+ parental_guide = config.IMDb.parental_guide(imdb_id)
+ labels = [f"{k.capitalize()}:{v}" for k, v in parental_guide.items() if library.mass_imdb_parental_labels == "with_none" or v != "None"]
+ library.edit_tags("label", item, append_tags=labels)
+ except Failed:
+ pass
path = os.path.dirname(str(item.locations[0])) if library.is_movie else str(item.locations[0])
if library.Radarr and library.radarr_add_all_existing and tmdb_id:
path = path.replace(library.Radarr.plex_path, library.Radarr.radarr_path)