Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bc37caf
Update README.md
CollinHeist Apr 24, 2023
0738397
Add all supported TMDb language codes
CollinHeist Apr 24, 2023
beb288f
Merge branch 'develop' of https://github.com/CollinHeist/TitleCardMak…
CollinHeist Apr 24, 2023
b973568
Merge branch 'master' into develop
CollinHeist Apr 24, 2023
b72ef01
Merge branch 'master' into develop
CollinHeist Apr 24, 2023
b94fd74
Merge branch 'master' into develop
CollinHeist Apr 24, 2023
995a132
Use 4:4:4 sampling factor in all card operations
CollinHeist Apr 28, 2023
008d1e0
Log everything (don't ignore just logged messages)
CollinHeist Apr 30, 2023
3ff9b49
Allow custom episode text color in Anime title card
CollinHeist May 1, 2023
f36ccf1
Handle Jellyfin libraries with no collection type
CollinHeist May 4, 2023
e54c440
Allow transparent background images
CollinHeist May 4, 2023
8db99d9
Allow logos to be omitted from season posters
CollinHeist May 4, 2023
30af6c2
Handle extra spaces in version files
CollinHeist May 4, 2023
77441cc
Fix manual title card importing to Emby and Jellyfin
CollinHeist May 5, 2023
0bb2389
Add hide_episode_text to Fade title card
CollinHeist May 5, 2023
9e84a9c
Parse "season_text_color" extra for RomanNumeral title card
CollinHeist May 5, 2023
7563b66
Uppercase episode text in StarWars title card
CollinHeist May 5, 2023
808b8c2
Finalize #337
CollinHeist May 5, 2023
68e7f62
Handle errors in interface creation when importing cards with fixer
CollinHeist May 5, 2023
bcc52cf
Update JellyfinInterface.py
CollinHeist May 5, 2023
7fc408f
Handle empty string database ID's
CollinHeist May 8, 2023
8712429
Create resize image method in ImageMagickInterface
CollinHeist May 8, 2023
125a667
Handle text wider than the frame in TintedFrame title card
CollinHeist May 8, 2023
dd65458
Move series YAML read log statement
CollinHeist May 13, 2023
d53469c
Allow logo file format strings in Tinted Frame card
CollinHeist May 15, 2023
57709c4
Error if logo file indicated and DNE in TintedFrame card
CollinHeist May 15, 2023
39d48ee
Accept combined language codes in title translations
CollinHeist May 18, 2023
ddab18b
Update Episode.py
CollinHeist May 19, 2023
ff93327
Fix Tinted Frame syntax error
CollinHeist May 21, 2023
e895d5f
Handle badly typed template specification
CollinHeist May 26, 2023
984c6f1
Add multiple extras to Tinted Frame card type
CollinHeist May 31, 2023
7fb7c2e
Catch ValueError in PersistentDatabase function calls
CollinHeist Jun 3, 2023
384e5c9
Print traceback for RemoteFile download error
CollinHeist Jun 3, 2023
c4f86ce
Handle uncaught Exceptions for Series in serial run
CollinHeist Jun 3, 2023
c73403e
Change default SonarrInterface request timeout to 10 minutes
CollinHeist Jun 5, 2023
abdf833
Handle more types of malformed image content
CollinHeist Jun 5, 2023
fa9fe22
Merge branch 'master' into develop
CollinHeist Jun 5, 2023
0f377a5
Correct Tautulli import for entire seasons
CollinHeist Jun 12, 2023
eb0503f
Log PersistentDatabase deletion
CollinHeist Jun 12, 2023
6674ff3
Reject ID's of 0 in DatabaseInfoContainer
CollinHeist Jun 12, 2023
9d2e54a
Call set_*_id func in SeriesInfo.__init__
CollinHeist Jun 12, 2023
3a0dada
Check other image sources if TMDb just permanently blacklisted entry
CollinHeist Jun 12, 2023
e6424d1
Handle libraries without collection types in Jellyfin sync
CollinHeist Jun 14, 2023
4872eaf
Skip episodes in Jellyfin without indices
CollinHeist Jun 15, 2023
45b1d78
Always assign Episode ID's for Emby/Jellyfin series
CollinHeist Jun 15, 2023
3701141
Fix logo finding logic in Poster title card
CollinHeist Jun 15, 2023
8f71142
Correct non-default season text color assignment in Roman Numeral card
CollinHeist Jun 15, 2023
7264c00
Handle invalid logo paths with traceback in Tinted Frame card
CollinHeist Jun 15, 2023
30ff377
Properly assign Series TVRage IDs
CollinHeist Jun 15, 2023
336f814
Fix syncing from Emby
CollinHeist Jun 15, 2023
4dddfb5
Update many type annotations
CollinHeist Jun 15, 2023
74753d0
More type annotations
CollinHeist Jun 16, 2023
8c3b53b
More more TA
CollinHeist Jun 16, 2023
767f79f
Assign airdate to Episode correctly
CollinHeist Jun 16, 2023
3ab7474
Update Webhook to tag release Discord group
CollinHeist Jun 16, 2023
e41bdf1
Log relevant Series and Episode when download fails
CollinHeist Jun 16, 2023
68b5522
Update version number to v1.14.1
CollinHeist Jun 17, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .dockerignore
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ config/
fonts/
logs/
source/
cards/
archives/
title_cards/
docs/
yml/
yaml/
app/title_cards/
.DS_Store
.git
.github
Expand All @@ -11,6 +18,4 @@ source/
README.md
LICENSE
Dockerfile
._*
*.yml
*.yaml
._*
1 change: 1 addition & 0 deletions .github/workflows/discord_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ jobs:
webhook_token: ${{ secrets.RELEASE_WEBHOOK_TOKEN }}
release: true
title: TitleCardMaker VERSION
message: "<@&1111000623261958234> - A new version of TitleCardMaker has been released."
username: MakerBot
avatar_url: https://raw.githubusercontent.com/CollinHeist/TitleCardMaker/master/.github/logo.png
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Below are some examples of each style of title card that can be created automati
### Built-in Card Types
<img alt="Anime" src="https://user-images.githubusercontent.com/17693271/185820454-4e3dca1c-c0df-4fa0-a7a7-81e070aa9e69.jpg" height="150"/> <img alt="Cutout" src="https://user-images.githubusercontent.com/17693271/212500535-e88daff6-ecc0-4cc8-8627-82069114c7e0.jpg" height="150"/> <img alt="Divider" src="https://user-images.githubusercontent.com/17693271/232378485-a9a737dc-9faf-47c2-b639-7df3d3ffb194.jpg" height="150"> <img alt="Fade" src="https://user-images.githubusercontent.com/17693271/214648223-b4f68553-e982-4efa-a16b-9662018b5d40.jpg" height="150"/> <img alt="Frame" src="https://user-images.githubusercontent.com/17693271/202352614-155a176a-fdb0-4476-9f11-6a3a20533a54.jpg" height="150"/> <img alt="Landscape" src="https://user-images.githubusercontent.com/17693271/202352137-b411da21-65ce-4bed-991b-90428c71ec34.jpg" height="150"/> <img alt="Logo" src="https://user-images.githubusercontent.com/17693271/172227163-0ee4990a-b0a8-4dbd-91b3-3f57dfe6e732.jpg" height="150"/> <img alt="Olivier" src="https://user-images.githubusercontent.com/17693271/212500009-067f14ff-4f48-4f75-bacd-7311a9aba716.jpg" height="150"/> <img alt="Poster" src="https://user-images.githubusercontent.com/17693271/180627387-f72bb58e-e001-4608-b4be-82a26263c628.jpg" height="150"/> <img alt="Roman" src="https://user-images.githubusercontent.com/17693271/203910966-4dde1466-6c7e-4422-923b-1f9222ad49e9.jpg" height="150"/> <img alt="Standard" src="https://user-images.githubusercontent.com/17693271/212500240-ae946f2c-a5c8-4881-85f2-83ccb45bf46e.jpg" height="150"/> <img alt="Star Wars" src="https://user-images.githubusercontent.com/17693271/170836059-136fa6eb-40ef-4cd7-9aca-8ad8e0537239.jpg" height="150"> <img alt="tinted Frame" src="https://user-images.githubusercontent.com/17693271/233257029-8b17ce2e-01ea-4ae3-bc73-54e152be4d31.jpg" height="150"> <img alt="Tinted Glass" src="https://user-images.githubusercontent.com/17693271/213939482-6018b2be-28c5-42dd-988d-d7b9733fe0e8.jpg" height="150">

> The above cards are, in order, the [anime](https://github.com/CollinHeist/TitleCardMaker/wiki/AnimeTitleCard), [cutout](https://github.com/CollinHeist/TitleCardMaker/wiki/CutoutTitleCard), [divider](https://github.com/CollinHeist/TitleCardMaker/wiki/DividerTitleCard) [fade](https://github.com/CollinHeist/TitleCardMaker/wiki/FadeTitleCard), [frame](https://github.com/CollinHeist/TitleCardMaker/wiki/FrameTitleCard), [landscape](https://github.com/CollinHeist/TitleCardMaker/wiki/LandscapeTitleCard), [logo](https://github.com/CollinHeist/TitleCardMaker/wiki/LogoTitleCard), [olivier](https://github.com/CollinHeist/TitleCardMaker/wiki/OlivierTitleCard), [poster](https://github.com/CollinHeist/TitleCardMaker/wiki/PosterTitleCard), [roman](https://github.com/CollinHeist/TitleCardMaker/wiki/RomanNumeralTitleCard), [standard](https://github.com/CollinHeist/TitleCardMaker/wiki/StandardTitleCard), [star wars](https://github.com/CollinHeist/TitleCardMaker/wiki/StarWarsTitleCard), [tinted frame](https://github.com/CollinHeist/TitleCardMaker/wiki/TintedFrameTitleCard), and the [tinted glass](https://github.com/CollinHeist/TitleCardMaker/wiki/TintedGlassTitleCard) title cards - the [textless](https://github.com/CollinHeist/TitleCardMaker/wiki/TitleCard) card is not shown.
> The above cards are, in order, the [anime](https://github.com/CollinHeist/TitleCardMaker/wiki/AnimeTitleCard), [cutout](https://github.com/CollinHeist/TitleCardMaker/wiki/CutoutTitleCard), [divider](https://github.com/CollinHeist/TitleCardMaker/wiki/DividerTitleCard), [fade](https://github.com/CollinHeist/TitleCardMaker/wiki/FadeTitleCard), [frame](https://github.com/CollinHeist/TitleCardMaker/wiki/FrameTitleCard), [landscape](https://github.com/CollinHeist/TitleCardMaker/wiki/LandscapeTitleCard), [logo](https://github.com/CollinHeist/TitleCardMaker/wiki/LogoTitleCard), [olivier](https://github.com/CollinHeist/TitleCardMaker/wiki/OlivierTitleCard), [poster](https://github.com/CollinHeist/TitleCardMaker/wiki/PosterTitleCard), [roman](https://github.com/CollinHeist/TitleCardMaker/wiki/RomanNumeralTitleCard), [standard](https://github.com/CollinHeist/TitleCardMaker/wiki/StandardTitleCard), [star wars](https://github.com/CollinHeist/TitleCardMaker/wiki/StarWarsTitleCard), [tinted frame](https://github.com/CollinHeist/TitleCardMaker/wiki/TintedFrameTitleCard), and the [tinted glass](https://github.com/CollinHeist/TitleCardMaker/wiki/TintedGlassTitleCard) title cards - the [textless](https://github.com/CollinHeist/TitleCardMaker/wiki/TitleCard) card is not shown.

<details><summary><h3>User-Created Card Types</h3></summary>

Expand Down
30 changes: 21 additions & 9 deletions fixer.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,16 @@ class Episode:
spoil_type: str

# Create MediaServer Interface
if args.media_server == 'emby':
media_interface = EmbyInterface(**pp.emby_interface_kwargs)
elif args.media_server == 'jellyfin':
media_interface = JellyfinInterface(**pp.jellyfin_interface_kwargs)
else:
media_interface = PlexInterface(**pp.plex_interface_kwargs)
try:
if args.media_server == 'emby':
media_interface = EmbyInterface(**pp.emby_interface_kwargs)
elif args.media_server == 'jellyfin':
media_interface = JellyfinInterface(**pp.jellyfin_interface_kwargs)
else:
media_interface = PlexInterface(**pp.plex_interface_kwargs)
except Exception as e:
log.critical(f'Cannot connect to "{args.media_server}" Media Server')
exit(1)

# Get series/name + year from archive directory if unspecified
if hasattr(args, 'import_cards'):
Expand All @@ -228,13 +232,14 @@ class Episode:
archive = pp.source_directory / series_info.full_clean_name
library = args.revert_series[0]

# Get series ID's if provided
# Get series database ID's
if args.id:
for id_type, id_ in args.id:
try:
getattr(series_info, f'set_{id_type}_id')(id_)
except Exception as e:
log.error(f'Unrecognized ID type "{id_type}" - {e}')
media_interface.set_series_ids(library, series_info)

# Forget cards associated with this series
media_interface.remove_records(library, series_info)
Expand All @@ -246,7 +251,7 @@ class Episode:
exit(1)

# For each image, fill out episode map to load into server
episode_map = {}
episode_infos, episode_map = [], {}
for image in all_images:
if (groups := match(r'.*s(\d+).*e(\d+)', image.name, IGNORECASE)):
season, episode = map(int, groups.groups())
Expand All @@ -255,9 +260,16 @@ class Episode:
continue

# Import image into library
ep = Episode(image, EpisodeInfo('', season, episode), 'spoiled')
episode_infos.append((episode_info := EpisodeInfo('', season, episode)))
ep = Episode(image, episode_info, 'spoiled')
episode_map[f'{season}-{episode}'] = ep

# Set EpisodeInfo database ID's
media_interface.set_episode_ids(
library_name=library, series_info=series_info,
episode_infos=episode_infos, inplace=True
)

# Load images into server
media_interface.set_title_cards(library, series_info, episode_map)

Expand Down
3 changes: 2 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ def check_for_update():
except Exception:
log.debug(f'Failed to check for new version')
else:
if (available_version := response.json().get('name')) != pp.version:
available_version = response.json().get('name', '').strip()
if available_version != pp.version:
log.info(f'New version of TitleCardMaker ({available_version}) '
f'available')
if is_docker:
Expand Down
20 changes: 16 additions & 4 deletions mini_maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,10 +318,16 @@
season_poster_group.add_argument(
'--season-poster',
type=Path,
nargs=3,
nargs=2,
default=SUPPRESS,
metavar=('SOURCE', 'LOGO', 'DESTINATION'),
metavar=('SOURCE', 'DESTINATION'),
help='Create a season poster with the given assets')
season_poster_group.add_argument(
'--season-poster-logo',
type=Path,
default=SUPPRESS,
metavar='LOGO',
help='Add the given logo to the created season poster')
season_poster_group.add_argument(
'--season-text',
type=str,
Expand Down Expand Up @@ -550,15 +556,21 @@ class Show:

# Create season posters
if hasattr(args, 'season_poster'):
if hasattr(args, 'season_poster_logo'):
logo = args.season_poster_logo
else:
logo = None

SeasonPoster(
source=args.season_poster[0],
logo=args.season_poster[1],
destination=args.season_poster[2],
destination=args.season_poster[1],
logo=logo,
season_text=args.season_text,
font=args.season_font,
font_color=args.season_font_color,
font_size=float(args.season_font_size[:-1])/100.0,
font_kerning=float(args.season_font_kerning[:-1])/100.0,
top_placement=args.top_placement,
omit_gradient=args.no_gradient,
omit_logo=not hasattr(args, 'season_poster_logo'),
).create()
13 changes: 12 additions & 1 deletion modules/BaseCardType.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def USES_SEASON_TITLE(self) -> bool:
def __init__(self,
blur: bool = False,
grayscale: bool = False, *,
preferences: Optional['Preferences'] = None) -> None:
preferences: Optional['Preferences'] = None) -> None: # type: ignore
"""
Construct a new CardType. Must call super().__init__() to
initialize the parent ImageMaker class (for PreferenceParser and
Expand Down Expand Up @@ -190,6 +190,8 @@ def resize(self) -> ImageMagickCommands:
"""

return [
# Use 4:4:4 sampling by default
f'-sampling-factor 4:4:4',
# Full sRGB colorspace on source image
f'-set colorspace sRGB',
# Ignore profile conversion warnings
Expand All @@ -213,6 +215,8 @@ def style(self) -> ImageMagickCommands:
"""

return [
# Use 4:4:4 sampling by default
f'-sampling-factor 4:4:4',
# Full sRGB colorspace on source image
f'-set colorspace sRGB',
# Ignore profile conversion warnings
Expand All @@ -237,6 +241,8 @@ def resize_and_style(self) -> ImageMagickCommands:
"""

return [
# Use 4:4:4 sampling by default
f'-sampling-factor 4:4:4',
# Full sRGB colorspace on source image
f'-set colorspace sRGB',
# Ignore profile conversion warnings
Expand Down Expand Up @@ -267,6 +273,11 @@ def resize_output(self) -> ImageMagickCommands:
"""

return [
f'-sampling-factor 4:4:4',
f'-set colorspace sRGB',
f'+profile "*"',
f'-background transparent',
f'-gravity center',
f'-resize "{self.preferences.card_dimensions}"',
f'-extent "{self.preferences.card_dimensions}"',
]
Expand Down
5 changes: 3 additions & 2 deletions modules/BaseSummary.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from math import ceil
from pathlib import Path
from random import sample
from typing import Optional

from modules.Debug import log
from modules.ImageMaker import ImageMaker
Expand Down Expand Up @@ -35,7 +36,7 @@ class BaseSummary(ImageMaker):


@abstractmethod
def __init__(self, show: 'Show', created_by: str=None) -> None:
def __init__(self, show: 'Show', created_by: Optional[str] = None) -> None:
"""
Initialize this object.

Expand Down Expand Up @@ -65,7 +66,7 @@ def __init__(self, show: 'Show', created_by: str=None) -> None:
self.number_rows = 0


def _select_images(self, maximum_images: int=9) -> bool:
def _select_images(self, maximum_images: int = 9) -> bool:
"""
Select the images that are to be incorporated into the show
summary. This updates the object's inputs and number_rows
Expand Down
1 change: 0 additions & 1 deletion modules/CleanPath.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ def sanitize(self) -> 'CleanPath':
finalized_path = self.finalize()
# If path resolution raises an error, clean and then re-resolve
except Exception as e:
log.exception(f'Error finalizing "{self}"', e)
finalized_path =self._sanitize_parts(CleanPath.cwd()/self).resolve()

return self._sanitize_parts(finalized_path)
5 changes: 3 additions & 2 deletions modules/DataFileInterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from modules.Debug import log
from modules.EpisodeInfo import EpisodeInfo
from modules.SeriesInfo import SeriesInfo
import modules.global_objects as global_objects
from modules.Title import Title

Expand All @@ -18,7 +19,7 @@ class DataFileInterface:
GENERIC_DATA_FILE_NAME = 'data.yml'


def __init__(self, series_info: 'SeriesInfo', data_file: Path) -> None:
def __init__(self, series_info: SeriesInfo, data_file: Path) -> None:
"""
Constructs a new instance of the interface for the specified
data file. This also creates the parent directories for the data
Expand Down Expand Up @@ -223,7 +224,7 @@ def add_data_to_entry(self, episode_info: EpisodeInfo,
self.__write_data(yaml)


def add_many_entries(self, new_episodes: Iterable['EpisodeInfo']) -> None:
def add_many_entries(self, new_episodes: Iterable[EpisodeInfo]) -> None:
"""
Adds many entries at once. This only reads and writes from this
interface's file once.
Expand Down
13 changes: 9 additions & 4 deletions modules/DatabaseInfoContainer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from abc import ABC
from typing import Any, Optional
from typing import Any, Callable, Optional

from modules.Debug import log

Expand All @@ -11,8 +11,10 @@ class DatabaseInfoContainer(ABC):
within an objct.
"""

def _update_attribute(self, attribute: str, value: Any,
type_: Optional[callable] = None) -> None:
def _update_attribute(self,
attribute: str,
value: Any,
type_: Optional[Callable] = None) -> None:
"""
Set the given attribute to the given value with the given type.

Expand All @@ -23,7 +25,10 @@ def _update_attribute(self, attribute: str, value: Any,
"""

# Set attribute if current value is None and new value isn't
if getattr(self, attribute) is None and value is not None:
if (value is not None
and value != 0
and getattr(self, attribute) is None
and len(str(value)) > 0):
# If a type is defined, use that
if type_ is None:
setattr(self, attribute, value)
Expand Down
13 changes: 0 additions & 13 deletions modules/Debug.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,27 +28,14 @@ def exception(self, msg: object, excpt: Exception, *args, **kwargs) -> None:

# StreamHandler to integrate log messages with TQDM
class LogHandler(StreamHandler):
def __init__(self, level=NOTSET):
super().__init__(level)
self.__just_logged = []

def emit(self, record):
# Skip if logged recently and not at least an error
if record.levelno < ERROR and record.msg in self.__just_logged:
return None

# Write after flushing buffer to integrate with tqdm
try:
tqdm.write(self.format(record))
self.flush()
except Exception:
self.handleError(record)

# Add to just logged list, keep list below 5 entries
self.__just_logged.append(record.msg)
if len(self.__just_logged) > 5:
self.__just_logged.pop(0)


# Formatter classes to handle exceptions
class ErrorFormatterColor(Formatter):
Expand Down
Loading