260 lines
8.0 KiB
Python
260 lines
8.0 KiB
Python
import re
|
||
|
||
from jinja2 import Environment, Undefined
|
||
from .constants import DEFAULT_OUTPUT_FILENAME_TEMPLATE
|
||
from .configuration_controller import ConfigurationController
|
||
from .logging_utils import get_ffx_logger
|
||
from .show_descriptor import ShowDescriptor
|
||
|
||
|
||
class EmptyStringUndefined(Undefined):
|
||
def __str__(self):
|
||
return ''
|
||
|
||
|
||
DIFF_ADDED_KEY = 'added'
|
||
DIFF_REMOVED_KEY = 'removed'
|
||
DIFF_CHANGED_KEY = 'changed'
|
||
DIFF_UNCHANGED_KEY = 'unchanged'
|
||
|
||
FILENAME_FILTER_TRANSLATION = str.maketrans(
|
||
{
|
||
"/": "-",
|
||
":": ";",
|
||
"*": "",
|
||
"'": "",
|
||
"?": "#",
|
||
"♥": "",
|
||
"’": "",
|
||
}
|
||
)
|
||
TMDB_FILLER_MARKERS = (" (*)", "(*)")
|
||
TMDB_EPISODE_RANGE_SUFFIX_REGEX = re.compile(r"\(([0-9]+)[-/]([0-9]+)\)$")
|
||
TMDB_EPISODE_PART_SUFFIX_REGEX = re.compile(r"\(([0-9]+)\)$")
|
||
RICH_COLOR_REGEX = re.compile(r"\[[a-z_]+\](.+)\[/[a-z_]+\]")
|
||
|
||
|
||
def dictDiff(a : dict, b : dict, ignoreKeys: list = [], removeKeys: list = []):
|
||
"""
|
||
ignoreKeys: Ignored keys are filtered from calculating diff at all
|
||
removeKeys: Override diff calculation to remove keys certainly
|
||
"""
|
||
|
||
a_filtered = {k:v for k,v in a.items() if not k in ignoreKeys}
|
||
b_filtered = {k:v for k,v in b.items() if not k in ignoreKeys and k not in removeKeys}
|
||
|
||
a_only = {k:v for k,v in a_filtered.items() if not k in b_filtered.keys()}
|
||
b_only = {k:v for k,v in b_filtered.items() if not k in a_filtered.keys()}
|
||
|
||
a_b = set(a_filtered.keys()) & set(b_filtered.keys())
|
||
|
||
changed = {k:b_filtered[k] for k in a_b if a_filtered[k] != b_filtered[k]}
|
||
unchanged = {k:b_filtered[k] for k in a_b if a_filtered[k] == b_filtered[k]}
|
||
|
||
diffResult = {}
|
||
|
||
|
||
if a_only:
|
||
diffResult[DIFF_REMOVED_KEY] = a_only
|
||
diffResult[DIFF_UNCHANGED_KEY] = unchanged
|
||
if b_only:
|
||
diffResult[DIFF_ADDED_KEY] = b_only
|
||
if changed:
|
||
diffResult[DIFF_CHANGED_KEY] = changed
|
||
|
||
return diffResult
|
||
|
||
|
||
def dictKeysDiff(a : dict, b : dict):
|
||
|
||
a_keys = set(a.keys())
|
||
b_keys = set(b.keys())
|
||
|
||
a_only = a_keys - b_keys
|
||
b_only = b_keys - a_keys
|
||
a_b = a_keys & b_keys
|
||
|
||
changed = {k for k in a_b if a[k] != b[k]}
|
||
|
||
diffResult = {}
|
||
|
||
|
||
if a_only:
|
||
diffResult[DIFF_REMOVED_KEY] = a_only
|
||
diffResult[DIFF_UNCHANGED_KEY] = b_keys
|
||
if b_only:
|
||
diffResult[DIFF_ADDED_KEY] = b_only
|
||
if changed:
|
||
diffResult[DIFF_CHANGED_KEY] = changed
|
||
|
||
return diffResult
|
||
|
||
|
||
def dictCache(element: dict, cache: list = []):
|
||
for index in range(len(cache)):
|
||
diff = dictKeysDiff(cache[index], element)
|
||
if not diff:
|
||
return index, cache
|
||
cache.append(element)
|
||
return -1, cache
|
||
|
||
|
||
def setDiff(a : set, b : set) -> set:
|
||
|
||
a_only = a - b
|
||
b_only = b - a
|
||
a_and_b = a & b
|
||
|
||
diffResult = {}
|
||
|
||
if a_only:
|
||
diffResult[DIFF_REMOVED_KEY] = a_only
|
||
diffResult[DIFF_UNCHANGED_KEY] = a_and_b
|
||
if b_only:
|
||
diffResult[DIFF_ADDED_KEY] = b_only
|
||
|
||
return diffResult
|
||
|
||
|
||
def permutateList(inputList: list, permutation: list):
|
||
|
||
# 0,1,2: ABC
|
||
# 0,2,1: ACB
|
||
# 1,2,0: BCA
|
||
|
||
pass
|
||
|
||
|
||
|
||
def filterFilename(fileName: str) -> str:
|
||
"""This filter replaces charactes from TMDB responses with characters
|
||
less problemating when using in filenames or removes them"""
|
||
|
||
return str(fileName).translate(FILENAME_FILTER_TRANSLATION).strip()
|
||
|
||
def substituteTmdbFilename(fileName: str) -> str:
|
||
"""If chaining this method with filterFilename use this one first as the latter will destroy some patterns"""
|
||
|
||
normalizedFileName = str(fileName)
|
||
|
||
for fillerMarker in TMDB_FILLER_MARKERS:
|
||
normalizedFileName = normalizedFileName.replace(fillerMarker, '')
|
||
|
||
episodeRangeMatch = TMDB_EPISODE_RANGE_SUFFIX_REGEX.search(normalizedFileName)
|
||
if episodeRangeMatch is not None:
|
||
partFirstIndex, partLastIndex = episodeRangeMatch.groups()
|
||
return TMDB_EPISODE_RANGE_SUFFIX_REGEX.sub(
|
||
f"Teil {partFirstIndex}-{partLastIndex}",
|
||
normalizedFileName,
|
||
count=1,
|
||
)
|
||
|
||
episodePartMatch = TMDB_EPISODE_PART_SUFFIX_REGEX.search(normalizedFileName)
|
||
if episodePartMatch is not None:
|
||
partIndex = episodePartMatch.group(1)
|
||
return TMDB_EPISODE_PART_SUFFIX_REGEX.sub(
|
||
f"Teil {partIndex}",
|
||
normalizedFileName,
|
||
count=1,
|
||
)
|
||
|
||
return normalizedFileName
|
||
|
||
|
||
def getEpisodeFileBasename(showName,
|
||
episodeName,
|
||
season,
|
||
episode,
|
||
indexSeasonDigits = None,
|
||
indexEpisodeDigits = None,
|
||
indicatorSeasonDigits = None,
|
||
indicatorEpisodeDigits = None,
|
||
context = None):
|
||
"""
|
||
One Piece:
|
||
indexSeasonDigits = 0,
|
||
indexEpisodeDigits = 4,
|
||
indicatorSeasonDigits = 2,
|
||
indicatorEpisodeDigits = 4
|
||
|
||
Three-Body:
|
||
indexSeasonDigits = 0,
|
||
indexEpisodeDigits = 2,
|
||
indicatorSeasonDigits = 2,
|
||
indicatorEpisodeDigits = 2
|
||
|
||
Dragonball:
|
||
indexSeasonDigits = 0,
|
||
indexEpisodeDigits = 3,
|
||
indicatorSeasonDigits = 2,
|
||
indicatorEpisodeDigits = 3
|
||
|
||
Boruto:
|
||
indexSeasonDigits = 0,
|
||
indexEpisodeDigits = 4,
|
||
indicatorSeasonDigits = 2,
|
||
indicatorEpisodeDigits = 4
|
||
"""
|
||
|
||
cc: ConfigurationController = context['config'] if context is not None and 'config' in context.keys() else None
|
||
configData = cc.getData() if cc is not None else {}
|
||
outputFilenameTemplate = configData.get(ConfigurationController.OUTPUT_FILENAME_TEMPLATE_KEY,
|
||
DEFAULT_OUTPUT_FILENAME_TEMPLATE)
|
||
defaultDigitLengths = ShowDescriptor.getDefaultDigitLengths(context)
|
||
|
||
if indexSeasonDigits is None:
|
||
indexSeasonDigits = defaultDigitLengths[ShowDescriptor.INDEX_SEASON_DIGITS_KEY]
|
||
if indexEpisodeDigits is None:
|
||
indexEpisodeDigits = defaultDigitLengths[ShowDescriptor.INDEX_EPISODE_DIGITS_KEY]
|
||
if indicatorSeasonDigits is None:
|
||
indicatorSeasonDigits = defaultDigitLengths[ShowDescriptor.INDICATOR_SEASON_DIGITS_KEY]
|
||
if indicatorEpisodeDigits is None:
|
||
indicatorEpisodeDigits = defaultDigitLengths[ShowDescriptor.INDICATOR_EPISODE_DIGITS_KEY]
|
||
|
||
if context is not None and 'logger' in context.keys():
|
||
logger = context['logger']
|
||
else:
|
||
logger = get_ffx_logger()
|
||
|
||
|
||
indexSeparator = ' ' if indexSeasonDigits or indexEpisodeDigits else ''
|
||
seasonIndex = '{num:{fill}{width}}'.format(num=season, fill='0', width=indexSeasonDigits) if indexSeasonDigits else ''
|
||
episodeIndex = '{num:{fill}{width}}'.format(num=episode, fill='0', width=indexEpisodeDigits) if indexEpisodeDigits else ''
|
||
|
||
indicatorSeparator = ' - ' if indicatorSeasonDigits or indicatorEpisodeDigits else ''
|
||
seasonIndicator = 'S{num:{fill}{width}}'.format(num=season, fill='0', width=indicatorSeasonDigits) if indicatorSeasonDigits else ''
|
||
episodeIndicator = 'E{num:{fill}{width}}'.format(num=episode, fill='0', width=indicatorEpisodeDigits) if indicatorEpisodeDigits else ''
|
||
|
||
jinjaKwargs = {
|
||
'ffx_show_name': showName,
|
||
'ffx_index_separator': indexSeparator,
|
||
'ffx_season_index': str(seasonIndex),
|
||
'ffx_episode_index': str(episodeIndex),
|
||
'ffx_index': str(seasonIndex) + str(episodeIndex),
|
||
'ffx_episode_name': episodeName,
|
||
'ffx_indicator_separator': indicatorSeparator,
|
||
'ffx_season_indicator': str(seasonIndicator),
|
||
'ffx_episode_indicator': str(episodeIndicator),
|
||
'ffx_indicator': str(seasonIndicator) + str(episodeIndicator)
|
||
}
|
||
|
||
jinjaEnv = Environment(undefined=EmptyStringUndefined)
|
||
jinjaTemplate = jinjaEnv.from_string(outputFilenameTemplate)
|
||
return jinjaTemplate.render(**jinjaKwargs)
|
||
|
||
# return ''.join(filenameTokens)
|
||
|
||
|
||
def formatRichColor(text: str, color: str = None):
|
||
if color is None:
|
||
return text
|
||
else:
|
||
return f"[{color}]{text}[/{color}]"
|
||
|
||
def removeRichColor(text: str):
|
||
richColorMatch = RICH_COLOR_REGEX.search(str(text))
|
||
if richColorMatch is None:
|
||
return text
|
||
else:
|
||
return str(richColorMatch.group(1))
|