[26] add mass_studio_update

This commit is contained in:
meisnate12 2023-01-25 02:43:25 -05:00
parent 6d81d34be2
commit 8e8a14ab35
8 changed files with 106 additions and 23 deletions

View file

@ -1 +1 @@
1.18.3-develop25
1.18.3-develop26

View file

@ -23,6 +23,7 @@ The available attributes for the operations attribute are as follows
| [Mass Genre Update](#mass-genre-update) | Updates every item's genres in the library to the chosen site's genres. | ✅ | ✅ | ✅ |
| [Mass Content Rating Update](#mass-content-rating-update) | Updates every item's content rating in the library to the chosen site's content rating. | ✅ | ✅ | ❌ |
| [Mass Original Title Update](#mass-original-title-update) | Updates every item's original title in the library to the chosen site's original title. | ✅ | ✅ | ❌ |
| [Mass Studio Update](#mass-studio-update) | Updates every item's studio in the library to the chosen site's studio. | ✅ | ✅ | ❌ |
| [Mass Originally Available Update](#mass-originally-available-update) | Updates every item's originally available date in the library to the chosen site's date. | ✅ | ✅ | ❌ |
| [Mass * Rating Update](#mass--rating-update) | Updates every item's audience/critic/user rating in the library to the chosen site's rating. | ✅ | ✅ | ❌ |
| [Mass Episode * Rating Update](#mass-episode--rating-update) | Updates every item's episode's audience/critic/user rating in the library to the chosen site's rating. | ❌ | ✅ | ❌ |
@ -137,6 +138,24 @@ Updates every item's original title in the library to the chosen site's original
| `remove` | Remove Original Title and Lock Field |
| `reset` | Remove Original Title and Unlock Field |
## Mass Studio Update
Updates every item's studio in the library to the chosen site's studio.
**Attribute:** `mass_studio_update`
**Values:**
| Value | Description |
|:---------|:---------------------------------------|
| `anidb` | Use AniDB Animation Work for Studio |
| `mal` | Use MyAnimeList Studio for Studio |
| `tmdb` | Use TMDb Studio for Studio |
| `lock` | Lock Original Title Field |
| `unlock` | Unlock Original Title Field |
| `remove` | Remove Original Title and Lock Field |
| `reset` | Remove Original Title and Unlock Field |
## Mass Originally Available Update
Updates every item's originally available date in the library to the chosen site's date.

View file

@ -22,29 +22,29 @@ class AniDBObj:
self.anidb_id = anidb_id
self._data = data
def _parse(attr, xpath, is_list=False, is_dict=False, is_float=False, is_date=False, fail=False):
def _parse(attr, xpath, is_list=False, is_dict=False, is_int=False, is_float=False, is_date=False, fail=False):
try:
if isinstance(data, dict):
if is_list:
return data[attr].split("|") if data[attr] else []
elif is_dict:
return json.loads(data[attr])
elif is_float:
return util.check_num(data[attr], is_int=False)
elif is_int or is_float:
return util.check_num(data[attr], is_int=is_int)
elif is_date:
return datetime.strptime(data[attr], "%Y-%m-%d")
else:
return data[attr]
parse_results = data.xpath(xpath)
if len(parse_results) > 0:
if is_dict:
return {ta.get("xml:lang"): ta.text_content() for ta in parse_results}
elif len(parse_results) > 0:
parse_results = [r.strip() for r in parse_results if len(r) > 0]
if parse_results:
if is_list:
return parse_results
elif is_dict:
return {ta.get("xml:lang"): ta.text_content() for ta in parse_results}
elif is_float:
return float(parse_results[0])
elif is_int or is_float:
return util.check_num(parse_results[0], is_int=is_int)
elif is_date:
return datetime.strptime(parse_results[0], "%Y-%m-%d")
else:
@ -63,11 +63,26 @@ class AniDBObj:
self.main_title = _parse("main_title", "//anime/titles/title[@type='main']/text()", fail=True)
self.titles = _parse("titles", "//anime/titles/title[@type='official']", is_dict=True)
self.official_title = self.titles[self._anidb.language] if self._anidb.language in self.titles else self.main_title
self.studio = _parse("studio", "//anime/creators/name[@type='Animation Work']/text()")
self.rating = _parse("rating", "//anime/ratings/permanent/text()", is_float=True)
self.average = _parse("average", "//anime/ratings/temporary/text()", is_float=True)
self.score = _parse("score", "//anime/ratings/review/text()", is_float=True)
self.released = _parse("released", "//anime/startdate/text()", is_date=True)
self.tags = _parse("tags", "//anime/tags/tag[@infobox='true']/name/text()", is_list=True)
self.mal_id = _parse("mal_id", "//anime/resources/resource[@type='2']/externalentity/identifier/text()", is_int=True)
self.imdb_id = _parse("imdb_id", "//anime/resources/resource[@type='43']/externalentity/identifier/text()")
if isinstance(data, dict):
self.tmdb_id = _parse("tmdb_id", "", is_int=True)
self.tmdb_type = _parse("tmdb_type", "")
else:
tmdb = _parse("tmdb", "//anime/resources/resource[@type='44']/externalentity/identifier/text()", is_list=True)
self.tmdb_id = None
self.tmdb_type = None
for i in tmdb:
try:
self.tmdb_id = int(i)
except ValueError:
self.tmdb_type = i
class AniDB:

View file

@ -30,6 +30,8 @@ class Cache:
cursor.execute("DROP TABLE IF EXISTS tvdb_data")
cursor.execute("DROP TABLE IF EXISTS tvdb_data2")
cursor.execute("DROP TABLE IF EXISTS overlay_ratings")
cursor.execute("DROP TABLE IF EXISTS anidb_data")
cursor.execute("DROP TABLE IF EXISTS mal_data")
cursor.execute(
"""CREATE TABLE IF NOT EXISTS guids_map (
key INTEGER PRIMARY KEY,
@ -121,20 +123,25 @@ class Cache:
expiration_date TEXT)"""
)
cursor.execute(
"""CREATE TABLE IF NOT EXISTS anidb_data (
"""CREATE TABLE IF NOT EXISTS anidb_data2 (
key INTEGER PRIMARY KEY,
anidb_id INTEGER UNIQUE,
main_title TEXT,
titles TEXT,
studio TEXT,
rating REAL,
average REAL,
score REAL,
released TEXT,
tags TEXT,
mal_id INTEGER,
imdb_id TEXT,
tmdb_id INTEGER,
tmdb_type TEXT,
expiration_date TEXT)"""
)
cursor.execute(
"""CREATE TABLE IF NOT EXISTS mal_data (
"""CREATE TABLE IF NOT EXISTS mal_data2 (
key INTEGER PRIMARY KEY,
mal_id INTEGER UNIQUE,
title TEXT,
@ -148,6 +155,7 @@ class Cache:
rank INTEGER,
popularity TEXT,
genres TEXT,
studio TEXT,
expiration_date TEXT)"""
)
cursor.execute(
@ -518,16 +526,21 @@ 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 anidb_data WHERE anidb_id = ?", (anidb_id,))
cursor.execute("SELECT * FROM anidb_data2 WHERE anidb_id = ?", (anidb_id,))
row = cursor.fetchone()
if row:
anidb_dict["main_title"] = row["main_title"]
anidb_dict["titles"] = row["titles"] if row["titles"] else None
anidb_dict["studio"] = row["studio"] if row["studio"] else None
anidb_dict["rating"] = row["rating"] if row["rating"] else None
anidb_dict["average"] = row["average"] if row["average"] else None
anidb_dict["score"] = row["score"] if row["score"] else None
anidb_dict["released"] = row["released"] if row["released"] else None
anidb_dict["tags"] = row["tags"] if row["tags"] else None
anidb_dict["mal_id"] = row["mal_id"] if row["mal_id"] else None
anidb_dict["imdb_id"] = row["imdb_id"] if row["imdb_id"] else None
anidb_dict["tmdb_id"] = row["tmdb_id"] if row["tmdb_id"] else None
anidb_dict["tmdb_type"] = row["tmdb_type"] if row["tmdb_type"] 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
@ -538,12 +551,13 @@ 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 anidb_data(anidb_id) VALUES(?)", (anidb_id,))
update_sql = "UPDATE anidb_data SET main_title = ?, titles = ?, rating = ?, average = ?, score = ?, " \
"released = ?, tags = ?, expiration_date = ? WHERE anidb_id = ?"
cursor.execute("INSERT OR IGNORE INTO anidb_data2(anidb_id) VALUES(?)", (anidb_id,))
update_sql = "UPDATE anidb_data SET main_title = ?, titles = ?, studio = ?, rating = ?, average = ?, score = ?, " \
"released = ?, tags = ?, mal_id = ?, imdb_id = ?, tmdb_id = ?, tmdb_type = ?, expiration_date = ? WHERE anidb_id = ?"
cursor.execute(update_sql, (
anidb.main_title, str(anidb.titles), anidb.rating, anidb.average, anidb.score,
anidb.main_title, str(anidb.titles), anidb.studio, anidb.rating, anidb.average, anidb.score,
anidb.released.strftime("%Y-%m-%d") if anidb.released else None, "|".join(anidb.tags),
anidb_id.mal_id, anidb.imdb_id, anidb.tmdb_id, anidb.tmdb_type,
expiration_date.strftime("%Y-%m-%d"), anidb_id
))
@ -553,7 +567,7 @@ 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 mal_data WHERE mal_id = ?", (mal_id,))
cursor.execute("SELECT * FROM mal_data2 WHERE mal_id = ?", (mal_id,))
row = cursor.fetchone()
if row:
mal_dict["title"] = row["title"]
@ -567,6 +581,7 @@ class Cache:
mal_dict["rank"] = row["rank"] if row["rank"] else None
mal_dict["popularity"] = row["popularity"] if row["popularity"] else None
mal_dict["genres"] = row["genres"] if row["genres"] else None
mal_dict["studio"] = row["studio"] if row["studio"] 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
@ -577,12 +592,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 mal_data(mal_id) VALUES(?)", (mal_id,))
cursor.execute("INSERT OR IGNORE INTO mal_data2(mal_id) VALUES(?)", (mal_id,))
update_sql = "UPDATE mal_data SET title = ?, title_english = ?, title_japanese = ?, status = ?, airing = ?, " \
"aired = ?, rating = ?, score = ?, rank = ?, popularity = ?, genres = ? , expiration_date = ? WHERE mal_id = ?"
"aired = ?, rating = ?, score = ?, rank = ?, popularity = ?, genres = ?, studio = ? expiration_date = ? WHERE mal_id = ?"
cursor.execute(update_sql, (
mal.title, mal.title_english, mal.title_japanese, mal.status, mal.airing, mal.aired.strftime("%Y-%m-%d") if mal.aired else None,
mal.rating, mal.score, mal.rank, mal.popularity, "|".join(mal.genres), expiration_date.strftime("%Y-%m-%d"), mal_id
mal.rating, mal.score, mal.rank, mal.popularity, "|".join(mal.genres), mal.studio, expiration_date.strftime("%Y-%m-%d"), mal_id
))
def query_tmdb_movie(self, tmdb_id, expiration):

View file

@ -44,6 +44,10 @@ mass_content_options = {
"omdb": "Use IMDb Rating through OMDb", "mdb": "Use MdbList Rating", "mdb_commonsense": "Use Commonsense Rating through MDbList",
"mdb_commonsense0": "Use Commonsense Rating with Zero Padding through MDbList", "mal": "Use MyAnimeList Rating"
}
mass_studio_options = {
"lock": "Unlock 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"
}
mass_original_title_options = {
"lock": "Unlock Original Title", "unlock": "Unlock Original Title", "remove": "Remove and Lock Original Title", "reset": "Remove and Unlock Original Title",
"anidb": "Use AniDB Main Title", "anidb_official": "Use AniDB Official Title based on the language attribute in the config file",
@ -90,7 +94,7 @@ 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": "bool", "sonarr_add_all_existing": "bool", "sonarr_remove_by_tag": "bool",
"mass_genre_update": mass_genre_options, "mass_content_rating_update": mass_content_options,
"mass_genre_update": mass_genre_options, "mass_content_rating_update": mass_content_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,

View file

@ -74,6 +74,7 @@ class Library(ABC):
self.ignore_imdb_ids = params["ignore_imdb_ids"]
self.assets_for_all = params["assets_for_all"]
self.delete_collections = params["delete_collections"]
self.mass_studio_update = params["mass_studio_update"]
self.mass_genre_update = params["mass_genre_update"]
self.mass_audience_rating_update = params["mass_audience_rating_update"]
self.mass_critic_rating_update = params["mass_critic_rating_update"]
@ -112,7 +113,7 @@ class Library(ABC):
or self.mass_audience_rating_update or self.mass_critic_rating_update or self.mass_user_rating_update \
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_original_title_update\
or self.mass_imdb_parental_labels or self.genre_mapper or self.content_rating_mapper \
or self.mass_imdb_parental_labels or self.genre_mapper or self.content_rating_mapper or self.mass_studio_update\
or self.radarr_add_all_existing or self.sonarr_add_all_existing or self.mass_poster_update or self.mass_background_update else False
self.library_operation = True if self.items_library_operation or self.delete_collections or self.mass_collection_mode \
or self.radarr_remove_by_tag or self.sonarr_remove_by_tag or self.show_unmanaged or self.show_unconfigured \

View file

@ -75,6 +75,7 @@ class MyAnimeListObj:
self.rank = self._data["rank"]
self.popularity = self._data["popularity"]
self.genres = [] if not self._data["genres"] else self._data["genres"].split("|") if cache else [g["name"] for g in self._data["genres"]]
self.studio = self._data["studio"] if cache else self._data["studios"][0]["name"] if self._data["studios"] else None
class MyAnimeList:

View file

@ -10,7 +10,7 @@ meta_operations = [
"mass_audience_rating_update", "mass_user_rating_update", "mass_critic_rating_update",
"mass_episode_audience_rating_update", "mass_episode_user_rating_update", "mass_episode_critic_rating_update",
"mass_genre_update", "mass_content_rating_update", "mass_originally_available_update", "mass_original_title_update",
"mass_poster_update", "mass_background_update"
"mass_poster_update", "mass_background_update", "mass_studio_update"
]
class Operations:
@ -425,6 +425,34 @@ class Operations:
except Failed:
pass
if self.library.mass_studio_update:
if self.library.mass_studio_update in ["remove", "reset"] and item.studio:
item.editField("studio", None, locked=self.library.mass_studio_update == "remove")
batch_display += f"\nStudio | None"
elif self.library.mass_studio_update in ["unlock", "reset"] and "studio" in locked_fields:
self.library.edit_query(item, {"originalTitle.locked": 0})
batch_display += f"\nStudio | Unlocked"
elif self.library.mass_studio_update in ["lock", "remove"] and "studio" not in locked_fields:
self.library.edit_query(item, {"studio.locked": 1})
batch_display += f"\nStudio | Locked"
elif self.library.mass_studio_update not in ["lock", "unlock", "remove", "reset"]:
try:
if anidb_item and self.library.mass_studio_update == "anidb":
new_studio = anidb_item.studio
elif mal_item and self.library.mass_studio_update == "mal":
new_studio = mal_item.studio
elif tmdb_item and self.library.mass_studio_update == "tmdb":
new_studio = tmdb_item.studio
else:
raise Failed
if not new_studio:
logger.info(f"No Studio Found")
elif str(item.studio) != str(new_studio):
item.editStudio(new_studio)
batch_display += f"\nStudio | {new_studio}"
except Failed:
pass
if self.library.mass_originally_available_update:
if self.library.mass_originally_available_update in ["remove", "reset"] and item.originallyAvailableAt:
item.editField("originallyAvailableAt", None, locked=self.library.mass_originally_available_update == "remove")