Source code for musicdb.lib.cfg.musicdb

# MusicDB,  a music manager with web-bases UI that focus on music.
# Copyright (C) 2017 - 2021  Ralf Stemmer <ralf.stemmer@gmx.net>
# 
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""
All sections and options can be accessed by their names: ``MusicDBConfig(...).section.option``.

Details of the configurations possible for MusicDB can be found in :doc:`/basics/config`.

To create a new entry for the MusicDB Configuration the following steps must be done:

    #. Set the section and option in the configuration file as well as in the template in the share directory
    #. In case a new section shall be created, create an empty class for this section inside this module
    #. Read the option in the constructor of the :class:`~musicdb.lib.cfg.musicdb.MusicDBConfig` class

For example, the following option shall be added:

    .. code-block:: ini

        [newmod]
        enable = True

The option must be read inside the :class:`~musicdb.lib.cfg.musicdb.MusicDBConfig` ``__init__`` method.
The read value must be written into an attribute named as the option, inside an instance of the dummy class named liked the section:

    .. code-block:: python

        self.newmod = SECTION()
        self.newmod.enable = self.Get(bool, "newmod", "enable", False)

Now, the new option is available in the configuration object created using this class:

    .. code-block:: python

        cfg = MusicDBConfig("musicdb.ini")
        if cfg.newmod.enable:
            print("newmod enabled!")

"""

import logging
import grp
import pwd
import stat
from musicdb.lib.cfg.config import Config
from musicdb.lib.filesystem import Filesystem

class META:
    pass
class SERVER:
    pass
class WEBSOCKET:
    pass
class SOCKET:
    pass
class TLS:
    pass
class DATABASE:
    pass
class MUSIC:
    pass
class ARTWORK:
    pass
class VIDEOFRAMES:
    pass
class UPLOAD:
    pass
class EXTERN:
    pass
class TRACKER:
    pass
class ICECAST:
    pass
class LOG:
    pass
class DEBUG:
    pass
class RANDY:
    pass


class SECTION:
    pass

[docs]class MusicDBConfig(Config): """ This class provides the access to the MusicDB configuration file. By default, the configuration will be read from ``/etc/musicdb.ini``. Args: path (str): Optional alternative configuration path. Default is ``/etc/musicdb.ini`` """ def __init__(self, path="/etc/musicdb.ini"): Config.__init__(self, path) self.fs = Filesystem("/") logging.info("Reading and checking MusicDB Configuration at \033[0;36m%s", path) # [meta] self.meta = META() self.meta.version = self.Get(int, "meta", "version", 1) if self.meta.version < 6: logging.warning("Version of \"%s\" is too old. Please update the MusicDB Configuration!", path) # [musicdb] self.musicdb = SECTION() self.musicdb.username = self.Get(str, "musicdb", "username", "musicdb") self.musicdb.groupname = self.Get(str, "musicdb", "groupname", "musicdb") try: pwd.getpwnam(self.musicdb.username) except KeyError: logging.warning("The user name \"%s\" for [musicdb]->username is not an existing UNIX user!", self.musicdb.username) try: grp.getgrnam(self.musicdb.groupname) except KeyError: logging.warning("The group name \"%s\" for [musicdb]->groupname is not an existing UNIX group!", self.musicdb.groupname) # [directories] self.directories = SECTION() self.directories.music = self.GetDirectory("directories", "music", "/var/music") self.directories.data = self.GetDirectory("directories", "data", "/var/lib/musicdb") self.directories.webdata = self.directories.data + "/webdata" self.directories.uploads = self.directories.data + "/uploads" self.directories.tasks = self.directories.data + "/tasks" self.directories.state = self.directories.data + "/state" self.directories.config = self.directories.data + "/config" self.directories.share = "/usr/share/musicdb" self.directories.artwork = self.directories.webdata + "/artwork" # files self.files = SECTION() self.files.webuiconfig = self.directories.config + "/webui.ini" self.files.wsapikey = self.directories.config + "/wsapikey.txt" self.files.musicdatabase = self.directories.data + "/music.db" self.files.trackerdatabase = self.directories.data + "/tracker.db" self.files.defaultalbumcover= self.directories.artwork + "/default.jpg" self.files.webuijsconfig = self.directories.webdata + "/config.js" # [log] self.log = SECTION() self.log.logfile = self.Get(str, "log", "logfile", "journal") self.log.loglevel = self.Get(str, "log", "loglevel", "WARNING").upper() if not self.log.loglevel in ["DEBUG", "INFO", "WARNING", "ERROR"]: logging.error("Invalid loglevel for [log]->loglevel. Loglevel must be one of the following: DEBUG, INFO, WARNING, ERROR") self.log.debugfile = self.Get(str, "log", "debugfile", None) if self.log.debugfile == "/dev/null": self.log.debugfile = None self.log.ignore = self.Get(str, "log", "ignore", None, islist=True) # [debug] self.debug = SECTION() self.debug.disablestats = self.Get(bool, "debug", "disablestats", False) self.debug.disabletracker = self.Get(bool, "debug", "disabletracker", False) self.debug.disableai = self.Get(bool, "debug", "disableai", True) self.debug.disabletagging = self.Get(bool, "debug", "disabletagging", False) self.debug.disableicecast = self.Get(bool, "debug", "disableicecast", False) self.debug.disablevideos = self.Get(bool, "debug", "disablevideos", False) # [websocket] self.websocket = SECTION() self.websocket.bind = self.Get(str, "websocket", "bind", "127.0.0.1") self.websocket.port = self.Get(int, "websocket", "port", 9000) self.websocket.opentimeout = self.Get(int, "websocket", "opentimeout", 10) self.websocket.closetimeout = self.Get(int, "websocket", "closetimeout", 5) self.websocket.cert = self.Get(str, "websocket", "cert", self.directories.data + "websocket.cert") self.websocket.key = self.Get(str, "websocket", "key", self.directories.data + "websocket.key") # The certificate and key files are validated in detail when MusicDB starts. No need to check them here. # [uploads] self.uploads = SECTION() self.uploads.allow = self.Get(str, "uploads", "allow", "artwork, songs", islist=True) # [tracker] self.tracker = SECTION() self.tracker.cuttime = self.Get(int, "tracker", "cuttime", 30) self.tracker.trackrandom = self.Get(bool, "tracker", "trackrandom", False) # [Icecast] self.icecast = SECTION() self.icecast.port = self.Get(int, "Icecast", "port", 6666) self.icecast.user = self.Get(str, "Icecast", "user", "source") self.icecast.password = self.Get(str, "Icecast", "password", None) self.icecast.mountname = self.Get(str, "Icecast", "mountname", "/stream") # [randy] self.randy = SECTION() self.randy.nodisabled = self.Get(bool, "randy", "nodisabled", True) self.randy.nohated = self.Get(bool, "randy", "nohated", True) self.randy.nohidden = self.Get(bool, "randy", "nohidden", True) self.randy.nobadfile = self.Get(bool, "randy", "nobadfile", True) self.randy.nolivemusic = self.Get(bool, "randy", "nolivemusic", True) self.randy.minsonglen = self.Get(int, "randy", "minsonglen", 120) self.randy.maxsonglen = self.Get(int, "randy", "maxsonglen", 600) self.randy.songbllen = self.Get(int, "randy", "songbllen", 50) self.randy.albumbllen = self.Get(int, "randy", "albumbllen", 20) self.randy.artistbllen = self.Get(int, "randy", "artistbllen", 10) self.randy.videobllen = self.Get(int, "randy", "videobllen", 10) self.randy.maxblage = self.Get(int, "randy", "maxblage", 24) self.randy.maxtries = self.Get(int, "randy", "maxtries", 10) # [music] self.music = SECTION() ignorelist = self.Get(str, "music", "ignoreartists","lost+found") ignorelist = ignorelist.split("/") self.music.ignoreartists = [item.strip() for item in ignorelist] ignorelist = self.Get(str, "music", "ignorealbums", "") ignorelist = ignorelist.split("/") self.music.ignorealbums = [item.strip() for item in ignorelist] ignorelist = self.Get(str, "music", "ignoresongs", ".directory / desktop.ini / Desktop.ini / .DS_Store / Thumbs.db") ignorelist = ignorelist.split("/") self.music.ignoresongs = [item.strip() for item in ignorelist] # [albumcover] self.albumcover = SECTION() self.albumcover.scales = self.Get(int, "albumcover", "scales", "50, 150, 500", islist=True) # [videoframes] self.videoframes = SECTION() self.videoframes.frames = self.Get(int, "videoframes", "frames", "5") self.videoframes.previewlength = self.Get(int, "videoframes", "previewlength","3") self.videoframes.scales = self.Get(str, "videoframes", "scales", "50x27, 150x83", islist=True) for scale in self.videoframes.scales: try: width, height = map(int, scale.split("x")) except Exception as e: logging.error("Invalid video scale format in [videoframes]->scales: Expected format WxH, with W and H as integers. Actual format: %s.", scale) # [extern] self.extern = SECTION() self.extern.configtemplate = self.GetFile( "extern", "configtemplate","/usr/share/musicdb/extconfig.ini") self.extern.statedir = self.Get(str, "extern", "statedir", ".mdbstate") self.extern.configfile = self.Get(str, "extern", "configfile", "config.ini") self.extern.songmap = self.Get(str, "extern", "songmap", "songmap.csv")
[docs] def GetDirectory(self, section, option, default): """ This method gets a string from the configuration file and checks if it is an existing directory. If not it prints a warning. The \"invalid\" path will be returned anyway, because it may be OK that the directory does not exist yet. Args: section (str): Section of an ini-file option (str): Option inside the section of an ini-file default (str): Default directory path if option is not set in the file Returns: The value of the option set in the config-file or the default value. """ path = self.Get(str, section, option, default) if not self.fs.IsDirectory(path): logging.warning("Value of [%s]->%s=%s does not address an existing directory.", section, option, path) return path
[docs] def GetFile(self, section, option, default): """ This method gets a string from the configuration file and checks if it is an existing file. If not it prints a warning. The \"invalid\" path will be returned anyway, because it may be OK that the file does not exist yet. Args: section (str): Section of an ini-file option (str): Option inside the section of an ini-file default (str): Default file path if option is not set in the file Returns: The value of the option set in the config-file or the default value. """ path = self.Get(str, section, option, default) if not self.fs.IsFile(path): logging.warning("Value of [%s]->%s=%s does not address an existing file.", section, option, path) return path
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4