From 346661b9d7d68341e5cf0b780d2febda8edcc377 Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Sun, 9 Jul 2023 13:25:20 -0400 Subject: [PATCH 01/13] Various improvements for TitleColorMatch --- azuravian/TitleColorMatch.py | 54 +++++++++++++++++------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index bf63110..79d3e00 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -12,7 +12,7 @@ class TitleColorMatch(BaseCardType): """ - This class describes a type of CardType created by azuravian, and is + This class describes a type of CardType created by azuravian, and is a modification of Beedman's GradientLogoTitleCard class with a few changes, specifically the ability to autoselect a font color that matches the logo, as well as trimming the logo of any extra @@ -38,6 +38,9 @@ class CardModel(BaseCardTypeCustomFontAllText): TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) TITLE_COLOR = '#EBEBEB' + """Logos with luminance lower than this percentage will have their colors inverted""" + TITLE_MIN_LUMINANCE = 10 + """Default characters to replace in the generic font""" FONT_REPLACEMENTS = { '[': '(', ']': ')', '(': '[', ')': ']', '―': '-', '…': '...' @@ -64,7 +67,7 @@ class CardModel(BaseCardTypeCustomFontAllText): 'source_file', 'output_file', 'title_text', 'season_text', 'episode_text', 'hide_season_text', 'font_color', 'font_file', 'font_interline_spacing', 'font_kerning', 'font_size', - 'font_stroke_width', 'font_vertical_shift', 'logo', + 'font_stroke_width', 'font_vertical_shift', 'logo', ) def __init__(self, @@ -89,7 +92,7 @@ def __init__(self, """ Construct a new instance of this Card. """ - + # Initialize the parent class - this sets up an ImageMagickInterface super().__init__(blur, grayscale, preferences=preferences) @@ -102,7 +105,7 @@ def __init__(self, self.season_text = self.image_magick.escape_chars(season_text.upper()) self.episode_text = self.image_magick.escape_chars(episode_text.upper()) self.hide_season_text = hide_season_text - + self.font_color = font_color self.font_file = font_file self.font_interline_spacing = font_interline_spacing @@ -112,8 +115,7 @@ def __init__(self, self.font_vertical_shift = font_vertical_shift - @property - def logo_command(self) -> ImageMagickCommands: + def logo_command(self, luminance) -> ImageMagickCommands: """ Get the ImageMagick commands to add the resized logo to the source image. @@ -128,18 +130,20 @@ def logo_command(self) -> ImageMagickCommands: f'-trim', f'+repage', f'-resize x650', - f'-resize 1155x650\> \)', + f'-resize 1155x650\>', + # Recolor dark logos to be visible on the black gradient + f'-channel RGB' if luminance < self.TITLE_MIN_LUMINANCE else '', + (f'-negate' if luminance < self.TITLE_MIN_LUMINANCE else '') + ' \)', # Overlay resized logo f'-gravity northwest', f'-define colorspace:auto-grayscale=false', - f'-type TrueColorAlpha', + f'-type TrueColorAlpha', f'-geometry "+50+50"', f'-composite', ] - @property - def title_text_command(self) -> ImageMagickCommands: + def title_text_command(self, title_color, stroke_color) -> ImageMagickCommands: """ ImageMagick commands to implement the title text's global effects. Specifically the the font, kerning, fontsize, and @@ -149,9 +153,6 @@ def title_text_command(self) -> ImageMagickCommands: List of ImageMagick commands. """ - # Get the title color and stroke for this logo - title_color, stroke_color = self._get_logo_color() - font_size = 157.41 * self.font_size interline_spacing = -22 + self.font_interline_spacing kerning = -1.25 * self.font_kerning @@ -174,18 +175,18 @@ def title_text_command(self) -> ImageMagickCommands: ] - def _get_logo_color(self) -> tuple[str, str]: + def _get_logo_color(self) -> tuple[str, str, int]: """ Get the logo color for this card's logo. Returns: - Tuple whose values are the title color text and the stroke - width color. + Tuple whose values are the title color text, the stroke + width color and luminance (or 100 where not applicable). """ # If auto color wasn't indicated use indicated color and black stroke if str(self.font_color) != 'auto': - return self.font_color, 'black' + return self.font_color, 'black', 100 # Command to get histogram of the colors in logo image command = ' '.join([ @@ -225,19 +226,15 @@ def _get_logo_color(self) -> tuple[str, str]: color_ = hexcolor.lstrip('#') lv = len(color_) r, g, b = (int(color_[i:i+lv//3], 16) for i in range(0, lv, lv//3)) - - # Skip values that are too dark/light - if min(r, g, b) > 240 or max(r, g, b) < 15: - continue # First valid color, return color and stroke based on luminance luminance = (r * 0.299) + (g * 0.587) + (b * 0.114) - return hexcolor, 'black' if luminance > 50 else 'white' + return hexcolor if luminance >= self.TITLE_MIN_LUMINANCE else self.TITLE_COLOR, 'black' if luminance < self.TITLE_MIN_LUMINANCE or luminance > 50 else 'white', luminance # No valid colors identified, return defaults - return self.TITLE_COLOR, 'black' + return self.TITLE_COLOR, 'black', 100 + - @property def index_text_command(self) -> ImageMagickCommands: """ @@ -302,7 +299,7 @@ def is_custom_font(font: 'Font') -> bool: """ Determines whether the given arguments represent a custom font for this card. - + Args: font: The Font being evaluated. @@ -347,6 +344,7 @@ def create(self) -> None: object's defined title card. """ + title_color, stroke_color, luminance = self._get_logo_color() command = ' '.join([ f'convert', # Resize source image @@ -356,9 +354,9 @@ def create(self) -> None: f'"{self.__GRADIENT_IMAGE}"', f'-composite', # Overlay resized logo - *self.logo_command, + *self.logo_command(luminance), # Put title text - *self.title_text_command, + *self.title_text_command(title_color, stroke_color), # Put season/episode text *self.index_text_command, # Create and resize output @@ -366,4 +364,4 @@ def create(self) -> None: f'"{self.output_file.resolve()}"', ]) - self.image_magick.run(command) \ No newline at end of file + self.image_magick.run(command) From 22e99ef2d65f1a7147f93980589f48cb97138537 Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Sun, 9 Jul 2023 13:04:59 -0600 Subject: [PATCH 02/13] Update docstrings, add annotations, fix line length --- azuravian/TitleColorMatch.py | 43 ++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 79d3e00..dc42f44 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -38,7 +38,7 @@ class CardModel(BaseCardTypeCustomFontAllText): TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) TITLE_COLOR = '#EBEBEB' - """Logos with luminance lower than this percentage will have their colors inverted""" + """Threshold (%) undet which logos will have their colors inverted""" TITLE_MIN_LUMINANCE = 10 """Default characters to replace in the generic font""" @@ -88,7 +88,8 @@ def __init__(self, blur: bool = False, grayscale: bool = False, preferences: Optional['Preferences'] = None, - **unused) -> None: + **unused + ) -> None: """ Construct a new instance of this Card. """ @@ -115,7 +116,7 @@ def __init__(self, self.font_vertical_shift = font_vertical_shift - def logo_command(self, luminance) -> ImageMagickCommands: + def logo_command(self, luminance: int) -> ImageMagickCommands: """ Get the ImageMagick commands to add the resized logo to the source image. @@ -124,6 +125,13 @@ def logo_command(self, luminance) -> ImageMagickCommands: List of ImageMagick commands. """ + negate_commands = [] + if luminance < self.TITLE_MIN_LUMINANCE: + negate_commands = [ + f'-channel RGB', + f'-negate', + ] + return [ # Resize logo f'\( "{self.logo.resolve()}"', @@ -132,10 +140,9 @@ def logo_command(self, luminance) -> ImageMagickCommands: f'-resize x650', f'-resize 1155x650\>', # Recolor dark logos to be visible on the black gradient - f'-channel RGB' if luminance < self.TITLE_MIN_LUMINANCE else '', - (f'-negate' if luminance < self.TITLE_MIN_LUMINANCE else '') + ' \)', + *negate_commands, # Overlay resized logo - f'-gravity northwest', + f'\) -gravity northwest', f'-define colorspace:auto-grayscale=false', f'-type TrueColorAlpha', f'-geometry "+50+50"', @@ -143,12 +150,19 @@ def logo_command(self, luminance) -> ImageMagickCommands: ] - def title_text_command(self, title_color, stroke_color) -> ImageMagickCommands: + def title_text_command(self, + title_color: str, + stroke_color: str, + ) -> ImageMagickCommands: """ ImageMagick commands to implement the title text's global effects. Specifically the the font, kerning, fontsize, and center gravity. + Args: + title_color: Color to utilize for the title text. + stroke_color: Color to utilize for the stroke. + Returns: List of ImageMagick commands. """ @@ -229,7 +243,16 @@ def _get_logo_color(self) -> tuple[str, str, int]: # First valid color, return color and stroke based on luminance luminance = (r * 0.299) + (g * 0.587) + (b * 0.114) - return hexcolor if luminance >= self.TITLE_MIN_LUMINANCE else self.TITLE_COLOR, 'black' if luminance < self.TITLE_MIN_LUMINANCE or luminance > 50 else 'white', luminance + + # Determine returns + if luminance >= self.TITLE_MIN_LUMINANCE: + title_color = hexcolor + stroke_color = 'black' if luminance > 50 else 'white' + else: + title_color = self.TITLE_COLOR + stroke_color = 'black' + + return title_color, stroke_color, luminance # No valid colors identified, return defaults return self.TITLE_COLOR, 'black', 100 @@ -319,7 +342,9 @@ def is_custom_font(font: 'Font') -> bool: @staticmethod def is_custom_season_titles( - custom_episode_map: bool, episode_text_format: str) -> bool: + custom_episode_map: bool, + episode_text_format: str, + ) -> bool: """ Determines whether the given attributes constitute custom or generic season titles. From 7a9805adaf12719e6637fdd4c80e8c2565e60d7b Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Sun, 9 Jul 2023 15:24:33 -0400 Subject: [PATCH 03/13] fix typo --- azuravian/TitleColorMatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index dc42f44..850d071 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -38,7 +38,7 @@ class CardModel(BaseCardTypeCustomFontAllText): TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) TITLE_COLOR = '#EBEBEB' - """Threshold (%) undet which logos will have their colors inverted""" + """Threshold (%) under which logos will have their colors inverted""" TITLE_MIN_LUMINANCE = 10 """Default characters to replace in the generic font""" From cc54dad8b0ee0caff000fd6a573ba84e66e9a22b Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Sun, 9 Jul 2023 19:44:30 -0400 Subject: [PATCH 04/13] Add some new variables and improve some stuff --- azuravian/TitleColorMatch.py | 39 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 850d071..757ec3e 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -34,12 +34,19 @@ class CardModel(BaseCardTypeCustomFontAllText): 'top_heavy': False, # This class uses bottom heavy titling } - """Default font and text color for episode title text""" + """Default font, text and stroke color for episode title text""" TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) TITLE_COLOR = '#EBEBEB' + STROKE_COLOR = 'black' - """Threshold (%) under which logos will have their colors inverted""" - TITLE_MIN_LUMINANCE = 10 + """Threshold under which logos will have their colors inverted (if enabled) and text will use TITLE_COLOR from above (0 being black and 255 being pure white)""" + TITLE_MIN_LUMINANCE = 50 + + """When enabled, any color with a significant presence may be checked for luminance. When disabled, only the most common color is checked.""" + CHECK_MULTIPLE_LUMINANCES = True + + """When enabled, logos will have their colors inverted when they are darker than the above threshold""" + INVERT_LOGOS = True """Default characters to replace in the generic font""" FONT_REPLACEMENTS = { @@ -126,7 +133,7 @@ def logo_command(self, luminance: int) -> ImageMagickCommands: """ negate_commands = [] - if luminance < self.TITLE_MIN_LUMINANCE: + if self.INVERT_LOGOS and luminance < self.TITLE_MIN_LUMINANCE: negate_commands = [ f'-channel RGB', f'-negate', @@ -195,12 +202,12 @@ def _get_logo_color(self) -> tuple[str, str, int]: Returns: Tuple whose values are the title color text, the stroke - width color and luminance (or 100 where not applicable). + width color and luminance """ # If auto color wasn't indicated use indicated color and black stroke if str(self.font_color) != 'auto': - return self.font_color, 'black', 100 + return self.font_color, 'black', 255 # Command to get histogram of the colors in logo image command = ' '.join([ @@ -243,19 +250,19 @@ def _get_logo_color(self) -> tuple[str, str, int]: # First valid color, return color and stroke based on luminance luminance = (r * 0.299) + (g * 0.587) + (b * 0.114) + log.debug(f'Luminance for {self.logo} ({r}, {g}, {b}) is {luminance}') - # Determine returns + # If luminance is sufficient return right away, otherwise check the next most common colors if luminance >= self.TITLE_MIN_LUMINANCE: title_color = hexcolor - stroke_color = 'black' if luminance > 50 else 'white' - else: - title_color = self.TITLE_COLOR - stroke_color = 'black' - - return title_color, stroke_color, luminance - - # No valid colors identified, return defaults - return self.TITLE_COLOR, 'black', 100 + stroke_color = 'black' if luminance > 100 else 'white' + return title_color, stroke_color, luminance + # Only check the first luminance when set to do so + else if not self.CHECK_MULTIPLE_LUMINANCES: + break + + # None of the most common colors had sufficient luminance, return defaults + return self.TITLE_COLOR, self.STROKE_COLOR, -1 @property From 4e24352ff9710811a7aa89a24fc5fcf9dc0262d4 Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Sun, 9 Jul 2023 20:03:56 -0400 Subject: [PATCH 05/13] Whoops --- azuravian/TitleColorMatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 757ec3e..9befcf4 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -258,7 +258,7 @@ def _get_logo_color(self) -> tuple[str, str, int]: stroke_color = 'black' if luminance > 100 else 'white' return title_color, stroke_color, luminance # Only check the first luminance when set to do so - else if not self.CHECK_MULTIPLE_LUMINANCES: + elif not self.CHECK_MULTIPLE_LUMINANCES: break # None of the most common colors had sufficient luminance, return defaults From 39475f49b2af84326be26b06609e39de962a5cd1 Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Wed, 12 Jul 2023 09:16:43 -0400 Subject: [PATCH 06/13] Add adjustable settings --- azuravian/TitleColorMatch.py | 69 +++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 9befcf4..51d68ec 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -16,17 +16,35 @@ class TitleColorMatch(BaseCardType): a modification of Beedman's GradientLogoTitleCard class with a few changes, specifically the ability to autoselect a font color that matches the logo, as well as trimming the logo of any extra - transparent space that makes its location incorrect. + transparent space that makes its location incorrect. """ class CardModel(BaseCardTypeCustomFontAllText): logo_file: FilePath font_color: Union[BetterColor, Literal['auto']] = Field(default='#EBEBEB') font_file: FilePath + '''Threshold under which logos will have their colors inverted (if enabled) and text will use default_title_stroke_color (0 being black and 255 being pure white)''' + title_min_luminance: int = Field(default=50) + '''When enabled, any color with a significant presence may be checked for luminance. When disabled, only the most common color is checked''' + check_multiple_luminances: bool = Field(default=True) + '''When enabled, logos will have their colors inverted when they are darker than the specified title_min_luminance''' + invert_logos: bool = Field(default=True) + '''Sets the title color when min luminance is not reached''' + default_title_color: BetterColor = Field(default='#EBEBEB') + '''Sets the title stroke color when min luminance is not reached''' + default_title_stroke_color: BetterColor = Field(default='black') + '''Whether or not to enable the gradient background being drawn''' + enable_gradient: bool = Field(default=True) + # TODO: The gradient background should be changeable by the user, setting to "none" should disable, would replace the setting above + # TODO: The fonts for season and episode count should be changeable and so should their colors? """Directory where all reference files used by this card are stored""" REF_DIRECTORY = Path(__file__).parent.parent / 'ref' + """Default font and color for episode title text""" + TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) + TITLE_COLOR = '#EBEBEB' + """Characteristics for title splitting by this class""" TITLE_CHARACTERISTICS = { 'max_line_width': 32, # Character count to begin splitting titles @@ -34,20 +52,6 @@ class CardModel(BaseCardTypeCustomFontAllText): 'top_heavy': False, # This class uses bottom heavy titling } - """Default font, text and stroke color for episode title text""" - TITLE_FONT = str((REF_DIRECTORY / 'Sequel-Neue.otf').resolve()) - TITLE_COLOR = '#EBEBEB' - STROKE_COLOR = 'black' - - """Threshold under which logos will have their colors inverted (if enabled) and text will use TITLE_COLOR from above (0 being black and 255 being pure white)""" - TITLE_MIN_LUMINANCE = 50 - - """When enabled, any color with a significant presence may be checked for luminance. When disabled, only the most common color is checked.""" - CHECK_MULTIPLE_LUMINANCES = True - - """When enabled, logos will have their colors inverted when they are darker than the above threshold""" - INVERT_LOGOS = True - """Default characters to replace in the generic font""" FONT_REPLACEMENTS = { '[': '(', ']': ')', '(': '[', ')': ']', '―': '-', '…': '...' @@ -75,6 +79,9 @@ class CardModel(BaseCardTypeCustomFontAllText): 'episode_text', 'hide_season_text', 'font_color', 'font_file', 'font_interline_spacing', 'font_kerning', 'font_size', 'font_stroke_width', 'font_vertical_shift', 'logo', + 'title_min_luminance', 'check_multiple_luminances', + 'invert_logos', 'default_title_color', + 'default_title_stroke_color', 'enable_gradient' ) def __init__(self, @@ -95,6 +102,12 @@ def __init__(self, blur: bool = False, grayscale: bool = False, preferences: Optional['Preferences'] = None, + title_min_luminance: int = 50, + check_multiple_luminances: bool = True, + invert_logos: bool = True, + default_title_color: str = TITLE_COLOR, + default_title_stroke_color: str = 'black', + enable_gradient: bool = True, **unused ) -> None: """ @@ -122,6 +135,12 @@ def __init__(self, self.font_stroke_width = font_stroke_width self.font_vertical_shift = font_vertical_shift + self.title_min_luminance = title_min_luminance + self.check_multiple_luminances = check_multiple_luminances + self.invert_logos = invert_logos + self.default_title_color = default_title_color + self.default_title_stroke_color = default_title_stroke_color + self.enable_gradient = enable_gradient def logo_command(self, luminance: int) -> ImageMagickCommands: """ @@ -133,10 +152,11 @@ def logo_command(self, luminance: int) -> ImageMagickCommands: """ negate_commands = [] - if self.INVERT_LOGOS and luminance < self.TITLE_MIN_LUMINANCE: + if self.invert_logos and luminance < self.title_min_luminance: negate_commands = [ - f'-channel RGB', - f'-negate', + '-channel RGB', + '-negate', + '-colorspace Gray' ] return [ @@ -253,16 +273,16 @@ def _get_logo_color(self) -> tuple[str, str, int]: log.debug(f'Luminance for {self.logo} ({r}, {g}, {b}) is {luminance}') # If luminance is sufficient return right away, otherwise check the next most common colors - if luminance >= self.TITLE_MIN_LUMINANCE: + if luminance >= self.title_min_luminance: title_color = hexcolor stroke_color = 'black' if luminance > 100 else 'white' return title_color, stroke_color, luminance # Only check the first luminance when set to do so - elif not self.CHECK_MULTIPLE_LUMINANCES: + elif not self.check_multiple_luminances: break # None of the most common colors had sufficient luminance, return defaults - return self.TITLE_COLOR, self.STROKE_COLOR, -1 + return self.default_title_color, self.default_title_stroke_color, -1 @property @@ -377,19 +397,20 @@ def create(self) -> None: """ title_color, stroke_color, luminance = self._get_logo_color() + gradient_command = [f'"{self.__GRADIENT_IMAGE}"', '-composite'] if self.enable_gradient else [] + command = ' '.join([ f'convert', # Resize source image f'"{self.source_file.resolve()}"', *self.resize_and_style, # Overlay gradient - f'"{self.__GRADIENT_IMAGE}"', - f'-composite', + *gradient_command, # Overlay resized logo *self.logo_command(luminance), # Put title text *self.title_text_command(title_color, stroke_color), - # Put season/episode text + # Put season/episode text TODO: The outline/text color should probably be inverted based on luminance *self.index_text_command, # Create and resize output *self.resize_output, From 66a19a94efe6ed755de0afa10ea0196484cf9fbd Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Wed, 12 Jul 2023 09:26:04 -0600 Subject: [PATCH 07/13] Use omit_gradient Other cards use this variable. Changing for consistency --- azuravian/TitleColorMatch.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 51d68ec..31b295d 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -33,9 +33,7 @@ class CardModel(BaseCardTypeCustomFontAllText): default_title_color: BetterColor = Field(default='#EBEBEB') '''Sets the title stroke color when min luminance is not reached''' default_title_stroke_color: BetterColor = Field(default='black') - '''Whether or not to enable the gradient background being drawn''' - enable_gradient: bool = Field(default=True) - # TODO: The gradient background should be changeable by the user, setting to "none" should disable, would replace the setting above + omit_gradient: bool = Field(default=False) # TODO: The fonts for season and episode count should be changeable and so should their colors? """Directory where all reference files used by this card are stored""" @@ -81,7 +79,7 @@ class CardModel(BaseCardTypeCustomFontAllText): 'font_stroke_width', 'font_vertical_shift', 'logo', 'title_min_luminance', 'check_multiple_luminances', 'invert_logos', 'default_title_color', - 'default_title_stroke_color', 'enable_gradient' + 'default_title_stroke_color', 'omit_gradient' ) def __init__(self, @@ -107,12 +105,9 @@ def __init__(self, invert_logos: bool = True, default_title_color: str = TITLE_COLOR, default_title_stroke_color: str = 'black', - enable_gradient: bool = True, + omit_gradient: bool = False, **unused ) -> None: - """ - Construct a new instance of this Card. - """ # Initialize the parent class - this sets up an ImageMagickInterface super().__init__(blur, grayscale, preferences=preferences) @@ -140,7 +135,7 @@ def __init__(self, self.invert_logos = invert_logos self.default_title_color = default_title_color self.default_title_stroke_color = default_title_stroke_color - self.enable_gradient = enable_gradient + self.omit_gradient = omit_gradient def logo_command(self, luminance: int) -> ImageMagickCommands: """ @@ -397,7 +392,12 @@ def create(self) -> None: """ title_color, stroke_color, luminance = self._get_logo_color() - gradient_command = [f'"{self.__GRADIENT_IMAGE}"', '-composite'] if self.enable_gradient else [] + gradient_command = [] + if not self.omit_gradient: + gradient_command = [ + f'"{self.__GRADIENT_IMAGE}"', + f'-composite', + ] command = ' '.join([ f'convert', @@ -410,7 +410,8 @@ def create(self) -> None: *self.logo_command(luminance), # Put title text *self.title_text_command(title_color, stroke_color), - # Put season/episode text TODO: The outline/text color should probably be inverted based on luminance + # Put season/episode text + # TODO: The outline/text color should probably be inverted based on luminance *self.index_text_command, # Create and resize output *self.resize_output, From 89a4fbfe58e82485d9520b9f6fa0f3e227753399 Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Wed, 12 Jul 2023 09:45:13 -0600 Subject: [PATCH 08/13] Reposition preferences argument --- azuravian/TitleColorMatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 31b295d..f5a5ebb 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -99,13 +99,13 @@ def __init__(self, font_vertical_shift: int = 0, blur: bool = False, grayscale: bool = False, - preferences: Optional['Preferences'] = None, title_min_luminance: int = 50, check_multiple_luminances: bool = True, invert_logos: bool = True, default_title_color: str = TITLE_COLOR, default_title_stroke_color: str = 'black', omit_gradient: bool = False, + preferences: Optional['Preferences'] = None, **unused ) -> None: From ce17a546d0036d4879da28941d4a93908d04db0e Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Sun, 30 Jul 2023 16:29:52 -0600 Subject: [PATCH 09/13] Simplify CardModel, add additional Field validators --- azuravian/TitleColorMatch.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index f5a5ebb..b710a48 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -21,19 +21,19 @@ class TitleColorMatch(BaseCardType): class CardModel(BaseCardTypeCustomFontAllText): logo_file: FilePath - font_color: Union[BetterColor, Literal['auto']] = Field(default='#EBEBEB') + font_color: Union[BetterColor, Literal['auto']] = '#EBEBEB' font_file: FilePath '''Threshold under which logos will have their colors inverted (if enabled) and text will use default_title_stroke_color (0 being black and 255 being pure white)''' - title_min_luminance: int = Field(default=50) + title_min_luminance: int = Field(min=0, default=50, max=255) '''When enabled, any color with a significant presence may be checked for luminance. When disabled, only the most common color is checked''' - check_multiple_luminances: bool = Field(default=True) + check_multiple_luminances: bool = True '''When enabled, logos will have their colors inverted when they are darker than the specified title_min_luminance''' - invert_logos: bool = Field(default=True) + invert_logos: bool = False # TODO: Evaluate whether this should default to enabled '''Sets the title color when min luminance is not reached''' - default_title_color: BetterColor = Field(default='#EBEBEB') + default_title_color: BetterColor = '#EBEBEB' '''Sets the title stroke color when min luminance is not reached''' - default_title_stroke_color: BetterColor = Field(default='black') - omit_gradient: bool = Field(default=False) + default_title_stroke_color: BetterColor = 'black' + omit_gradient: bool = False # TODO: The fonts for season and episode count should be changeable and so should their colors? """Directory where all reference files used by this card are stored""" @@ -105,8 +105,8 @@ def __init__(self, default_title_color: str = TITLE_COLOR, default_title_stroke_color: str = 'black', omit_gradient: bool = False, - preferences: Optional['Preferences'] = None, - **unused + preferences: Optional['Preferences'] = None, # type: ignore + **unused, ) -> None: # Initialize the parent class - this sets up an ImageMagickInterface @@ -118,8 +118,8 @@ def __init__(self, # Ensure characters that need to be escaped are self.title_text = self.image_magick.escape_chars(title_text) - self.season_text = self.image_magick.escape_chars(season_text.upper()) - self.episode_text = self.image_magick.escape_chars(episode_text.upper()) + self.season_text = self.image_magick.escape_chars(season_text) + self.episode_text = self.image_magick.escape_chars(episode_text) self.hide_season_text = hide_season_text self.font_color = font_color @@ -137,6 +137,7 @@ def __init__(self, self.default_title_stroke_color = default_title_stroke_color self.omit_gradient = omit_gradient + def logo_command(self, luminance: int) -> ImageMagickCommands: """ Get the ImageMagick commands to add the resized logo to the @@ -340,7 +341,7 @@ def index_text_command(self) -> ImageMagickCommands: @staticmethod - def is_custom_font(font: 'Font') -> bool: + def is_custom_font(font: 'Font') -> bool: # type: ignore """ Determines whether the given arguments represent a custom font for this card. From 2c050bd7e7cd90f0faabd5ed72546b5bcebf62b6 Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Sun, 30 Jul 2023 16:45:43 -0600 Subject: [PATCH 10/13] Add description of card extras to JSON file --- cards.json | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/cards.json b/cards.json index 7ab10ca..f467af1 100644 --- a/cards.json +++ b/cards.json @@ -9,7 +9,33 @@ "creators": ["Azuravian", "CollinHeist"], "supports_custom_fonts": true, "supports_custom_seasons": true, - "supported_extras": [] + "supported_extras": [ + { + "name": "Logo Inversion Toggle", + "identifier": "invert_logos", + "description": "Whether to invert logo colors when they are darker than the specified minimum luminance" + }, { + "name": "Minimum Title Luminance", + "identifier": "title_min_luminance", + "description": "Threshold under which logos will have their colors inverted - 0-255 for white-black" + }, { + "name": "Check Multiple Luminance Toggle", + "identifier": "check_multiple_luminances", + "description": "Whether to check all significant colors, or only the most common color" + }, { + "name": "Fallback Title Color", + "identifier": "default_title_color", + "description": "Title color to use when the minimum luminance is not reached" + }, { + "name": "Fallback Stroke Color", + "identifier": "default_title_stroke_color", + "description": "Stroke color to use when the minimum luminance is not reached" + }, { + "name": "Gradient Omission", + "identifier": "omit_gradient", + "description": "Whether to omit the gradient overlay" + } + ] }, { "name": "Gradient Logo", "identifier": "Beedman/GradientLogoTitleCard", From 11af535743858f05bf54168bdd86726cc33ead3c Mon Sep 17 00:00:00 2001 From: buthed010203 Date: Tue, 8 Aug 2023 18:44:24 -0400 Subject: [PATCH 11/13] Make logo optional Shows without logos will still have the rest of the card applied. --- azuravian/TitleColorMatch.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index b710a48..bd58671 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -20,7 +20,7 @@ class TitleColorMatch(BaseCardType): """ class CardModel(BaseCardTypeCustomFontAllText): - logo_file: FilePath + logo_file: Path font_color: Union[BetterColor, Literal['auto']] = '#EBEBEB' font_file: FilePath '''Threshold under which logos will have their colors inverted (if enabled) and text will use default_title_stroke_color (0 being black and 255 being pure white)''' @@ -147,6 +147,10 @@ def logo_command(self, luminance: int) -> ImageMagickCommands: List of ImageMagick commands. """ + # The logo file for this series doesn't exist, don't attempt to do any work + if not self.logo.exists(): + return [] + negate_commands = [] if self.invert_logos and luminance < self.title_min_luminance: negate_commands = [ @@ -224,6 +228,10 @@ def _get_logo_color(self) -> tuple[str, str, int]: # If auto color wasn't indicated use indicated color and black stroke if str(self.font_color) != 'auto': return self.font_color, 'black', 255 + # The logo file for this series doesn't exist, return the default colors + if not self.logo.exists(): + return self.default_title_color, self.default_title_stroke_color, 255 + # Command to get histogram of the colors in logo image command = ' '.join([ From 047530de1d150664e2fe99f2b6195b410951b3a8 Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Tue, 8 Aug 2023 23:17:08 -0600 Subject: [PATCH 12/13] Update optional logo logic, remove model field comments --- azuravian/TitleColorMatch.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index bd58671..3d9dbd5 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -10,6 +10,7 @@ from modules.Debug import log from modules.RemoteFile import RemoteFile + class TitleColorMatch(BaseCardType): """ This class describes a type of CardType created by azuravian, and is @@ -20,18 +21,13 @@ class TitleColorMatch(BaseCardType): """ class CardModel(BaseCardTypeCustomFontAllText): - logo_file: Path - font_color: Union[BetterColor, Literal['auto']] = '#EBEBEB' + logo_file: Optional[FilePath] = None + font_color: Union[BetterColor, Literal['auto']] = 'auto' font_file: FilePath - '''Threshold under which logos will have their colors inverted (if enabled) and text will use default_title_stroke_color (0 being black and 255 being pure white)''' title_min_luminance: int = Field(min=0, default=50, max=255) - '''When enabled, any color with a significant presence may be checked for luminance. When disabled, only the most common color is checked''' check_multiple_luminances: bool = True - '''When enabled, logos will have their colors inverted when they are darker than the specified title_min_luminance''' invert_logos: bool = False # TODO: Evaluate whether this should default to enabled - '''Sets the title color when min luminance is not reached''' default_title_color: BetterColor = '#EBEBEB' - '''Sets the title stroke color when min luminance is not reached''' default_title_stroke_color: BetterColor = 'black' omit_gradient: bool = False # TODO: The fonts for season and episode count should be changeable and so should their colors? @@ -147,8 +143,8 @@ def logo_command(self, luminance: int) -> ImageMagickCommands: List of ImageMagick commands. """ - # The logo file for this series doesn't exist, don't attempt to do any work - if not self.logo.exists(): + # Logo not provided, return empty commands + if not self.logo: return [] negate_commands = [] @@ -228,9 +224,10 @@ def _get_logo_color(self) -> tuple[str, str, int]: # If auto color wasn't indicated use indicated color and black stroke if str(self.font_color) != 'auto': return self.font_color, 'black', 255 + # The logo file for this series doesn't exist, return the default colors - if not self.logo.exists(): - return self.default_title_color, self.default_title_stroke_color, 255 + if not self.logo: + return self.default_title_color, self.default_title_stroke_color,255 # Command to get histogram of the colors in logo image From 0522ae334680988a2a2cdd56c886573a90dcd071 Mon Sep 17 00:00:00 2001 From: Collin Heist Date: Tue, 8 Aug 2023 23:31:26 -0600 Subject: [PATCH 13/13] Use Optional[Path] instead of FilePath (again) --- azuravian/TitleColorMatch.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/azuravian/TitleColorMatch.py b/azuravian/TitleColorMatch.py index 6b29b85..f6f0ad6 100644 --- a/azuravian/TitleColorMatch.py +++ b/azuravian/TitleColorMatch.py @@ -2,7 +2,7 @@ from re import compile as re_compile, findall from typing import Literal, Optional, Union -from pydantic import FilePath +from pydantic import Field from app.schemas.base import BetterColor from app.schemas.card_type import BaseCardTypeCustomFontAllText @@ -21,9 +21,9 @@ class TitleColorMatch(BaseCardType): """ class CardModel(BaseCardTypeCustomFontAllText): - logo_file: Optional[FilePath] = None + logo_file: Optional[Path] = None font_color: Union[BetterColor, Literal['auto']] = 'auto' - font_file: FilePath + font_file: Path title_min_luminance: int = Field(min=0, default=50, max=255) check_multiple_luminances: bool = True invert_logos: bool = False # TODO: Evaluate whether this should default to enabled @@ -82,7 +82,6 @@ class CardModel(BaseCardTypeCustomFontAllText): def __init__(self, source_file: Path, card_file: Path, - logo_file: Path, title_text: str, season_text: str, episode_text: str, @@ -97,6 +96,7 @@ def __init__(self, font_vertical_shift: int = 0, blur: bool = False, grayscale: bool = False, + logo_file: Optional[Path] = None, title_min_luminance: int = 50, check_multiple_luminances: bool = True, invert_logos: bool = True, @@ -150,7 +150,7 @@ def logo_command(self, luminance: int) -> ImageMagickCommands: """ # Logo not provided, return empty commands - if not self.logo: + if self.logo is None or not self.logo.exists(): return [] negate_commands = [] @@ -232,7 +232,7 @@ def _get_logo_color(self) -> tuple[str, str, int]: return self.font_color, 'black', 255 # The logo file for this series doesn't exist, return the default colors - if not self.logo: + if self.logo is None or not self.logo.exists(): return self.default_title_color, self.default_title_stroke_color,255