collinheist / titlecardmaker Goto Github PK
View Code? Open in Web Editor NEWAutomated title card maker for Plex, Jellyfin, and Emby
Home Page: https://titlecardmaker.com/
License: GNU General Public License v3.0
Automated title card maker for Plex, Jellyfin, and Emby
Home Page: https://titlecardmaker.com/
License: GNU General Public License v3.0
Add the ability to change the final card's dimensions, in case the default 3200x1800 is not desired
For thinner fonts, the black stroke effect can overwhelm the title text. This should be adjustable.
Probably like
font:
stroke_width: 105%
When querying Sonarr, episodes that have not aired yet are ignored. This behavior should be skipped if the episode file is present.
The JSON return of the API request gives:
{
'seriesId': ...,
'tvdbId': ...,
'episodeFileId': 0,
'seasonNumber': 0,
'episodeNumber': 4,
'title': 'The Moms',
'hasFile': False,
'monitored': False,
'unverifiedSceneNumbering': False,
'id': ...
}
And the hasFile
attribute can be used here
Some media naming schemes are formatted as Show Name (Year)/Season 0x/
rather than Season x
.
The Maker should support this, probably through global option like zero_pad_seasons
which takes a boolean
If a "bad image" is being sent to Plex via the API, plexapi.exceptions.BadRequest
is raised, causing the Maker to exit early.
This can happen if the Maker fails to write an image properly, and should be handled.
It would be convenient for cross-platform deployment (and dependency handling) to release the TitleCardMaker through Docker.
I know literally nothing about how to do this, so I am actively looking for tips/resources/help on getting this done.
For cards that support custom fonts (notably the StandardTitleCard
class), if the given font has unbounded text - an example of which is the South Park font:
Here, the text extends well beyond the bounding box (in red). This results in bad offsets during -annotate
calls, and overall poorly formatted cards like:
The Maker ideally should have some way of handling or identifying these situations
The TMDbInterface.get_source_image()
method fails (KeyError
) for Scrubs (2009) S00E05.
The TMDb API request returns
{'air_date': None,
'episode_number': 5,
'id': 555667,
'name': 'Webisode 5 - The Late Night with Jimmy Show',
'overview': "While Sunny tells...began to dance.",
'production_code': '',
'season_number': 0,
'show_id': 17486,
'still_path': None,
'vote_average': 0.0,
'vote_count': 0}
The __find_episode()
method updates the tmdb_id
of the SeriesInfo
object to the returned show ID (17486) which corresponds to Scrubs: Interns, despite returning the season/episode index of Scrubs (2009).
I believe this is an issue with the TMDb API, and I have created an issue here.
The automated cards can potentially spoil some episodes. The Maker should permit some sort of "non spoiler" variation of card creation that could perhaps use the series' backdrop for cards, rather than episode images.
TMDb has textless background art that could be queried and used. An example is:
However, optional blurring could also be used instead. Below is the "standard" card with a -blur 0x30
applied after resizing the source image:
Currently any non-standard card type has to be specified per-show, but for library styles (like the AnimeCard
), this is repetitive and tedious.
Changing the series yaml file to some variation of:
libraries:
TV Shows:
path: /media/tv/
card_type: standard # Default
Anime:
path: /media/anime/
card_type: anime
Would allow for all shows of a specified library to automatically have that assigned card, and then the card type for a series can always be overwritten by manually specifying the card_type
key for the series.
Cards with the words of
, on
, the
, in
, and at
(maybe others) are being modified like:
This is accompanied with an error like:
error/convert.c/ConvertImageCommand/3322.
composite: unable to open image 'C:\TitleCardMaker\modules\.objects\gradient_title.png': No such file or directory @ error/blob.c/OpenBlob/3527.
composite: `C:\TitleCardMaker\archives\iZombie (2015)\Generic Season Titles, Generic Font\Season 1\iZombie (2015) - S01E12.jpg' @ error/composite.c/CompositeImageCommand/1634
If ImageMagick is not installed, this can present itself as some archaic errors. The Maker should identify this ahead of time and warn the user at execution.
A lot of series on TMDb only have logos in the SVG format. These are currently ignored by TMDbInterface
, but a conversion can probably be used.
Modify TMDb.get_episode_title()
method to ignore the generic "Episode x"
translated into whatever language is being requested. This happens a lot for currently airing Anime in AnimeTitleCard
using the kanji
key, producing data like:
kanji: 第123話
korean: 에피소드 123
For episode 123. TMDb permits these "non-translations" for some reason. I believe the form is relatively standard so it should be easy to check for and temporarily blacklist until a better translation is available.
Add some fixer option, like --show-summary {directory}
to create summary for arbitrary input directory.
Currently the only format of video filenames (and therefore matching title card filenames) is Show Title (Year) - SxxEyy.ext
, however many people name their cards as Show Title (Year) - SxxEyy - Title.ext
.
Ideally a global preference can be added that is some form of a format string, perhaps options like:
{title}
{year}
{full_title}
{episode_title}
{season_number}
{episode_number}
For numeric values like {episode_number}
, format options like {episode_number:02}
could be used for zero-padding.
This format could also be parsed from Sonarr, as it reports the episode's filename.. However, this would really slow down the Maker, and would have to "guess" for episodes that aren't monitored/added to Sonarr
Documentation purpose only, I plan to improve the progress bars used by the Maker to not include the time estimate. Haven't decided on a new format, yet.
Rework to use plexapi
module. Something like:
plex.library.sections()[_].search(title)[0].episodes()[0].locations
Returns a list of episode files that the title cards can be named as. Looking into docs whether this API can be used to update cards..
If a filename_format
is specified that does not have episode
in it, the Maker will error on TitleCard.get_multi_output_filename()
(during regex)
These should be handled
Add the ability to skip/ignore adding specials when syncing from Sonarr. These are usually not collected, and the available images are usually poor quality.
TMDb has translations of most of their data, especially the episode titles.
An option should be available in the series YAML to get this title and store it alongside the english title from Sonarr under some arbitrary key. This would automate the kanji
key for AnimeTitleCard
type cards, but could be generalized for any card type that can utilize a non-english title.
Add a report that prints out missing assets such as:
That can either be printed at end of main loop (if some optional flag is provided), or via the fixer.
Add web interface (like TMDbInterface
) for getting source images from Apple TV.
Got a report of TMDb returning API limit error while finding episode/gathering images
HTTPSConnectionPool(host='api.themoviedb.org', port=443): Max retries exceeded with url
Currently, if a string being passed to ImageMagick has a back-tick in it, the command will fail (in one way or another).
This character should be escaped by the ImageMagickInterface
class before executing a command
Add some CI/GitHub workflow for automated tests.
Not sure what can be tested, but having some basics would be good
Add global option to change extension to something other than jpg.
Probably like:
options:
card_extension: jpg
Could use ImageMaker.VALID_IMAGE_EXTENSIONS
which is ('.jpg', '.jpeg', '.png', '.tiff', '.gif')
If a series name has an illegal character (most likely a colon), the Maker currently errors on datafile creation. This should be handled.
tv.yml example:
series:
Serie_1:
tmdb_id: X
tmdb_sync: true
It the tmdb_id (or tvdb_id) exists you can grab the whole serie information from the tmdb database, sonarr is useless. With this you can create cards for series which didn't downloaded or not exists in Sonarr.
When the card is created, it would be great if it could add a full stop between titles. eg. Slow Horses becomes Slow.Horses
Add some validation for all fonts and whether they have all characters necessary (for a given title).
Probably can use the ImageMagick font metrics functionality to do this..
It creates the archive with no issues, however, when I am trying to set my location as a mapped drive, no luck.
[DEBUG] Could not create card "\\10.38.138.253\tvshows\Slow Horses (2022)\Season 1\Slow Horses.S01E01.jpg"
[DEBUG] Command: magick convert "C:\Utils\TitleCardMaker\source\Slow Horses (2022)\s1e1.jpg" +profile "*" -gravity center -resize "3200x1800^" -extent "3200x1800" "C:\Utils\TitleCardMaker\modules\ref\GRADIENT.png" -background None -layers Flatten "C:\Utils\TitleCardMaker\modules\.objects\source_gradient.png"
stdout: b''
stderr: b''
------------------------------------------------------------
[DEBUG] Command: magick convert "C:\Utils\TitleCardMaker\modules\.objects\source_gradient.png" -font "C:\Utils\TitleCardMaker\modules\ref\Sequel-Neue.otf" -kerning -1.25 -interword-spacing 50 -interline-spacing -22 -pointsize 157.41 -gravity south -fill black -stroke black -strokewidth 3 -annotate +0+245 "FAILURE’S CONTAGIOUS" -fill "#EBEBEB" -annotate +0+245 "FAILURE’S CONTAGIOUS" "C:\Utils\TitleCardMaker\modules\.objects\gradient_title.png"
stdout: b''
stderr: b''
------------------------------------------------------------
[DEBUG] Command: magick convert -debug annotate xc: -kerning 5.42 -pointsize 67.75 -font "C:\Utils\TitleCardMaker\modules\ref\Proxima Nova Semibold.otf" -gravity east -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +1600+697.2 "SEASON 1 " -font "C:\Utils\TitleCardMaker\modules\ref\Proxima Nova Regular.otf" -gravity center -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +0+689.5 "• " -gravity west -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +1640+697.2 "EPISODE 1" null: 2>&1
stdout: b''
stderr: b"2022-04-18T07:43:28+00:00 0:00.011 0.016u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Semibold.otf; font-encoding none; text-encoding none; pointsize 67.75\r\n2022-04-18T07:43:28+00:00 0:00.012 0.016u 7.1.0 Annotate convert[24504]: annotate.c/GetTypeMetrics/921/Annotate\r\n Metrics: text: SEASON 1 ; width: 370; height: 68; ascent: 54; descent: -15; max advance: 78; bounds: 0,-0.234375 49.0469,45; origin: 369.078,0; pixels per em: 68,68; underline position: -9.044; underline thickness: 1.36\r\n2022-04-18T07:43:28+00:00 0:00.013 0.016u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Semibold.otf; font-encoding none; text-encoding none; pointsize 67.75\r\n2022-04-18T07:43:28+00:00 0:00.069 0.078u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Regular.otf; font-encoding none; text-encoding none; pointsize 67.75\r\n2022-04-18T07:43:28+00:00 0:00.070 0.078u 7.1.0 Annotate convert[24504]: annotate.c/GetTypeMetrics/921/Annotate\r\n Metrics: text: \xe2\x80\xa2 ; width: 48; height: 83; ascent: 63; descent: -21; max advance: 76; bounds: 0,-0.328125 19.5,23.5625; origin: 47.1406,0; pixels per em: 68,68; underline position: -9.044; underline thickness: 1.36\r\n2022-04-18T07:43:28+00:00 0:00.072 0.078u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Regular.otf; font-encoding none; text-encoding none; pointsize 67.75\r\n2022-04-18T07:43:28+00:00 0:00.079 0.078u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Regular.otf; font-encoding none; text-encoding none; pointsize 67.75\r\n2022-04-18T07:43:28+00:00 0:00.080 0.078u 7.1.0 Annotate convert[24504]: annotate.c/GetTypeMetrics/921/Annotate\r\n Metrics: text: EPISODE 1; width: 356; height: 83; ascent: 63; descent: -21; max advance: 76; bounds: 0,-0.328125 48.2969,45; origin: 355.703,0; pixels per em: 68,68; underline position: -9.044; underline thickness: 1.36\r\n2022-04-18T07:43:28+00:00 0:00.082 0.078u 7.1.0 Annotate convert[24504]: annotate.c/RenderFreetype/1670/Annotate\r\n Font C:\\Utils\\TitleCardMaker\\modules\\ref\\Proxima Nova Regular.otf; font-encoding none; text-encoding none; pointsize 67.75\r\nconvert: no encode delegate for this image format `XC' @ error/constitute.c/WriteImage/1392.\r\n"
------------------------------------------------------------
[DEBUG] Command: magick convert -size "774x108" -alpha on -background transparent xc:transparent -kerning 5.42 -pointsize 67.75 -font "C:\Utils\TitleCardMaker\modules\ref\Proxima Nova Semibold.otf" -fill black -stroke black -strokewidth 6 -annotate +0+83 "SEASON 1 " -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +0+83 "SEASON 1 " -font "C:\Utils\TitleCardMaker\modules\ref\Proxima Nova Regular.otf" -fill black -stroke black -strokewidth 6 -annotate +370+76.5 "•" -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +370+76.5 "•" -fill black -stroke black -strokewidth 6 -annotate +418+83 "EPISODE 1" -fill "#CFCFCF" -stroke "#CFCFCF" -strokewidth 0.75 -annotate +418+83 "EPISODE 1" "PNG32:C:\Utils\TitleCardMaker\modules\.objects\series_count_text.png"
stdout: b''
stderr: b''
------------------------------------------------------------
[DEBUG] Command: magick composite -gravity center -geometry +0+690.2 "C:\Utils\TitleCardMaker\modules\.objects\series_count_text.png" "C:\Utils\TitleCardMaker\modules\.objects\gradient_title.png" "\\10.38.138.253\tvshows\Slow Horses (2022)\Season 1\Slow Horses.S01E01.jpg"
stdout: b''
stderr: b"composite: unable to open image '\\10.38.138.253\\tvshows\\Slow Horses (2022)\\Season 1\\Slow Horses.S01E01.jpg': No such file or directory @ error/blob.c/OpenBlob/3529.\r\n"
------------------------------------------------------------
If you want to specify a custom font for multiple shows, it requires a lot of copy/pasting.
I think some variation to the series yaml files as:
libraries:
# ...
fonts:
font_label_1:
size: 110%
file: # ...
color: "#ababab"
series:
series_1:
year: # ...
font: font_label_1
Could be used (similar to libraries)
This would enable easier implementation with Plex Meta Manager, so the cards can be created into an asset directory (as opposed to media directory).
Not sure the best way to implement this, some global preference for hide_season_folders
perhaps? I wouldn't want this to carry over to archives though, just for media directories.
Datafiles being tab-separated value sheets is a remnant of the project working on exported spreadsheets, and is rather limiting for development
Datafiles should be YAML like the preference/card files, perhaps formatted as:
data:
Season 0:
1:
title: When the Future Fights Back
Season 1:
1:
absolute_number: 1
title: The Rabbit Hole
# ...
6:
absolute_number: 6
title:
- Happy Birthday,
- Lee Harvey Oswald
Where title linebreaks can be manually specified if the title is given as a list (ep 6 of example), otherwise the full title is passed to the Title
class, and split based on CardType
attributes.
Probably a fixer method to generate some series YAML file for all series' within Sonarr, this would make large libraries much easier to create cards for
Add a progress bar for each major operation (like reading source files, creating cards for a given show, updating archive, etc.)
tqdm module might work best, but how it interacts with logging is TDB
If a title or show-name has characters that aren't permitted (like ?
¿
\
), then an OSError is raised: OSError: [WinError 123] The filename, directory name, or volume label syntax is incorrect:
These should be handled by the Maker, and replaced with their reasonable counterparts.
Invalid Character | Replacement |
---|---|
? |
! |
< |
Deleted |
> |
Deleted |
: |
- |
" |
Deleted |
/ |
+ |
\ |
+ |
| |
Deleted |
* |
- |
I am not a graphic designer, but a simple logo for the Maker would be nice to have.
TMDb supports querying by external ID via https://api.themoviedb.org/3/find/{id}
, this can prevent false data pulls from TMDb through the interface.
Created info
, warn
, and error
functions to aid in development before I knew about the builtin logging
module
Rework code to work with this module instead, removing need for the meh Debug methods/file.
Add ability/option to detect episodes that are 'split' across multiple episodes, such as: Star Trek: Voyager Season 1 Episodes 1-2
.
These episodes are usually titled Title (1)
and Title (2)
, and should be contained within the same season.
The wiki needs to be updated with documentation on use of the fixer
Implement the "template" idea (I think from Plex Meta Manager) for series YAML files.
Could use same variable specification as PMM, like:
template:
no_season:
year: <<year>>
library: <<library>>
seasons:
hide: true
series:
Series Name:
template: {name: no_season, year: 2020, library: TV}
Would be equivalent to:
series:
Series Name:
year: 2020
library: TV
seasons:
hide: true
Transition all applicable code/classes to work on Title
objects rather than hard-coded instances of title_top
and title_bottom
.
Similar to #20, but instead of matching each episode with the TVDb ID (which isn't currently possible without adding interface to TVDb's paid API), each series can be matched by series ID as that is returned by Sonarr.
Although there are not many false positives when matching by name and year, this should be pretty much foolproof (and can use old matching if needed).
Currently --read-all-series
pulls all series from Sonarr, this should be modified to support optionally pulling by tag.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.