mirror of
https://github.com/Screenly/Anthias.git
synced 2026-04-18 05:29:47 -04:00
- Catch OSError in AnthiasSettings.__init__ when config directory can't be created (e.g., Docker volume permissions during startup) - Change generate-openapi-schema wait to use curl on port 8000 instead of `import django`, ensuring start_server.sh has completed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
180 lines
5.5 KiB
Python
180 lines
5.5 KiB
Python
import configparser
|
|
import hashlib
|
|
import logging
|
|
from collections import UserDict
|
|
from os import getenv, makedirs, path
|
|
from typing import Any
|
|
|
|
from lib.auth import BasicAuth, NoAuth
|
|
|
|
CONFIG_DIR = '.screenly/'
|
|
CONFIG_FILE = 'screenly.conf'
|
|
DEFAULTS = {
|
|
'main': {
|
|
'analytics_opt_out': False,
|
|
'assetdir': 'screenly_assets',
|
|
'database': CONFIG_DIR + 'screenly.db',
|
|
'date_format': 'mm/dd/yyyy',
|
|
'use_24_hour_clock': False,
|
|
'use_ssl': False,
|
|
'auth_backend': '',
|
|
'websocket_port': '9999',
|
|
'django_secret_key': '',
|
|
},
|
|
'viewer': {
|
|
'audio_output': 'hdmi',
|
|
'debug_logging': False,
|
|
'default_duration': 10,
|
|
'default_streaming_duration': '300',
|
|
'player_name': '',
|
|
'resolution': '1920x1080',
|
|
'show_splash': True,
|
|
'shuffle_playlist': False,
|
|
'verify_ssl': True,
|
|
'default_assets': False,
|
|
},
|
|
}
|
|
CONFIGURABLE_SETTINGS = DEFAULTS['viewer'].copy()
|
|
CONFIGURABLE_SETTINGS['use_24_hour_clock'] = DEFAULTS['main'][
|
|
'use_24_hour_clock'
|
|
]
|
|
CONFIGURABLE_SETTINGS['date_format'] = DEFAULTS['main']['date_format']
|
|
|
|
PORT = int(getenv('PORT', 8000))
|
|
LISTEN = getenv('LISTEN', '127.0.0.1')
|
|
|
|
# Initiate logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(message)s',
|
|
datefmt='%a, %d %b %Y %H:%M:%S',
|
|
)
|
|
|
|
# Silence urllib info messages ('Starting new HTTP connection')
|
|
# that are triggered by the remote url availability check in view_web
|
|
requests_log = logging.getLogger('requests')
|
|
requests_log.setLevel(logging.WARNING)
|
|
|
|
logging.debug('Starting viewer')
|
|
|
|
|
|
class AnthiasSettings(UserDict):
|
|
"""Anthias' Settings."""
|
|
|
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
super().__init__(*args, **kwargs)
|
|
self.home = getenv('HOME')
|
|
self.conf_file = self.get_configfile()
|
|
self.auth_backends_list = [NoAuth(), BasicAuth(self)]
|
|
self.auth_backends = {}
|
|
for backend in self.auth_backends_list:
|
|
DEFAULTS.update(backend.config)
|
|
self.auth_backends[backend.name] = backend
|
|
|
|
if not path.isfile(self.conf_file):
|
|
logging.error(
|
|
'Config-file %s missing. Using defaults.', self.conf_file
|
|
)
|
|
self.use_defaults()
|
|
try:
|
|
self.save()
|
|
except OSError as e:
|
|
logging.warning(
|
|
'Could not save config file %s: %s',
|
|
self.conf_file,
|
|
e,
|
|
)
|
|
else:
|
|
self.load()
|
|
|
|
def _get(
|
|
self,
|
|
config: configparser.ConfigParser,
|
|
section: str,
|
|
field: str,
|
|
default: Any,
|
|
) -> None:
|
|
try:
|
|
if isinstance(default, bool):
|
|
self[field] = config.getboolean(section, field)
|
|
elif isinstance(default, int):
|
|
self[field] = config.getint(section, field)
|
|
else:
|
|
self[field] = config.get(section, field)
|
|
# Likely not a hashed password
|
|
if (
|
|
field == 'password'
|
|
and self[field] != ''
|
|
and len(self[field]) != 64
|
|
):
|
|
# Hash the original password.
|
|
self[field] = hashlib.sha256(self[field]).hexdigest()
|
|
except configparser.Error as e:
|
|
logging.debug(
|
|
"Could not parse setting '%s.%s': %s. "
|
|
"Using default value: '%s'.",
|
|
section,
|
|
field,
|
|
str(e),
|
|
default,
|
|
)
|
|
self[field] = default
|
|
if field in ['database', 'assetdir']:
|
|
self[field] = path.join(self.home, self[field])
|
|
|
|
def _set(
|
|
self,
|
|
config: configparser.ConfigParser,
|
|
section: str,
|
|
field: str,
|
|
default: Any,
|
|
) -> None:
|
|
if isinstance(default, bool):
|
|
config.set(
|
|
section, field, self.get(field, default) and 'on' or 'off'
|
|
)
|
|
else:
|
|
config.set(section, field, str(self.get(field, default)))
|
|
|
|
def load(self) -> None:
|
|
"""Loads the latest settings from screenly.conf into memory."""
|
|
logging.debug('Reading config-file...')
|
|
config = configparser.ConfigParser()
|
|
config.read(self.conf_file)
|
|
|
|
for section, defaults in DEFAULTS.items():
|
|
for field, default in defaults.items():
|
|
self._get(config, section, field, default)
|
|
|
|
def use_defaults(self) -> None:
|
|
for defaults in DEFAULTS.items():
|
|
for field, default in defaults[1].items():
|
|
self[field] = default
|
|
|
|
def save(self) -> None:
|
|
# Write new settings to disk.
|
|
makedirs(path.dirname(self.conf_file), exist_ok=True)
|
|
config = configparser.ConfigParser()
|
|
for section, defaults in DEFAULTS.items():
|
|
config.add_section(section)
|
|
for field, default in defaults.items():
|
|
self._set(config, section, field, default)
|
|
with open(self.conf_file, 'w') as f:
|
|
config.write(f)
|
|
self.load()
|
|
|
|
def get_configdir(self) -> str:
|
|
return path.join(self.home, CONFIG_DIR)
|
|
|
|
def get_configfile(self) -> str:
|
|
return path.join(self.home, CONFIG_DIR, CONFIG_FILE)
|
|
|
|
@property
|
|
def auth(self):
|
|
backend_name = self['auth_backend']
|
|
if backend_name in self.auth_backends:
|
|
return self.auth_backends[self['auth_backend']]
|
|
|
|
|
|
settings = AnthiasSettings()
|