Source code for srcalchemy.config.config_load
#################################################################################
# MIT License
#
# Copyright (c) 2025 Duncan Fraser
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#################################################################################
#
# File : config_load.py
#
# Author : Duncan Fraser <dfraser@thedrunkencoder.uk>
#
# Description :
#
#################################################################################
import logging
import tomllib
from dataclasses import dataclass, field
from typing import BinaryIO, Literal
MODE = Literal["copy", "move"]
[docs]
@dataclass
class generalConfig:
"""General configuration options"""
logFile: str = "srcalchemy.log"
"""Log file location"""
dataDirectory: str = "data"
"""Directory to output collected information to"""
mode: MODE = "copy"
"""Mode of operation
Choice of:
- copy: Copy media items when processing
- move: Move media items when processing
"""
[docs]
@dataclass
class kodiConfig:
"""Kodi configuration options
Used when interacting with the Kodi JSONRPC
"""
host: str = "localhost"
"""Hostname of IP address of Kodi host to connect to"""
host_port: int = 8080
"""Kodi JSONRPC port"""
use_https: bool = False
"""Use HTTPS when connecting to Kodi host"""
ignore_ssl: bool = True
"""Ignore SSL when connecting to Kodi host"""
[docs]
@dataclass
class movieConfig:
"""Movie Configuration
Used for processing parsed movies
"""
basePath: str = ""
"""Base path of Movie Directory as defined in the Kodi Video Library"""
outputDirectory: str | None = None
"""Optional Output directory"""
parentFormat: str = "{TITLE} ({YEAR})"
"""Format map string for generating the output directory name"""
fileFormat: str = "{TITLE} [{FORMAT}] ({YEAR})"
"""Format map string for generating the output filename"""
[docs]
@dataclass
class tvConfig:
"""TV Configuration
Used for processing parsed TV and Episodes
"""
basePath: str = ""
"""Base path of TV Directory as defined in the Kodi Video Library"""
outputDirectory: str | None = None
""""""
parentFormat: str = "{TITLE}"
"""Format map string for generating the output directory name"""
seasonFormat: str = "Season {SEASON}"
"""Format map string for generating the season output directory name"""
fileFormat: str = "{SHOW_TITLE} S{SEASON:02}E{EPISODE:02}"
"""Format map string for generating the output filename"""
[docs]
@dataclass
class driveMappingConfig:
"""Drive Mapping Configuration
Used for replacing path strings for local mappings
"""
src: str
"""Source Path string to be replace"""
dest: str
"""Destination path"""
[docs]
@dataclass
class ConfigSpec:
"""Configurations Options"""
general: generalConfig = field(default_factory=generalConfig)
"""General configuration options"""
log: logging.Logger = logging.getLogger(__name__)
"""Application Logger"""
kodi: kodiConfig = field(default_factory=kodiConfig)
"""Kodi configuration options"""
movie_config: movieConfig = field(default_factory=movieConfig)
"""Movie configuration options"""
tv_config: tvConfig = field(default_factory=tvConfig)
"""TV configuration options"""
driveMapping: list[driveMappingConfig] = field(default_factory=list)
"""List of Drive mapping configurations"""
[docs]
def parseGeneralConfig(data: dict) -> generalConfig:
"""Parse General Configuration from Dictionary
:param data: Dictionary to parse
:returns: General Configuration
"""
general_config = generalConfig()
if "logFile" in data:
general_config.logFile = data["logFile"]
if "dataDirectory" in data:
general_config.dataDirectory = data["dataDirectory"]
if "mode" in data:
if data["mode"] == "copy":
general_config.mode = "copy"
elif data["mode"] == "move":
general_config.mode = "move"
return general_config
[docs]
def parseKodiConfig(data: dict) -> kodiConfig:
"""Parse Kodi Config from Dictionary
:param data: Dictionary to parse
:returns: Kodi Configuration
"""
kodi_config = kodiConfig()
if "host" in data:
kodi_config.host = str(data["host"])
if "port" in data:
kodi_config.host_port = int(data["port"])
if "use_https" in data:
kodi_config.use_https = data["use_https"] == "True"
if "ignore_ssl" in data:
kodi_config.ignore_ssl = data["ignore_ssl"] == "True"
return kodi_config
[docs]
def parseMovieConfig(data: dict) -> movieConfig:
"""Parse Movie Configuration from Dictionary
:param data: Dictionary to parse
:returns: Movie Configuration
"""
movie_config = movieConfig()
if "basePath" in data:
movie_config.basePath = data["basePath"]
if "outputDirectory" in data:
movie_config.outputDirectory = data["outputDirectory"]
if "parentFormat" in data:
movie_config.parentFormat = data["parentFormat"]
if "fileFormat" in data:
movie_config.fileFormat = data["fileFormat"]
return movie_config
[docs]
def parseTvConfig(data: dict) -> tvConfig:
"""Parse TV configuration from Dictionary
:param data: Dictionary to parse
:returns: TV Configuration
"""
tv_config = tvConfig()
if "basePath" in data:
tv_config.basePath = data["basePath"]
if "outputDirectory" in data:
tv_config.outputDirectory = data["outputDirectory"]
if "parentFormat" in data:
tv_config.parentFormat = data["parentFormat"]
if "seasonFormat" in data:
tv_config.seasonFormat = data["seasonFormat"]
if "fileFormat" in data:
tv_config.fileFormat = data["fileFormat"]
return tv_config
[docs]
def parseDriveMappingConfig(data: dict) -> driveMappingConfig:
"""Parse Drive Mapping configuration from Dictionary
:param data: Dictionary to parse
:returns: Drive mapping configuration
"""
drive_mapping = driveMappingConfig(src=data["src"], dest=data["dest"])
return drive_mapping
[docs]
def parseConfig(configIO: BinaryIO) -> ConfigSpec:
"""Parse Configuration Options from Binary I/O
:param configIO: Readable Binary I/O Object
:returns: Configuration Specification parsed from
"""
data = tomllib.load(configIO)
config = ConfigSpec()
if "general" in data:
config.general = parseGeneralConfig(data=data["general"])
if "kodi" in data:
config.kodi = parseKodiConfig(data=data["kodi"])
if "movie" in data:
config.movie_config = parseMovieConfig(data=data["movie"])
if "tv" in data:
config.tv_config = parseTvConfig(data=data["tv"])
if "drive_mapping" in data:
for mapping in data["drive_mapping"]:
drive_mapping = parseDriveMappingConfig(data=mapping)
config.driveMapping.append(drive_mapping)
return config