[30] add mass episode ratings

This commit is contained in:
meisnate12 2022-06-09 10:20:43 -04:00
parent 64d2d8874f
commit 43d08bad15
8 changed files with 227 additions and 64 deletions

View file

@ -1 +1 @@
1.17.0-develop29 1.17.0-develop30

View file

@ -16,28 +16,29 @@ libraries:
The available attributes for the operations attribute are as follows The available attributes for the operations attribute are as follows
| Attribute | Description | | Attribute | Description |
|:--------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:--------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `assets_for_all` | Search in assets for images for every item in your library.<br>**Values:** `true` or `false` | | `assets_for_all` | Search in assets for images for every item in your library.<br>**Values:** `true` or `false` |
| `delete_collections_with_less` | Deletes every collection with less than the given number of items.<br>**Values:** number greater than 0 | | `delete_collections_with_less` | Deletes every collection with less than the given number of items.<br>**Values:** number greater than 0 |
| `delete_unmanaged_collections` | Deletes every unmanaged collection<br>**Values:** `true` or `false` | | `delete_unmanaged_collections` | Deletes every unmanaged collection<br>**Values:** `true` or `false` |
| `mass_genre_update` | Updates every item's genres in the library to the chosen site's genres<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb for Genres</td></tr><tr><td>`tvdb`</td><td>Use TVDb for Genres</td></tr><tr><td>`omdb`</td><td>Use IMDb through OMDb for Genres</td></tr><tr><td>`anidb`</td><td>Use AniDB Tags for Genres</td></tr></table> | | `mass_genre_update` | Updates every item's genres in the library to the chosen site's genres<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb for Genres</td></tr><tr><td>`tvdb`</td><td>Use TVDb for Genres</td></tr><tr><td>`imdb`</td><td>Use IMDb for Genres</td></tr><tr><td>`omdb`</td><td>Use IMDb through OMDb for Genres</td></tr><tr><td>`anidb`</td><td>Use AniDB Tags for Genres</td></tr></table> |
| `mass_content_rating_update` | Updates every item's content rating in the library to the chosen site's content rating<br>**Values:** <table class="clearTable"><tr><td>`mdb`</td><td>Use MdbList for Content Ratings</td></tr><tr><td>`mdb_commonsense`</td><td>Use Commonsense Rating through MDbList for Content Ratings</td></tr><tr><td>`omdb`</td><td>Use IMDb through OMDb for Content Ratings</td></tr></table> | | `mass_content_rating_update` | Updates every item's content rating in the library to the chosen site's content rating<br>**Values:** <table class="clearTable"><tr><td>`mdb`</td><td>Use MdbList for Content Ratings</td></tr><tr><td>`mdb_commonsense`</td><td>Use Commonsense Rating through MDbList for Content Ratings</td></tr><tr><td>`omdb`</td><td>Use IMDb through OMDb for Content Ratings</td></tr></table> |
| `mass_originally_available_update` | Updates every item's originally available date in the library to the chosen site's date<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb Release Date</td></tr><tr><td>`tvdb`</td><td>Use TVDb Release Date</td></tr><tr><td>`omdb`</td><td>Use IMDb Release Date through OMDb</td></tr><tr><td>`mdb`</td><td>Use MdbList Release Date</td></tr><tr><td>`anidb`</td><td>Use AniDB Release Date</td></tr></table> | | `mass_originally_available_update` | Updates every item's originally available date in the library to the chosen site's date<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb Release Date</td></tr><tr><td>`tvdb`</td><td>Use TVDb Release Date</td></tr><tr><td>`omdb`</td><td>Use IMDb Release Date through OMDb</td></tr><tr><td>`mdb`</td><td>Use MdbList Release Date</td></tr><tr><td>`anidb`</td><td>Use AniDB Release Date</td></tr></table> |
| `mass_audience_rating_update`/<br>`mass_critic_rating_update` | Updates every item's audience/critic rating in the library to the chosen site's rating<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb Rating</td></tr><tr><td>`omdb`</td><td>Use IMDbRating through OMDb</td></tr><tr><td>`mdb`</td><td>Use MdbList Score</td></tr><tr><td>`mdb_imdb`</td><td>Use IMDb Rating through MDbList</td></tr><tr><td>`mdb_metacritic`</td><td>Use Metacritic Rating through MDbList</td></tr><tr><td>`mdb_metacriticuser`</td><td>Use Metacritic User Rating through MDbList</td></tr><tr><td>`mdb_trakt`</td><td>Use Trakt Rating through MDbList</td></tr><tr><td>`mdb_tomatoes`</td><td>Use Rotten Tomatoes Rating through MDbList</td></tr><tr><td>`mdb_tomatoesaudience`</td><td>Use Rotten Tomatoes Audience Rating through MDbList</td></tr><tr><td>`mdb_tmdb`</td><td>Use TMDb Rating through MDbList</td></tr><tr><td>`mdb_letterboxd`</td><td>Use Letterboxd Rating through MDbList</td></tr><tr><td>`anidb_rating`</td><td>Use AniDB Rating</td></tr><tr><td>`anidb_average`</td><td>Use AniDB Average</td></tr></table> | | `mass_audience_rating_update`/<br>`mass_critic_rating_update`/<br>`mass_user_rating_update` | Updates every item's audience/critic/user rating in the library to the chosen site's rating<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb Rating</td></tr><tr><td>`imdb`</td><td>Use IMDb Rating</td></tr><tr><td>`omdb`</td><td>Use IMDbRating through OMDb</td></tr><tr><td>`mdb`</td><td>Use MdbList Score</td></tr><tr><td>`mdb_imdb`</td><td>Use IMDb Rating through MDbList</td></tr><tr><td>`mdb_metacritic`</td><td>Use Metacritic Rating through MDbList</td></tr><tr><td>`mdb_metacriticuser`</td><td>Use Metacritic User Rating through MDbList</td></tr><tr><td>`mdb_trakt`</td><td>Use Trakt Rating through MDbList</td></tr><tr><td>`mdb_tomatoes`</td><td>Use Rotten Tomatoes Rating through MDbList</td></tr><tr><td>`mdb_tomatoesaudience`</td><td>Use Rotten Tomatoes Audience Rating through MDbList</td></tr><tr><td>`mdb_tmdb`</td><td>Use TMDb Rating through MDbList</td></tr><tr><td>`mdb_letterboxd`</td><td>Use Letterboxd Rating through MDbList</td></tr><tr><td>`anidb_rating`</td><td>Use AniDB Rating</td></tr><tr><td>`anidb_average`</td><td>Use AniDB Average</td></tr></table> |
| `mass_imdb_parental_labels` | Updates every item's labels in the library to match the IMDb Parental Guide<br>**Values** `with_none` or `without_none` | | `mass_episode_audience_rating_update`/<br>`mass_episode_critic_rating_update`/<br>`mass_episode_user_rating_update` | Updates every item's episode's audience/critic/user rating in the library to the chosen site's rating<br>**Values:** <table class="clearTable"><tr><td>`tmdb`</td><td>Use TMDb Rating</td></tr><tr><td>`imdb`</td><td>Use IMDb Rating</td></tr></table> |
| `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<br>**Values:** `true` or `false` | | `mass_imdb_parental_labels` | Updates every item's labels in the library to match the IMDb Parental Guide<br>**Values** `with_none` or `without_none` |
| `mass_collection_mode` | Updates every Collection in your library to the specified Collection Mode<br>**Values:** `default`: Library default<br>`hide`: Hide Collection<br>`hide_items`: Hide Items in this Collection<br>`show_items`: Show this Collection and its Items<table class="clearTable"><tr><td>`default`</td><td>Library default</td></tr><tr><td>`hide`</td><td>Hide Collection</td></tr><tr><td>`hide_items`</td><td>Hide Items in this Collection</td></tr><tr><td>`show_items`</td><td>Show this Collection and its Items</td></tr></table> | | `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<br>**Values:** `true` or `false` |
| `update_blank_track_titles` | Search though every track in a music library and replace any blank track titles with the tracks sort title<br>**Values:** `true` or `false` | | `mass_collection_mode` | Updates every Collection in your library to the specified Collection Mode<br>**Values:** `default`: Library default<br>`hide`: Hide Collection<br>`hide_items`: Hide Items in this Collection<br>`show_items`: Show this Collection and its Items<table class="clearTable"><tr><td>`default`</td><td>Library default</td></tr><tr><td>`hide`</td><td>Hide Collection</td></tr><tr><td>`hide_items`</td><td>Hide Items in this Collection</td></tr><tr><td>`show_items`</td><td>Show this Collection and its Items</td></tr></table> |
| `remove_title_parentheses` | Search through every title and remove all ending parentheses in an items title if the title isn not locked.<br>**Values:** `true` or `false` | | `update_blank_track_titles` | Search though every track in a music library and replace any blank track titles with the tracks sort title<br>**Values:** `true` or `false` |
| `split_duplicates` | Splits all duplicate movies/shows found in this library<br>**Values:** `true` or `false` | | `remove_title_parentheses` | Search through every title and remove all ending parentheses in an items title if the title isn not locked.<br>**Values:** `true` or `false` |
| `radarr_add_all` | Adds every item in the library to Radarr. The existing paths in plex will be used as the root folder of each item, if the paths in Plex are not the same as your Radarr paths you can use the `plex_path` and `radarr_path` [Radarr](radarr) details to convert the paths.<br>**Values:** `true` or `false` | | `split_duplicates` | Splits all duplicate movies/shows found in this library<br>**Values:** `true` or `false` |
| `radarr_remove_by_tag` | Removes every item from Radarr with the Tags given<br>**Values:** List or comma separated string of tags | | `radarr_add_all` | Adds every item in the library to Radarr. The existing paths in plex will be used as the root folder of each item, if the paths in Plex are not the same as your Radarr paths you can use the `plex_path` and `radarr_path` [Radarr](radarr) details to convert the paths.<br>**Values:** `true` or `false` |
| `sonarr_add_all` | Adds every item in the library to Sonarr. The existing paths in plex will be used as the root folder of each item, if the paths in Plex are not the same as your Sonarr paths you can use the `plex_path` and `sonarr_path` [Sonarr](sonarr) details to convert the paths.<br>**Values:** `true` or `false` | | `radarr_remove_by_tag` | Removes every item from Radarr with the Tags given<br>**Values:** List or comma separated string of tags |
| `sonarr_remove_by_tag` | Removes every item from Sonarr with the Tags given<br>**Values:** List or comma separated string of tags | | `sonarr_add_all` | Adds every item in the library to Sonarr. The existing paths in plex will be used as the root folder of each item, if the paths in Plex are not the same as your Sonarr paths you can use the `plex_path` and `sonarr_path` [Sonarr](sonarr) details to convert the paths.<br>**Values:** `true` or `false` |
| [`genre_mapper`](#genre-mapper) | Allows genres to be changed to other genres or be removed from every item in your library.<br>**Values:** [see below for usage](#genre-mapper) | | `sonarr_remove_by_tag` | Removes every item from Sonarr with the Tags given<br>**Values:** List or comma separated string of tags |
| [`content_rating_mapper`](#content-rating-mapper) | Allows content ratings to be changed to other content ratings or be removed from every item in your library.<br>**Values:** [see below for usage](#content-rating-mapper) | | [`genre_mapper`](#genre-mapper) | Allows genres to be changed to other genres or be removed from every item in your library.<br>**Values:** [see below for usage](#genre-mapper) |
| [`metadata_backup`](#metadata-backup) | Creates/Maintains a PMM [Metadata File](../metadata/metadata) with a full `metadata` mapping based on the library's items locked attributes.<br>**Values:** [see below for usage](#metadata-backup) | | [`content_rating_mapper`](#content-rating-mapper) | Allows content ratings to be changed to other content ratings or be removed from every item in your library.<br>**Values:** [see below for usage](#content-rating-mapper) |
| [`metadata_backup`](#metadata-backup) | Creates/Maintains a PMM [Metadata File](../metadata/metadata) with a full `metadata` mapping based on the library's items locked attributes.<br>**Values:** [see below for usage](#metadata-backup) |
## Genre Mapper ## Genre Mapper

View file

@ -33,12 +33,14 @@ from retrying import retry
logger = util.logger logger = util.logger
sync_modes = {"append": "Only Add Items to the Collection or Playlist", "sync": "Add & Remove Items from the Collection or Playlist"} sync_modes = {"append": "Only Add Items to the Collection or Playlist", "sync": "Add & Remove Items from the Collection or Playlist"}
mass_genre_options = {"tmdb": "Use TMDb Metadata", "omdb": "Use IMDb Metadata through OMDb", "tvdb": "Use TVDb Metadata", "anidb": "Use AniDB Tag Metadata"} mass_genre_options = {"tmdb": "Use TMDb Metadata", "imdb": "Use IMDb Rating", "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_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"} 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"} imdb_label_options = {"with_none": "Add IMDb Parental Labels including None", "without_none": "Add IMDb Parental Labels including None"}
mass_episode_rating_options = {"tmdb": "Use TMDb Rating", "imdb": "Use IMDb Rating"}
mass_rating_options = { mass_rating_options = {
"tmdb": "Use TMDb Rating", "tmdb": "Use TMDb Rating",
"imdb": "Use IMDb Rating",
"omdb": "Use IMDb Rating through OMDb", "omdb": "Use IMDb Rating through OMDb",
"mdb": "Use MdbList Average Score", "mdb": "Use MdbList Average Score",
"mdb_imdb": "Use IMDb Rating through MDbList", "mdb_imdb": "Use IMDb Rating through MDbList",
@ -584,6 +586,9 @@ class ConfigFile:
"mass_originally_available_update": None, "mass_originally_available_update": None,
"mass_imdb_parental_labels": None, "mass_imdb_parental_labels": None,
"remove_title_parentheses": None, "remove_title_parentheses": None,
"mass_episode_audience_rating_update": None,
"mass_episode_critic_rating_update": None,
"mass_episode_user_rating_update": None,
} }
display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"] display_name = f"{params['name']} ({params['mapping_name']})" if lib and "library_name" in lib and lib["library_name"] else params["mapping_name"]
@ -652,6 +657,14 @@ class ConfigFile:
params["mass_audience_rating_update"] = check_for_attribute(lib["operations"], "mass_audience_rating_update", test_list=mass_rating_options, default_is_none=True, save=False) params["mass_audience_rating_update"] = check_for_attribute(lib["operations"], "mass_audience_rating_update", test_list=mass_rating_options, default_is_none=True, save=False)
if "mass_critic_rating_update" in lib["operations"]: if "mass_critic_rating_update" in lib["operations"]:
params["mass_critic_rating_update"] = check_for_attribute(lib["operations"], "mass_critic_rating_update", test_list=mass_rating_options, default_is_none=True, save=False) params["mass_critic_rating_update"] = check_for_attribute(lib["operations"], "mass_critic_rating_update", test_list=mass_rating_options, default_is_none=True, save=False)
if "mass_user_rating_update" in lib["operations"]:
params["mass_user_rating_update"] = check_for_attribute(lib["operations"], "mass_user_rating_update", test_list=mass_rating_options, default_is_none=True, save=False)
if "mass_episode_audience_rating_update" in lib["operations"]:
params["mass_episode_audience_rating_update"] = check_for_attribute(lib["operations"], "mass_episode_audience_rating_update", test_list=mass_episode_rating_options, default_is_none=True, save=False)
if "mass_episode_critic_rating_update" in lib["operations"]:
params["mass_episode_critic_rating_update"] = check_for_attribute(lib["operations"], "mass_episode_critic_rating_update", test_list=mass_episode_rating_options, default_is_none=True, save=False)
if "mass_episode_user_rating_update" in lib["operations"]:
params["mass_episode_user_rating_update"] = check_for_attribute(lib["operations"], "mass_episode_user_rating_update", test_list=mass_episode_rating_options, default_is_none=True, save=False)
if "mass_content_rating_update" in lib["operations"]: if "mass_content_rating_update" in lib["operations"]:
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) 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"]: if "mass_originally_available_update" in lib["operations"]:

View file

@ -1,4 +1,4 @@
import math, re, time import csv, gzip, math, os, re, requests, shutil, time
from modules import util from modules import util
from modules.util import Failed from modules.util import Failed
from urllib.parse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
@ -30,6 +30,9 @@ urls = {
class IMDb: class IMDb:
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
self._ratings = None
self._genres = None
self._episode_ratings = None
def validate_imdb_lists(self, err_type, imdb_lists, language): def validate_imdb_lists(self, err_type, imdb_lists, language):
valid_lists = [] valid_lists = []
@ -185,3 +188,71 @@ class IMDb:
return [(_i, "imdb") for _i in self._ids_from_chart(data)] return [(_i, "imdb") for _i in self._ids_from_chart(data)]
else: else:
raise Failed(f"IMDb Error: Method {method} not supported") raise Failed(f"IMDb Error: Method {method} not supported")
def _interface(self, interface):
gz = os.path.join(self.config.default_dir, f"title.{interface}.tsv.gz")
tsv = os.path.join(self.config.default_dir, f"title.{interface}.tsv")
if os.path.exists(gz):
os.remove(gz)
if os.path.exists(tsv):
os.remove(tsv)
with requests.get(f"https://datasets.imdbws.com/title.{interface}.tsv.gz", stream=True) as r:
r.raise_for_status()
total_length = r.headers.get('content-length')
if total_length is not None:
total_length = int(total_length)
dl = 0
with open(gz, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
dl += len(chunk)
f.write(chunk)
logger.ghost(f"Downloading IMDb Interface: {dl / total_length * 100:6.2f}%")
logger.exorcise()
with open(tsv, "wb") as f_out:
with gzip.open(gz, "rb") as f_in:
shutil.copyfileobj(f_in, f_out)
with open(tsv, "r") as t:
if interface == "ratings":
return {line[0]: line[1] for line in csv.reader(t, delimiter="\t")}
elif interface == "basics":
return {line[0]: str(line[-1]).split(",") for line in csv.reader(tsv, delimiter="\t")}
else:
return [line for line in csv.reader(t, delimiter="\t")]
@property
def ratings(self):
if self._ratings is None:
self._ratings = self._interface("ratings")
return self._ratings
@property
def genres(self):
if self._genres is None:
self._genres = self._interface("basics")
return self._genres
@property
def episode_ratings(self):
if self._episode_ratings is None:
self._episode_ratings = {}
for imdb_id, parent_id, season_num, episode_num in self._interface("episode"):
if imdb_id not in self.ratings:
continue
if parent_id not in self._episode_ratings:
self._episode_ratings[parent_id] = {}
if season_num not in self._episode_ratings[parent_id]:
self._episode_ratings[parent_id][season_num] = {}
self._episode_ratings[parent_id][season_num][episode_num] = self.ratings[imdb_id]
return self._episode_ratings
def get_rating(self, imdb_id):
return self.ratings[imdb_id] if imdb_id in self.ratings else None
def get_episode_rating(self, imdb_id, season_num, episode_num):
if imdb_id not in self.episode_ratings or season_num not in self.episode_ratings[imdb_id] or episode_num not in self.episode_ratings[imdb_id][season_num]:
return None
return self.episode_ratings[imdb_id][season_num][episode_num]

View file

@ -77,6 +77,10 @@ class Library(ABC):
self.mass_genre_update = params["mass_genre_update"] self.mass_genre_update = params["mass_genre_update"]
self.mass_audience_rating_update = params["mass_audience_rating_update"] self.mass_audience_rating_update = params["mass_audience_rating_update"]
self.mass_critic_rating_update = params["mass_critic_rating_update"] self.mass_critic_rating_update = params["mass_critic_rating_update"]
self.mass_user_rating_update = params["mass_user_rating_update"]
self.mass_episode_audience_rating_update = params["mass_episode_audience_rating_update"]
self.mass_episode_critic_rating_update = params["mass_episode_critic_rating_update"]
self.mass_episode_user_rating_update = params["mass_episode_user_rating_update"]
self.mass_content_rating_update = params["mass_content_rating_update"] self.mass_content_rating_update = params["mass_content_rating_update"]
self.mass_originally_available_update = params["mass_originally_available_update"] self.mass_originally_available_update = params["mass_originally_available_update"]
self.mass_imdb_parental_labels = params["mass_imdb_parental_labels"] self.mass_imdb_parental_labels = params["mass_imdb_parental_labels"]
@ -101,13 +105,18 @@ class Library(ABC):
self.stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0, "names": []} self.stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0, "names": []}
self.status = {} 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.remove_title_parentheses \ self.items_library_operation = True if self.assets_for_all or self.mass_genre_update or self.remove_title_parentheses \
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.mass_audience_rating_update or self.mass_critic_rating_update or self.mass_user_rating_update \
or self.genre_mapper or self.content_rating_mapper or self.radarr_add_all_existing or self.sonarr_add_all_existing else False or self.mass_episode_audience_rating_update or self.mass_episode_critic_rating_update or self.mass_episode_user_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.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 \ 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 \ or self.radarr_remove_by_tag or self.sonarr_remove_by_tag or self.mass_collection_mode \
or self.show_unmanaged or self.metadata_backup or self.update_blank_track_titles else False or self.show_unmanaged or self.metadata_backup or self.update_blank_track_titles else False
self.meta_operations = [self.mass_genre_update, self.mass_audience_rating_update, self.mass_critic_rating_update, self.mass_content_rating_update, self.mass_originally_available_update] self.meta_operations = [self.mass_genre_update, self.mass_audience_rating_update, self.mass_critic_rating_update,
self.mass_user_rating_update, self.mass_episode_audience_rating_update, self.mass_episode_critic_rating_update,
self.mass_episode_user_rating_update, self.mass_content_rating_update, self.mass_originally_available_update]
if self.asset_directory: if self.asset_directory:
logger.info("") logger.info("")

View file

@ -2,8 +2,6 @@ import os, re
from datetime import datetime from datetime import datetime
from modules import plex, util from modules import plex, util
from modules.util import Failed, YAML from modules.util import Failed, YAML
from plexapi.audio import Artist
from plexapi.video import Show
logger = util.logger logger = util.logger
@ -23,6 +21,10 @@ class Operations:
logger.debug(f"Mass Genre Update: {self.library.mass_genre_update}") logger.debug(f"Mass Genre Update: {self.library.mass_genre_update}")
logger.debug(f"Mass Audience Rating Update: {self.library.mass_audience_rating_update}") logger.debug(f"Mass Audience Rating Update: {self.library.mass_audience_rating_update}")
logger.debug(f"Mass Critic Rating Update: {self.library.mass_critic_rating_update}") logger.debug(f"Mass Critic Rating Update: {self.library.mass_critic_rating_update}")
logger.debug(f"Mass User Rating Update: {self.library.mass_user_rating_update}")
logger.debug(f"Mass Episode Audience Rating Update: {self.library.mass_episode_audience_rating_update}")
logger.debug(f"Mass Episode Critic Rating Update: {self.library.mass_episode_critic_rating_update}")
logger.debug(f"Mass Episode User Rating Update: {self.library.mass_episode_user_rating_update}")
logger.debug(f"Mass Content Rating Update: {self.library.mass_content_rating_update}") logger.debug(f"Mass Content Rating Update: {self.library.mass_content_rating_update}")
logger.debug(f"Mass Originally Available Update: {self.library.mass_originally_available_update}") logger.debug(f"Mass Originally Available Update: {self.library.mass_originally_available_update}")
logger.debug(f"Mass IMDb Parental Labels: {self.library.mass_imdb_parental_labels}") logger.debug(f"Mass IMDb Parental Labels: {self.library.mass_imdb_parental_labels}")
@ -136,21 +138,18 @@ class Operations:
omdb_item = None omdb_item = None
if any([o == "omdb" for o in self.library.meta_operations]): if any([o == "omdb" for o in self.library.meta_operations]):
if self.config.OMDb.limit is False: if self.config.OMDb.limit is not False:
if tmdb_id and not imdb_id: logger.error("Daily OMDb Limit Reached")
imdb_id = self.config.Convert.tmdb_to_imdb(tmdb_id) elif not imdb_id:
elif tvdb_id and not imdb_id: logger.info(f"{item.title[:25]:<25} | No IMDb ID for Guid: {item.guid}")
imdb_id = self.config.Convert.tvdb_to_imdb(tvdb_id) else:
if imdb_id: try:
try: omdb_item = self.config.OMDb.get_omdb(imdb_id)
omdb_item = self.config.OMDb.get_omdb(imdb_id) except Failed as e:
except Failed as e: logger.error(str(e))
logger.error(str(e)) except Exception:
except Exception: logger.error(f"IMDb ID: {imdb_id}")
logger.error(f"IMDb ID: {imdb_id}") raise
raise
else:
logger.info(f"{item.title[:25]:<25} | No IMDb ID for Guid: {item.guid}")
tvdb_item = None tvdb_item = None
if any([o == "tvdb" for o in self.library.meta_operations]): if any([o == "tvdb" for o in self.library.meta_operations]):
@ -200,6 +199,8 @@ class Operations:
def get_rating(attribute): def get_rating(attribute):
if tmdb_item and attribute == "tmdb": if tmdb_item and attribute == "tmdb":
return tmdb_item.vote_average return tmdb_item.vote_average
elif imdb_id and attribute == "imdb":
return self.config.imdb.get_rating(imdb_id)
elif omdb_item and attribute == "omdb": elif omdb_item and attribute == "omdb":
return omdb_item.imdb_rating return omdb_item.imdb_rating
elif mdb_item and attribute == "mdb": elif mdb_item and attribute == "mdb":
@ -233,6 +234,8 @@ class Operations:
if self.library.mass_genre_update: if self.library.mass_genre_update:
if tmdb_item and self.library.mass_genre_update == "tmdb": if tmdb_item and self.library.mass_genre_update == "tmdb":
new_genres = tmdb_item.genres new_genres = tmdb_item.genres
elif imdb_id and self.library.mass_genre_update == "imdb" and imdb_id in self.config.imdb.genres:
new_genres = self.config.imdb.genres[imdb_id]
elif omdb_item and self.library.mass_genre_update == "omdb": elif omdb_item and self.library.mass_genre_update == "omdb":
new_genres = omdb_item.genres new_genres = omdb_item.genres
elif tvdb_item and self.library.mass_genre_update == "tvdb": elif tvdb_item and self.library.mass_genre_update == "tvdb":
@ -276,7 +279,18 @@ class Operations:
logger.info(f"{item.title[:25]:<25} | No Rating Found") logger.info(f"{item.title[:25]:<25} | No Rating Found")
elif str(item.rating) != str(new_rating): elif str(item.rating) != str(new_rating):
item.editField("rating", new_rating) item.editField("rating", new_rating)
batch_display += f"{item.title[:25]:<25} | Critic Rating | {new_rating}" batch_display += f"\n{item.title[:25]:<25} | Critic Rating | {new_rating}"
except Failed:
pass
if self.library.mass_user_rating_update:
try:
new_rating = get_rating(self.library.mass_user_rating_update)
if new_rating is None:
logger.info(f"{item.title[:25]:<25} | No Rating Found")
elif str(item.userRating) != str(new_rating):
item.editField("userRating", new_rating)
batch_display += f"\n{item.title[:25]:<25} | User Rating | {new_rating}"
except Failed: except Failed:
pass pass
@ -327,6 +341,62 @@ class Operations:
pass pass
item.saveEdits() item.saveEdits()
logger.info(batch_display)
episode_ops = [self.library.mass_episode_audience_rating_update, self.library.mass_episode_critic_rating_update, self.library.mass_episode_user_rating_update]
if any([x is not None for x in episode_ops]):
if any([x == "imdb" for x in episode_ops]) and not imdb_id:
logger.info(f"{item.title[:25]:<25} | No IMDb ID for Guid: {item.guid}")
for ep in item.episodes():
ep.batchEdits()
item_title = self.library.get_item_sort_title(ep, atr="title")
def get_episode_rating(attribute):
if tmdb_id and attribute == "tmdb":
try:
return self.config.TMDb.get_episode(tmdb_id, ep.seasonNumber, ep.episodeNumber).vote_average
except Failed as er:
logger.error(er)
elif imdb_id and attribute == "imdb":
return self.config.IMDb.get_episode_rating(imdb_id, ep.seasonNumber, ep.episodeNumber)
else:
raise Failed
if self.library.mass_episode_audience_rating_update:
try:
new_rating = get_episode_rating(self.library.mass_episode_audience_rating_update)
if new_rating is None:
logger.info(f"{item_title[:25]:<25} | No Rating Found")
elif str(ep.audienceRating) != str(new_rating):
ep.editField("audienceRating", new_rating)
logger.info(f"\n{item_title[:25]:<25} | Audience Rating | {new_rating}")
except Failed:
pass
if self.library.mass_episode_critic_rating_update:
try:
new_rating = get_episode_rating(self.library.mass_episode_critic_rating_update)
if new_rating is None:
logger.info(f"{item_title[:25]:<25} | No Rating Found")
elif str(ep.rating) != str(new_rating):
ep.editField("rating", new_rating)
logger.info(f"{item_title[:25]:<25} | Critic Rating | {new_rating}")
except Failed:
pass
if self.library.mass_episode_user_rating_update:
try:
new_rating = get_episode_rating(self.library.mass_episode_user_rating_update)
if new_rating is None:
logger.info(f"{item_title[:25]:<25} | No Rating Found")
elif str(ep.userRating) != str(new_rating):
ep.editField("userRating", new_rating)
logger.info(f"{item_title[:25]:<25} | User Rating | {new_rating}")
except Failed:
pass
if self.library.Radarr and self.library.radarr_add_all_existing: if self.library.Radarr and self.library.radarr_add_all_existing:
try: try:

View file

@ -3,7 +3,6 @@ from datetime import datetime
from modules import plex, util from modules import plex, util
from modules.builder import CollectionBuilder from modules.builder import CollectionBuilder
from modules.util import Failed, NotScheduled from modules.util import Failed, NotScheduled
from plexapi.audio import Album
from plexapi.exceptions import BadRequest from plexapi.exceptions import BadRequest
from plexapi.video import Movie, Show, Season, Episode from plexapi.video import Movie, Show, Season, Episode
from PIL import Image, ImageFilter from PIL import Image, ImageFilter
@ -37,7 +36,7 @@ class Overlays:
logger.separator(f"Removing {old_overlay.title}") logger.separator(f"Removing {old_overlay.title}")
logger.info("") logger.info("")
for i, item in enumerate(label_items, 1): for i, item in enumerate(label_items, 1):
item_title = self.get_item_sort_title(item, atr="title") item_title = self.library.get_item_sort_title(item, atr="title")
logger.ghost(f"Restoring {old_overlay.title}: {i}/{len(label_items)} {item_title}") logger.ghost(f"Restoring {old_overlay.title}: {i}/{len(label_items)} {item_title}")
self.remove_overlay(item, item_title, old_overlay.title, [ self.remove_overlay(item, item_title, old_overlay.title, [
os.path.join(self.library.overlay_folder, old_overlay.title[:-8], f"{item.ratingKey}.png") os.path.join(self.library.overlay_folder, old_overlay.title[:-8], f"{item.ratingKey}.png")
@ -61,7 +60,7 @@ class Overlays:
if remove_overlays: if remove_overlays:
logger.separator(f"Removing Overlays for the {self.library.name} Library") logger.separator(f"Removing Overlays for the {self.library.name} Library")
for i, item in enumerate(remove_overlays, 1): for i, item in enumerate(remove_overlays, 1):
item_title = self.get_item_sort_title(item, atr="title") item_title = self.library.get_item_sort_title(item, atr="title")
logger.ghost(f"Restoring: {i}/{len(remove_overlays)} {item_title}") logger.ghost(f"Restoring: {i}/{len(remove_overlays)} {item_title}")
self.remove_overlay(item, item_title, "Overlay", [ self.remove_overlay(item, item_title, "Overlay", [
os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png"), os.path.join(self.library.overlay_backup, f"{item.ratingKey}.png"),
@ -75,9 +74,9 @@ class Overlays:
logger.info("") logger.info("")
logger.separator(f"Applying Overlays for the {self.library.name} Library") logger.separator(f"Applying Overlays for the {self.library.name} Library")
logger.info("") logger.info("")
for i, (over_key, (item, over_names)) in enumerate(sorted(key_to_overlays.items(), key=lambda io: self.get_item_sort_title(io[1][0])), 1): for i, (over_key, (item, over_names)) in enumerate(sorted(key_to_overlays.items(), key=lambda io: self.library.get_item_sort_title(io[1][0])), 1):
try: try:
item_title = self.get_item_sort_title(item, atr="title") item_title = self.library.get_item_sort_title(item, atr="title")
logger.ghost(f"Overlaying: {i}/{len(key_to_overlays)} {item_title}") logger.ghost(f"Overlaying: {i}/{len(key_to_overlays)} {item_title}")
image_compare = None image_compare = None
overlay_compare = None overlay_compare = None
@ -278,16 +277,6 @@ class Overlays:
logger.separator(f"Finished {self.library.name} Library Overlays\nOverlays Run Time: {overlay_run_time}") logger.separator(f"Finished {self.library.name} Library Overlays\nOverlays Run Time: {overlay_run_time}")
return overlay_run_time return overlay_run_time
def get_item_sort_title(self, item_to_sort, atr="titleSort"):
if isinstance(item_to_sort, Album):
return f"{getattr(item_to_sort.artist(), atr)} Album {getattr(item_to_sort, atr)}"
elif isinstance(item_to_sort, Season):
return f"{getattr(item_to_sort.show(), atr)} Season {item_to_sort.seasonNumber}"
elif isinstance(item_to_sort, Episode):
return f"{getattr(item_to_sort.show(), atr)} {item_to_sort.seasonEpisode.upper()}"
else:
return getattr(item_to_sort, atr)
def compile_overlays(self): def compile_overlays(self):
key_to_item = {} key_to_item = {}
properties = {} properties = {}
@ -334,7 +323,7 @@ class Overlays:
if item.ratingKey not in properties[builder.overlay.name].keys: if item.ratingKey not in properties[builder.overlay.name].keys:
properties[builder.overlay.name].keys.append(item.ratingKey) properties[builder.overlay.name].keys.append(item.ratingKey)
if added_titles: if added_titles:
logger.debug(f"{len(added_titles)} Titles Found: {[self.get_item_sort_title(a, atr='title') for a in added_titles]}") logger.debug(f"{len(added_titles)} Titles Found: {[self.library.get_item_sort_title(a, atr='title') for a in added_titles]}")
logger.info(f"{len(added_titles) if added_titles else 'No'} Items found for {builder.overlay.name}") logger.info(f"{len(added_titles) if added_titles else 'No'} Items found for {builder.overlay.name}")
except NotScheduled as e: except NotScheduled as e:
logger.info(e) logger.info(e)

View file

@ -1184,6 +1184,16 @@ class Plex(Library):
return map_key, attrs return map_key, attrs
def get_item_sort_title(self, item_to_sort, atr="titleSort"):
if isinstance(item_to_sort, Album):
return f"{getattr(item_to_sort.artist(), atr)} Album {getattr(item_to_sort, atr)}"
elif isinstance(item_to_sort, Season):
return f"{getattr(item_to_sort.show(), atr)} Season {item_to_sort.seasonNumber}"
elif isinstance(item_to_sort, Episode):
return f"{getattr(item_to_sort.show(), atr)} {item_to_sort.seasonEpisode.upper()}"
else:
return getattr(item_to_sort, atr)
def split(self, text): def split(self, text):
attribute, modifier = os.path.splitext(str(text).lower()) attribute, modifier = os.path.splitext(str(text).lower())
attribute = method_alias[attribute] if attribute in method_alias else attribute attribute = method_alias[attribute] if attribute in method_alias else attribute
@ -1356,4 +1366,4 @@ class Plex(Library):
elif (not list(set(filter_data) & set(attrs)) and modifier == "") \ elif (not list(set(filter_data) & set(attrs)) and modifier == "") \
or (list(set(filter_data) & set(attrs)) and modifier == ".not"): or (list(set(filter_data) & set(attrs)) and modifier == ".not"):
return False return False
return True return True