Skip to content

Commit aec4b67

Browse files
committed
Add Support for Blacklisting AniDB Tags in the Agent Options
also added: - a switch from tuple to set in the title_case function and ambiguous title check for a minor performance gain - backwards compatibility for 'rescan-recent import' on older versions of the v3 API - a check for empty values in the agent language prefs
1 parent 3346c29 commit aec4b67

4 files changed

Lines changed: 30 additions & 19 deletions

File tree

Contents/Code/__init__.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def Update(self, metadata, media, lang, force):
6666
series_titles['shoko'] = series_data['Name'] # Add Shoko's preferred series title to the dict
6767

6868
# Get Title according to the language preference
69-
for lang in [l.strip().lower() for l in Prefs['SeriesTitleLanguage'].split(',')]:
69+
for lang in [l.strip().lower() for l in Prefs['SeriesTitleLanguage'].split(',') if l.strip()]:
7070
title = try_get(series_titles, lang, None)
7171
if title: break
7272
if not title: title, lang = series_titles['shoko'], 'shoko (fallback)' # If not found, fallback to Shoko's preferred series title
@@ -88,7 +88,7 @@ def Update(self, metadata, media, lang, force):
8888
Log('Title %s %s [%s]' % (title_mod, title, lang.upper()))
8989

9090
# Get Alternate Title according to the language preference
91-
for lang in [l.strip().lower() for l in Prefs['SeriesAltTitleLanguage'].split(',')]:
91+
for lang in [l.strip().lower() for l in Prefs['SeriesAltTitleLanguage'].split(',') if l.strip()]:
9292
alt_title = try_get(series_titles, lang, None)
9393
if alt_title: break
9494

@@ -141,12 +141,15 @@ def Update(self, metadata, media, lang, force):
141141
metadata.genres.clear()
142142

143143
## Filter out weighted tags by the configured tag weight but leave ones weighted 0 as that means that they are unweighted (high priority) tags
144-
tags, c_rating, descriptor, descriptor_d, descriptor_s, descriptor_v = [], None, '', '', '', ''
144+
tags, c_tags, c_rating, descriptor, descriptor_d, descriptor_s, descriptor_v = [], set(), None, '', '', '', ''
145+
tag_blacklist = {t.strip().lower() for t in (Prefs['tagBlacklist'] or '').split(',') if t.strip()} # Prepare a set comprised of any blacklisted tags (lowercase)
146+
tag_c_ratings = {'kodomo', 'mina', 'shoujo', 'shounen', 'josei', 'seinen', 'borderline porn', '18 restricted'} # Prepare a set comprised of tags required for content ratings (lowercase)
145147
for tag in [t for t in series_tags if t['Source'] != 'User']: # Exclude custom user tags from any tag operations
146-
if (tag['Weight'] == 0 or tag['Weight'] >= int(Prefs['minimumTagWeight'])): tags.append(title_case(tag['Name'])) # Convert tags to title case and add them to the list
148+
indicator, weight = tag['Name'].lower(), tag['Weight']
149+
if (weight == 0 or weight >= int(Prefs['minimumTagWeight'])) and indicator not in tag_blacklist: tags.append(title_case(tag['Name'])) # Add title case tags to a list if they meet the minimum weight and aren't in the blacklist
147150
## Prep weight based content ratings (if enabled) using the content indicators described here: https://wiki.anidb.net/Categories:Content_Indicators
148151
if Prefs['contentRatings']:
149-
indicator, weight = tag['Name'].lower(), tag['Weight']
152+
if indicator in tag_c_ratings: c_tags.add(indicator) # If a tag is present that is required for content ratings add it to a set to avoid the tag blacklist interfering
150153
if indicator == 'nudity' or indicator == 'violence': # Raise ratings for the "Nudity" and "Violence" tags to TV-14 and then TV-MA if the weight exceeds 400 and 500 respectively
151154
if indicator == 'nudity': descriptor_s = 'S' # Apply the "Sexual Situations" descriptor for the "Nudity" tag
152155
if indicator == 'violence': descriptor_v = 'V' # Apply the "Violence" descriptor for the "Violence" tag
@@ -180,13 +183,13 @@ def Update(self, metadata, media, lang, force):
180183
## Uses the target audience tags on AniDB: https://anidb.net/tag/2606/animetb
181184
if Prefs['contentRatings']:
182185
if not c_rating: # If the rating wasn't already determined using the content indicators above take the lowest target audience rating
183-
if 'Kodomo' in tags : c_rating = 'TV-Y'
184-
elif 'Mina' in tags : c_rating = 'TV-G'
185-
elif 'Shoujo' in tags or 'Shounen' in tags : c_rating = 'TV-PG'
186-
elif 'Josei' in tags or 'Seinen' in tags : c_rating = 'TV-14'
187-
if 'Borderline Porn' in tags: c_rating = 'TV-MA' # Override any previous rating for borderline porn content
188-
if c_rating: c_rating += descriptor # Append the content descriptor using the content indicators above
189-
if '18 Restricted' in tags: c_rating = 'X' # Override any previous rating and remove content indicators for 18 restricted content
186+
if 'kodomo' in c_tags : c_rating = 'TV-Y'
187+
elif 'mina' in c_tags : c_rating = 'TV-G'
188+
elif 'shoujo' in c_tags or 'shounen' in c_tags : c_rating = 'TV-PG'
189+
elif 'josei' in c_tags or 'seinen' in c_tags : c_rating = 'TV-14'
190+
if 'borderline porn' in c_tags: c_rating = 'TV-MA' # Override any previous rating for borderline porn content
191+
if c_rating: c_rating += descriptor # Append the content descriptor using the content indicators above
192+
if '18 restricted' in c_tags: c_rating = 'X' # Override any previous rating and remove content indicators for 18 restricted content
190193

191194
metadata.content_rating = c_rating
192195
Log('Content Rating (Assumed): %s' % metadata.content_rating)
@@ -269,18 +272,18 @@ def Update(self, metadata, media, lang, force):
269272
ep_titles['shoko'] = ep_data['Name'] # Add Shoko's preferred episode title to the dict
270273

271274
# Get episode title according to the language preference
272-
ep_title_mod, tmdb_ep_title = '[LANG]: ', try_get(tmdb_ep_data, 'Title', None)
273-
for lang in [l.strip().lower() for l in Prefs['EpisodeTitleLanguage'].split(',')]:
275+
ep_title_mod, tmdb_ep_title, ep_title_lang = '[LANG]: ', try_get(tmdb_ep_data, 'Title', None), [l.strip().lower() for l in Prefs['EpisodeTitleLanguage'].split(',') if l.strip()]
276+
for lang in ep_title_lang:
274277
ep_title = try_get(ep_titles, lang, None)
275278
if ep_title: break
276279
if not ep_title: ep_title, lang = ep_titles['shoko'], 'shoko (fallback)' # If not found, fallback to Shoko's preferred episode title
277280
if Prefs['tmdbEpGroupNames'] and tmdb_ep_group > 1 and tmdb_ep_title: ep_title, lang = tmdb_ep_title, 'shoko (TMDB Ep Group)' # If TMDB episode group names are enabled and a group is present override the title
278281

279282
# Replace ambiguous single entry titles with the series title
280-
if ep_title in ('Complete Movie', 'Music Video', 'OAD', 'OVA', 'Short Movie', 'Special', 'TV Special', 'Web') and ep_data['AniDB']['EpisodeNumber'] == 1:
283+
if ep_title in {'Complete Movie', 'Music Video', 'OAD', 'OVA', 'Short Movie', 'Special', 'TV Special', 'Web'} and ep_data['AniDB']['EpisodeNumber'] == 1:
281284
# Get series title according to the language preference
282285
ep_title_mod, original_title = '(FromSeries) [LANG]: ', ep_title
283-
for lang in [l.strip().lower() for l in Prefs['EpisodeTitleLanguage'].split(',')]:
286+
for lang in ep_title_lang:
284287
if lang != 'shoko': ep_title = try_get(series_titles, lang, ep_title) # Exclude "shoko" as it will return the preferred language for series and not episodes
285288
if ep_title != original_title: break
286289
if ep_title == original_title and tmdb_ep_title: ep_title_mod, ep_title = '(TMDB) [LANG]: ', tmdb_ep_title # Fallback to the TMDB title if there is a TMDB Episodes match
@@ -398,9 +401,9 @@ def summary_sanitizer(summary):
398401

399402
def title_case(text):
400403
# Words to force lowercase in tags to follow AniDB capitalisation rules: https://wiki.anidb.net/Capitalisation (some romaji tag endings and separator words are also included)
401-
force_lower = ('a', 'an', 'the', 'and', 'but', 'or', 'nor', 'at', 'by', 'for', 'from', 'in', 'into', 'of', 'off', 'on', 'onto', 'out', 'over', 'per', 'to', 'up', 'with', 'as', '4-koma', '-hime', '-kei', '-kousai', '-sama', '-warashi', 'no', 'vs', 'x')
404+
force_lower = {'a', 'an', 'the', 'and', 'but', 'or', 'nor', 'at', 'by', 'for', 'from', 'in', 'into', 'of', 'off', 'on', 'onto', 'out', 'over', 'per', 'to', 'up', 'with', 'as', '4-koma', '-hime', '-kei', '-kousai', '-sama', '-warashi', 'no', 'vs', 'x'}
402405
# Abbreviations or acronyms that should be fully capitalised
403-
force_upper = ('3d', 'bdsm', 'cg', 'cgi', 'ed', 'fff', 'ffm', 'ii', 'milf', 'mmf', 'mmm', 'npc', 'op', 'rpg', 'tbs', 'tv')
406+
force_upper = {'3d', 'bdsm', 'cg', 'cgi', 'ed', 'fff', 'ffm', 'ii', 'milf', 'mmf', 'mmm', 'npc', 'op', 'rpg', 'tbs', 'tv'}
404407
# Special cases where a specific capitalisation style is preferred
405408
force_special = {'comicfesta': 'ComicFesta', 'd\'etat': 'd\'Etat', 'noitamina': 'noitaminA'}
406409
text = re.sub(r'[\'\w\d]+\b', lambda t: t.group(0).capitalize(), text) # Capitalise all words accounting for apostrophes

Contents/DefaultPrefs.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@
5757
"default": "SHOKO, EN, X-JAT"
5858
},
5959

60+
{
61+
"id": "tagBlacklist",
62+
"label": "AniDB tags to exclude from the genre list, separated by a comma.",
63+
"type": "text",
64+
"default": ""
65+
},
66+
6067
{
6168
"id": "minimumTagWeight",
6269
"label": "The minimum weight for AniDB tags to be applied",

Contents/Scripts/rescan-recent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def arg_parse(arg):
4747
try:
4848
import_folders = requests.get(f"http://{cfg.Shoko['Hostname']}:{cfg.Shoko['Port']}/api/v3/ImportFolder?apikey={shoko_key}").json()
4949
for folder in import_folders:
50-
if folder['DropFolderType'] == 'Source':
50+
if folder['DropFolderType'] == 'Source' or folder['DropFolderType'] == 1: # older versions of Shoko denote Source folders as '1'
5151
requests.get(f"http://{cfg.Shoko['Hostname']}:{cfg.Shoko['Port']}/api/v3/ImportFolder/{folder['ID']}/Scan?apikey={shoko_key}")
5252
print(f"│├─Scanning: {folder['Name']}")
5353
except Exception as error:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Enable the following options in Shoko to ensure that Plex has at least one sourc
6161
- Will move common series title prefixes like "Gekijouban", "Eiga" etc. to the end of the title
6262
- Removes the original tag hiding options and replaces them with a tag weight system similar to what [HAMA](https://github.com/ZeroQI/Hama.bundle) uses
6363
- **Note:** Automatically ignores all tags from Shoko's [TagBlacklistAniDBHelpers](https://github.com/ShokoAnime/ShokoServer/blob/9c0ae9208479420dea3b766156435d364794e809/Shoko.Server/Utilities/TagFilter.cs#L37) list
64+
- Supports manually excluding any unwanted AniDB tags
6465
- Series and movies will list the Studio as Animation Work (アニメーション制作) or Work (制作)
6566
- Support for:
6667
- Files which contain more than one episode or episodes which span multiple files

0 commit comments

Comments
 (0)