Source code for kodiRename.config.config_load

# -*- coding: utf-8 -*-
#################################################################################
# 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, List, Literal, Optional

MODE = Literal["copy", "move"]


[docs] @dataclass class generalConfig: """General configuration options""" logFile: str = "kodiRename.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: Optional[str] = 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: Optional[str] = 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=lambda: []) """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 = True if data["use_https"] == "True" else False if "ignore_ssl" in data: kodi_config.ignore_ssl = True if data["ignore_ssl"] == "True" else False 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