-
-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
10 changed files
with
436 additions
and
14 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,80 @@ | ||
from pathlib import Path | ||
|
||
from sqlalchemy import create_engine | ||
from sqlalchemy.orm import sessionmaker | ||
from sqlalchemy.ext.declarative import declarative_base | ||
|
||
from app.models.preferences import Preferences | ||
from modules.EmbyInterface import EmbyInterface | ||
from modules.JellyfinInterface import JellyfinInterface | ||
from modules.PlexInterface2 import PlexInterface | ||
from modules.SonarrInterface2 import SonarrInterface | ||
from modules.TMDbInterface2 import TMDbInterface | ||
|
||
SQLALCHEMY_DATABASE_URL = 'sqlite:///./db.sqlite' | ||
engine = create_engine( | ||
# TODO https://github.com/ChristopherGS/ultimate-fastapi-tutorial/blob/main/part-08-structure-and-versioning/app/db/session.py | ||
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} | ||
) | ||
|
||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | ||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | ||
Base = declarative_base() | ||
|
||
if (file := Path('/mnt/user/Media/TitleCardMaker/app/prefs.json')).exists(): | ||
from pickle import load | ||
with file.open('rb') as fh: | ||
PreferencesLocal = load(fh) | ||
else: | ||
PreferencesLocal = Preferences() | ||
|
||
EmbyInterfaceLocal = None | ||
if PreferencesLocal.use_emby: | ||
try: | ||
EmbyInterfaceLocal = EmbyInterface( | ||
PreferencesLocal.emby_url, | ||
PreferencesLocal.emby_api_key, | ||
PreferencesLocal.emby_username, | ||
PreferencesLocal.emby_use_ssl, | ||
PreferencesLocal.emby_filesize_limit, | ||
) | ||
except Exception as e: | ||
... | ||
|
||
JellyfinInterfaceLocal = None | ||
if PreferencesLocal.use_jellyfin: | ||
try: | ||
JellyfinInterfaceLocal = JellyfinInterface( | ||
PreferencesLocal.jellyfin_url, | ||
PreferencesLocal.jellyfin_api_key, | ||
PreferencesLocal.jellyfin_username, | ||
PreferencesLocal.jellyfin_use_ssl, | ||
PreferencesLocal.jellyfin_filesize_limit, | ||
) | ||
except Exception as e: | ||
... | ||
|
||
PlexInterfaceLocal = None | ||
if PreferencesLocal.use_plex: | ||
try: | ||
PlexInterfaceLocal = PlexInterface( | ||
PreferencesLocal.plex_url, | ||
PreferencesLocal.plex_token, | ||
PreferencesLocal.plex_use_ssl, | ||
PreferencesLocal.plex_integrate_with_pmm, | ||
PreferencesLocal.plex_filesize_limit, | ||
) | ||
except Exception: | ||
... | ||
|
||
SonarrInterfaceLocal = None | ||
if PreferencesLocal.use_sonarr: | ||
SonarrInterfaceLocal = SonarrInterface( | ||
PreferencesLocal.sonarr_url, | ||
PreferencesLocal.sonarr_api_key, | ||
PreferencesLocal.plex_use_ssl, | ||
) | ||
|
||
TMDbInterfaceLocal = None | ||
if PreferencesLocal.use_tmdb: | ||
TMDbInterfaceLocal = TMDbInterface( | ||
PreferencesLocal.tmdb_api_key, | ||
) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey | ||
|
||
from app.database.session import Base | ||
|
||
class Card(Base): | ||
__tablename__ = 'card' | ||
|
||
id = Column(Integer, primary_key=True, index=True) | ||
series_id = Column(Integer, ForeignKey('series.id')) | ||
episode_id = Column(Integer, ForeignKey('episode.id')) | ||
font_id = Column(Integer, ForeignKey('font.id')) | ||
|
||
source = Column(String) | ||
output = Column(String) | ||
card_type = Column(String) | ||
|
||
blur = Column(Boolean) | ||
grayscale = Column(Boolean) | ||
|
||
title = Column(String) | ||
season_text = Column(String) | ||
episode_text = Column(String) | ||
hide_season = Column(Boolean) | ||
|
||
extras = Column(String, nullable=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,35 @@ | ||
from pydantic import BaseModel | ||
from sqlalchemy import Column, Integer, String, ForeignKey | ||
from sqlalchemy import Boolean, Column, Integer, String, ForeignKey | ||
|
||
class Episode(BaseModel): | ||
from app.database.session import Base | ||
|
||
def default_source_file(context) -> str: | ||
params = context.get_current_parameters() | ||
return f's{params["season_number"]}e{params["episode_number"]}.jpg' | ||
|
||
def default_destination(context) -> str: | ||
params = context.get_current_parameters() | ||
return f'card-s{params["season_number"]}e{params["episode_number"]}.jpg' | ||
|
||
class Episode(Base): | ||
__tablename__ = 'episode' | ||
|
||
id = Column(Integer, primary_key=True, index=True) | ||
series_id = Column(Integer, ForeignKey('series.id')) | ||
|
||
season_number = Column(Integer) | ||
episode_number = Column(Integer) | ||
absolute_number = Column(Integer, nullable=True) | ||
absolute_number = Column(Integer, default=None) | ||
|
||
title = Column(String) | ||
match_title = Column(Boolean, default=False) | ||
|
||
source_file = Column(String, default=default_source_file) | ||
destination = Column(String, default=default_destination) | ||
|
||
source_file = Column(String) | ||
destination = Column(String) | ||
emby_id = Column(Integer, default=None) | ||
imdb_id = Column(String, default=None) | ||
jellyfin_id = Column(String, default=None) | ||
sonarr_id = Column(String, default=None) | ||
tmdb_id = Column(Integer, default=None) | ||
tvdb_id = Column(Integer, default=None) | ||
tvrage_id = Column(Integer, default=None) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from sqlalchemy import Boolean, Column, Integer, Float, String, ForeignKey | ||
|
||
from json import dumps, loads | ||
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method | ||
from sqlalchemy.ext.mutable import MutableDict, MutableList | ||
from sqlalchemy import PickleType | ||
|
||
from app.database.session import Base | ||
|
||
class Font(Base): | ||
__tablename__ = 'font' | ||
|
||
id = Column(Integer, primary_key=True, index=True) | ||
name = Column(String) | ||
file_path = Column(String, default=None) | ||
color = Column(String, default=None) | ||
title_case = Column(String, default=None) | ||
size = Column(Float, default=1.0) | ||
kerning = Column(Float, default=1.0) | ||
stroke_width = Column(Float, default=1.0) | ||
interline_spacing = Column(Integer, default=0) | ||
vertical_shift = Column(Integer, default=0) | ||
validate_characters = Column(Boolean, default=None) | ||
delete_missing = Column(Boolean, default=True) | ||
replacements = Column(MutableDict.as_mutable(PickleType), default={}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
from collections import namedtuple | ||
from os import environ | ||
from pathlib import Path | ||
|
||
from pickle import dump | ||
|
||
from modules.Debug import log | ||
from modules.ImageMagickInterface import ImageMagickInterface | ||
from modules.StyleSet import StyleSet | ||
|
||
EpisodeDataSource = namedtuple('EpisodeDataSource', ('value', 'label')) | ||
Emby = EpisodeDataSource('emby', 'Emby') | ||
Jellyfin = EpisodeDataSource('jellyfin', 'Jellyfin') | ||
Plex = EpisodeDataSource('plex', 'Plex') | ||
Sonarr = EpisodeDataSource('sonarr', 'Sonarr') | ||
TMDb = EpisodeDataSource('tmdb', 'TMDb') | ||
|
||
class Preferences: | ||
|
||
DEFAULT_CARD_FILENAME_FORMAT = ('{full_name} S{season_number:02}' | ||
'E{episode_number:02}') | ||
DEFAULT_CARD_EXTENSION = '.jpg' | ||
DEFAULT_IMAGE_SOURCE_PRIORITY = ['TMDb', 'Plex', 'Jellyfin', 'Emby'] | ||
DEFAULT_EPISODE_DATA_SOURCE = 'Sonarr' | ||
VALID_IMAGE_EXTENSIONS = ('.jpg', '.jpeg', '.png', '.tiff', '.gif', '.webp') | ||
|
||
|
||
def __init__(self) -> None: | ||
self.asset_directory = Path(__file__).parent.parent / 'assets' | ||
self.card_directory = Path(__file__).parent / 'cards' | ||
self.source_directory = Path(__file__).parent / 'source' | ||
|
||
self.card_filename_format = self.DEFAULT_CARD_FILENAME_FORMAT | ||
self.card_extension = self.DEFAULT_CARD_EXTENSION | ||
self.image_source_priority = self.DEFAULT_IMAGE_SOURCE_PRIORITY | ||
self.episode_data_source = self.DEFAULT_EPISODE_DATA_SOURCE | ||
self.valid_image_extensions = self.VALID_IMAGE_EXTENSIONS | ||
|
||
self.validate_fonts = True | ||
self.specials_folder_format = 'Specials' | ||
self.season_folder_format = 'Season {season}' | ||
self.sync_specials = True | ||
self.supported_language_codes = [] | ||
|
||
self.default_card_type = 'Standard' | ||
self.default_watched_style = 'Unique' | ||
self.default_unwatched_style = 'Unique' | ||
|
||
self.use_emby = False | ||
self.emby_url = 'http://192.168.0.11:8096/emby' #'' | ||
self.emby_api_key = 'e25b06a1aee34fc0949c35d74f379d03' #'' | ||
self.emby_username = 'CollinHeist' #'' | ||
self.emby_use_ssl = False | ||
self.emby_filesize_limit = None | ||
|
||
self.use_jellyfin = False | ||
self.jellyfin_url = '' | ||
self.jellyfin_api_key = '' | ||
self.jellyfin_username = '' | ||
self.jellyfin_use_ssl = False | ||
self.jellyfin_filesize_limit = None | ||
|
||
self.use_plex = True #False | ||
self.plex_url = 'http://192.168.0.29:32400/' #'' | ||
self.plex_token = 'pzfzWxW-ygxzJJc-t_Pw' #'' | ||
self.plex_use_ssl = False | ||
self.plex_integrate_with_pmm = False | ||
self.plex_filesize_limit = None | ||
|
||
self.use_sonarr = True #False | ||
self.sonarr_url = 'http://192.168.0.29:8989/api/v3/' #'' | ||
self.sonarr_api_key = 'd43fdeb9c3744f58b1ffbaf5dc21e55a' #'' | ||
self.sonarr_use_ssl = False | ||
self.sonarr_libraries = {} | ||
|
||
self.use_tmdb = True #False | ||
self.tmdb_api_key = 'b1dbf02a14b523a94401e3e6ee521353' #'' | ||
self.tmdb_minimum_width = 800 #0 | ||
self.tmdb_minimum_height = 400 #0 | ||
self.tmdb_skip_localized = False | ||
self.supported_language_codes = [] | ||
|
||
self.is_docker = environ.get('TCM_IS_DOCKER', 'false').lower() == 'true' | ||
self.use_magick_prefix = False | ||
self.imagemagick_container = 'ImageMagick' #None | ||
self.imagemagick_timeout = 60 | ||
self.__determine_imagemagick_prefix() | ||
|
||
|
||
def __setattr__(self, name, value) -> None: | ||
self.__dict__[name] = value | ||
self._rewrite_preferences() | ||
|
||
|
||
def _rewrite_preferences(self) -> None: | ||
with Path('/mnt/user/Media/TitleCardMaker/app/prefs.json').open('wb') as fh: | ||
dump(self, fh) | ||
|
||
|
||
def __determine_imagemagick_prefix(self) -> None: | ||
""" | ||
Determine whether to use the "magick " prefix for ImageMagick commands. | ||
If a prefix cannot be determined, a critical message is logged and the | ||
program exits with an error. | ||
""" | ||
|
||
# Try variations of the font list command with/out the "magick " prefix | ||
for prefix, use_magick in zip(('', 'magick '), (False, True)): | ||
# Create ImageMagickInterface and verify validity | ||
interface = ImageMagickInterface( | ||
self.imagemagick_container, | ||
use_magick, | ||
self.imagemagick_timeout | ||
) | ||
if interface.validate_interface(): | ||
self.use_magick_prefix = use_magick | ||
log.debug(f'Using "{prefix}" ImageMagick command prefix') | ||
return None | ||
|
||
# If none of the font commands worked, IM might not be installed | ||
log.critical(f"ImageMagick doesn't appear to be installed") | ||
self.valid = False | ||
|
||
|
||
def update_values(self, **update_kwargs) -> None: | ||
for name, value in update_kwargs.items(): | ||
self.__dict__[name] = value | ||
self._rewrite_preferences() | ||
|
||
|
||
@property | ||
def valid_image_sources(self) -> list[str]: | ||
return set( | ||
(['Emby'] if self.use_emby else []) | ||
+ (['Plex'] if self.use_plex else []) | ||
+ (['TMDb'] if self.use_tmdb else []) | ||
) | ||
|
||
|
||
@property | ||
def valid_episode_data_sources(self) -> list[str]: | ||
return ( | ||
(['Emby'] if self.use_emby else []) | ||
+ (['Plex'] if self.use_plex else []) | ||
+ (['TMDb'] if self.use_tmdb else []) | ||
+ (['Sonarr'] if self.use_sonarr else []) | ||
) | ||
|
||
|
||
@property | ||
def enabled_media_servers(self) -> list[str]: | ||
return ( | ||
(['Emby'] if self.use_emby else []) | ||
+ (['Plex'] if self.use_plex else []) | ||
) | ||
|
||
|
||
@staticmethod | ||
def get_filesize(value: int, unit: str) -> int: | ||
return value * { | ||
'B': 1, 'Bytes': 1, | ||
'KB': 2**10, 'Kilobytes': 2**10, | ||
'MB': 2**20, 'Megabytes': 2**20, | ||
'GB': 2**30, 'Gigabytes': 2**30, | ||
'TB': 2**40, 'Terabytes': 2**40, | ||
}[unit] |
Oops, something went wrong.