diff --git a/media_manager/auth/config.py b/media_manager/auth/config.py index 16cbabd..c1445f9 100644 --- a/media_manager/auth/config.py +++ b/media_manager/auth/config.py @@ -1,4 +1,4 @@ -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_settings import BaseSettings from pydantic import Field import secrets @@ -6,7 +6,6 @@ import secrets class AuthConfig(BaseSettings): # to get a signing key run: # openssl rand -hex 32 - model_config = SettingsConfigDict(env_prefix="AUTH_") token_secret: str = Field(default_factory=secrets.token_hex) session_lifetime: int = 60 * 60 * 24 admin_email: list[str] = [] @@ -18,7 +17,6 @@ class AuthConfig(BaseSettings): class OpenIdConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="OPENID_") client_id: str client_secret: str configuration_endpoint: str diff --git a/media_manager/auth/users.py b/media_manager/auth/users.py index c9f37f3..e215522 100644 --- a/media_manager/auth/users.py +++ b/media_manager/auth/users.py @@ -20,8 +20,7 @@ import media_manager.notification.utils from media_manager.auth.config import AuthConfig, OpenIdConfig from media_manager.auth.db import User, get_user_db from media_manager.auth.schemas import UserUpdate -from media_manager.config import BasicConfig - +from media_manager.config import AllEncompassingConfig log = logging.getLogger(__name__) @@ -59,7 +58,7 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]): async def on_after_forgot_password( self, user: User, token: str, request: Optional[Request] = None ): - link = f"{BasicConfig().FRONTEND_URL}login/reset-password?token={token}" + link = f"{AllEncompassingConfig().misc.FRONTEND_URL}login/reset-password?token={token}" log.info(f"User {user.id} has forgot their password. Reset Link: {link}") if not config.email_password_resets: @@ -115,7 +114,7 @@ def get_jwt_strategy() -> JWTStrategy[models.UP, models.ID]: class RedirectingCookieTransport(CookieTransport): async def get_login_response(self, token: str) -> Response: response = RedirectResponse( - str(BasicConfig().FRONTEND_URL) + "dashboard", + str(AllEncompassingConfig().misc.FRONTEND_URL) + "dashboard", status_code=status.HTTP_302_FOUND, ) return self._set_login_cookie(response, token) diff --git a/media_manager/config.py b/media_manager/config.py index 28c49cf..0211896 100644 --- a/media_manager/config.py +++ b/media_manager/config.py @@ -1,7 +1,17 @@ from pathlib import Path from pydantic import AnyHttpUrl -from pydantic_settings import BaseSettings +from pydantic_settings import ( + BaseSettings, + SettingsConfigDict, +) + +from media_manager.auth.config import AuthConfig +from media_manager.database import DbConfig +from media_manager.indexer.config import IndexerConfig +from media_manager.metadataProvider.config import MetadataProviderConfig +from media_manager.notification.config import NotificationConfig +from media_manager.torrent.config import TorrentConfig class BasicConfig(BaseSettings): @@ -14,3 +24,19 @@ class BasicConfig(BaseSettings): FRONTEND_URL: AnyHttpUrl = "http://localhost:3000/" CORS_URLS: list[str] = [] DEVELOPMENT: bool = False + +class AllEncompassingConfig(BaseSettings): + model_config = SettingsConfigDict(toml_file="config.toml") + """ + This class is used to load all configurations from the environment variables. + It combines the BasicConfig with any additional configurations needed. + """ + misc: BasicConfig = BasicConfig() + torrents: TorrentConfig = TorrentConfig() + notifications: NotificationConfig = NotificationConfig() + metadata: MetadataProviderConfig = MetadataProviderConfig() + indexers: IndexerConfig = IndexerConfig() + database: DbConfig = DbConfig() + auth: AuthConfig = AuthConfig() + + diff --git a/media_manager/database/config.py b/media_manager/database/config.py index 5a9cb04..ea17783 100644 --- a/media_manager/database/config.py +++ b/media_manager/database/config.py @@ -1,8 +1,7 @@ -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_settings import BaseSettings class DbConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="DB_") HOST: str = "localhost" PORT: int = 5432 USER: str = "MediaManager" diff --git a/media_manager/indexer/config.py b/media_manager/indexer/config.py index 1635da8..c82484c 100644 --- a/media_manager/indexer/config.py +++ b/media_manager/indexer/config.py @@ -1,16 +1,18 @@ -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_settings import BaseSettings class ProwlarrConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="PROWLARR_") enabled: bool | None = False api_key: str | None = None url: str = "http://localhost:9696" class JackettConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="JACKETT_") enabled: bool | None = False api_key: str | None = None url: str = "http://localhost:9696" indexers: list[str] = ["all"] + +class IndexerConfig(BaseSettings): + prowlarr: ProwlarrConfig = ProwlarrConfig() + jackett: JackettConfig = JackettConfig() diff --git a/media_manager/main.py b/media_manager/main.py index 713d104..f5f84d4 100644 --- a/media_manager/main.py +++ b/media_manager/main.py @@ -50,7 +50,7 @@ logging.basicConfig( log = logging.getLogger(__name__) from media_manager.database import init_db # noqa: E402 -from media_manager.config import BasicConfig # noqa: E402 +from media_manager.config import AllEncompassingConfig # noqa: E402 import media_manager.torrent.router as torrent_router # noqa: E402 import media_manager.movies.router as movies_router # noqa: E402 import media_manager.tv.router as tv_router # noqa: E402 @@ -100,7 +100,7 @@ from apscheduler.triggers.cron import CronTrigger # noqa: E402 init_db() log.info("Database initialized") -basic_config = BasicConfig() +basic_config = AllEncompassingConfig().misc if basic_config.DEVELOPMENT: basic_config.torrent_directory.mkdir(parents=True, exist_ok=True) basic_config.tv_directory.mkdir(parents=True, exist_ok=True) diff --git a/media_manager/metadataProvider/abstractMetaDataProvider.py b/media_manager/metadataProvider/abstractMetaDataProvider.py index 060a167..cd8b661 100644 --- a/media_manager/metadataProvider/abstractMetaDataProvider.py +++ b/media_manager/metadataProvider/abstractMetaDataProvider.py @@ -1,16 +1,16 @@ import logging from abc import ABC, abstractmethod -import media_manager.config from media_manager.metadataProvider.schemas import MetaDataProviderSearchResult from media_manager.tv.schemas import Show from media_manager.movies.schemas import Movie +from media_manager.config import AllEncompassingConfig log = logging.getLogger(__name__) class AbstractMetadataProvider(ABC): - storage_path = media_manager.config.BasicConfig().image_directory + storage_path = AllEncompassingConfig().misc.image_directory @property @abstractmethod diff --git a/media_manager/metadataProvider/config.py b/media_manager/metadataProvider/config.py new file mode 100644 index 0000000..a2c7e1f --- /dev/null +++ b/media_manager/metadataProvider/config.py @@ -0,0 +1,9 @@ +from pydantic_settings import BaseSettings + +from media_manager.metadataProvider.tmdb import TmdbConfig +from media_manager.metadataProvider.tvdb import TvdbConfig + + +class MetadataProviderConfig(BaseSettings): + tvdb: TvdbConfig = TvdbConfig() + tmdb: TmdbConfig = TmdbConfig() \ No newline at end of file diff --git a/media_manager/movies/service.py b/media_manager/movies/service.py index 829f66b..0d9eb49 100644 --- a/media_manager/movies/service.py +++ b/media_manager/movies/service.py @@ -3,6 +3,7 @@ import re from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session +from media_manager.config import AllEncompassingConfig from media_manager.exceptions import InvalidConfigError from media_manager.indexer.repository import IndexerRepository from media_manager.database import SessionLocal @@ -28,7 +29,6 @@ from media_manager.torrent.schemas import QualityStrings from media_manager.movies.repository import MovieRepository from media_manager.exceptions import NotFoundError import pprint -from media_manager.config import BasicConfig from media_manager.torrent.repository import TorrentRepository from media_manager.torrent.utils import import_file, import_torrent from media_manager.indexer.service import IndexerService @@ -464,7 +464,7 @@ class MovieService: ) movie_file_path = ( - BasicConfig().movie_directory + AllEncompassingConfig().misc.movie_directory / f"{movie.name} ({movie.year}) [{movie.metadata_provider}id-{movie.external_id}]" ) movie_files: list[MovieFile] = self.torrent_service.get_movie_files_of_torrent( diff --git a/media_manager/notification/config.py b/media_manager/notification/config.py index c7acd2e..b95c06f 100644 --- a/media_manager/notification/config.py +++ b/media_manager/notification/config.py @@ -1,8 +1,7 @@ -from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic_settings import BaseSettings class EmailConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="EMAIL_") smtp_host: str smtp_port: int smtp_user: str @@ -12,8 +11,7 @@ class EmailConfig(BaseSettings): class NotificationConfig(BaseSettings): - model_config = SettingsConfigDict(env_prefix="NOTIFICATION_") - + smtp_config: EmailConfig = EmailConfig() email: str | None = None # the email address to send notifications to ntfy_url: str | None = ( diff --git a/media_manager/torrent/config.py b/media_manager/torrent/config.py new file mode 100644 index 0000000..d4ded4b --- /dev/null +++ b/media_manager/torrent/config.py @@ -0,0 +1,9 @@ +from pydantic_settings import BaseSettings + +from media_manager.torrent.download_clients.qbittorrent import QbittorrentConfig +from media_manager.torrent.download_clients.sabnzbd import SabnzbdConfig + + +class TorrentConfig(BaseSettings): + qbittorrent: QbittorrentConfig = QbittorrentConfig() + sabnzbd: SabnzbdConfig = SabnzbdConfig() \ No newline at end of file diff --git a/media_manager/torrent/download_clients/qbittorrent.py b/media_manager/torrent/download_clients/qbittorrent.py index 86090b7..d1d1589 100644 --- a/media_manager/torrent/download_clients/qbittorrent.py +++ b/media_manager/torrent/download_clients/qbittorrent.py @@ -6,7 +6,7 @@ import qbittorrentapi import requests from pydantic_settings import BaseSettings, SettingsConfigDict -from media_manager.config import BasicConfig +from media_manager.config import AllEncompassingConfig from media_manager.indexer.schemas import IndexerQueryResult from media_manager.torrent.download_clients.abstractDownloadClient import ( AbstractDownloadClient, @@ -69,7 +69,7 @@ class QbittorrentDownloadClient(AbstractDownloadClient): log.info(f"Attempting to download torrent: {indexer_result.title}") torrent_filepath = ( - BasicConfig().torrent_directory / f"{indexer_result.title}.torrent" + AllEncompassingConfig().misc.torrent_directory / f"{indexer_result.title}.torrent" ) if torrent_filepath.exists(): diff --git a/media_manager/torrent/utils.py b/media_manager/torrent/utils.py index afb16d5..23ea0cf 100644 --- a/media_manager/torrent/utils.py +++ b/media_manager/torrent/utils.py @@ -4,7 +4,7 @@ from pathlib import Path import shutil import patoolib -from media_manager.config import BasicConfig +from media_manager.config import AllEncompassingConfig from media_manager.torrent.schemas import Torrent log = logging.getLogger(__name__) @@ -53,7 +53,7 @@ def extract_archives(files): def get_torrent_filepath(torrent: Torrent): - return BasicConfig().torrent_directory / torrent.title + return AllEncompassingConfig().misc.torrent_directory / torrent.title def import_file(target_file: Path, source_file: Path): diff --git a/media_manager/tv/service.py b/media_manager/tv/service.py index d927ef5..41de593 100644 --- a/media_manager/tv/service.py +++ b/media_manager/tv/service.py @@ -3,6 +3,7 @@ import re from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session +from media_manager.config import AllEncompassingConfig from media_manager.exceptions import InvalidConfigError from media_manager.indexer.repository import IndexerRepository from media_manager.database import SessionLocal @@ -35,7 +36,6 @@ from media_manager.tv.repository import TvRepository from media_manager.exceptions import NotFoundError import pprint from pathlib import Path -from media_manager.config import BasicConfig from media_manager.torrent.repository import TorrentRepository from media_manager.torrent.utils import import_file, import_torrent from media_manager.indexer.service import IndexerService @@ -502,7 +502,7 @@ class TvService: ) show_file_path = ( - BasicConfig().tv_directory + AllEncompassingConfig().misc.tv_directory / f"{show.name} ({show.year}) [{show.metadata_provider}id-{show.external_id}]" ) season_files = self.torrent_service.get_season_files_of_torrent(torrent=torrent)