# -*- 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 :##################################################################################importloggingimporttomllibfromdataclassesimportdataclass,fieldfromtypingimportBinaryIO,List,Literal,OptionalMODE=Literal["copy","move"]
[docs]@dataclassclassgeneralConfig:"""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]@dataclassclasskodiConfig:"""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]@dataclassclassmovieConfig:"""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]@dataclassclasstvConfig:"""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]@dataclassclassdriveMappingConfig:"""Drive Mapping Configuration Used for replacing path strings for local mappings """src:str"""Source Path string to be replace"""dest:str"""Destination path"""
[docs]defparseGeneralConfig(data:dict)->generalConfig:"""Parse General Configuration from Dictionary :param data: Dictionary to parse :returns: General Configuration """general_config=generalConfig()if"logFile"indata:general_config.logFile=data["logFile"]if"dataDirectory"indata:general_config.dataDirectory=data["dataDirectory"]if"mode"indata:ifdata["mode"]=="copy":general_config.mode="copy"elifdata["mode"]=="move":general_config.mode="move"returngeneral_config
[docs]defparseKodiConfig(data:dict)->kodiConfig:"""Parse Kodi Config from Dictionary :param data: Dictionary to parse :returns: Kodi Configuration """kodi_config=kodiConfig()if"host"indata:kodi_config.host=str(data["host"])if"port"indata:kodi_config.host_port=int(data["port"])if"use_https"indata:kodi_config.use_https=Trueifdata["use_https"]=="True"elseFalseif"ignore_ssl"indata:kodi_config.ignore_ssl=Trueifdata["ignore_ssl"]=="True"elseFalsereturnkodi_config
[docs]defparseMovieConfig(data:dict)->movieConfig:"""Parse Movie Configuration from Dictionary :param data: Dictionary to parse :returns: Movie Configuration """movie_config=movieConfig()if"basePath"indata:movie_config.basePath=data["basePath"]if"outputDirectory"indata:movie_config.outputDirectory=data["outputDirectory"]if"parentFormat"indata:movie_config.parentFormat=data["parentFormat"]if"fileFormat"indata:movie_config.fileFormat=data["fileFormat"]returnmovie_config
[docs]defparseTvConfig(data:dict)->tvConfig:"""Parse TV configuration from Dictionary :param data: Dictionary to parse :returns: TV Configuration """tv_config=tvConfig()if"basePath"indata:tv_config.basePath=data["basePath"]if"outputDirectory"indata:tv_config.outputDirectory=data["outputDirectory"]if"parentFormat"indata:tv_config.parentFormat=data["parentFormat"]if"seasonFormat"indata:tv_config.seasonFormat=data["seasonFormat"]if"fileFormat"indata:tv_config.fileFormat=data["fileFormat"]returntv_config
[docs]defparseDriveMappingConfig(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"])returndrive_mapping
[docs]defparseConfig(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"indata:config.general=parseGeneralConfig(data=data["general"])if"kodi"indata:config.kodi=parseKodiConfig(data=data["kodi"])if"movie"indata:config.movie_config=parseMovieConfig(data=data["movie"])if"tv"indata:config.tv_config=parseTvConfig(data=data["tv"])if"drive_mapping"indata:formappingindata["drive_mapping"]:drive_mapping=parseDriveMappingConfig(data=mapping)config.driveMapping.append(drive_mapping)returnconfig