maloja/maloja/pkg_global/conf.py

340 lines
14 KiB
Python
Raw Permalink Normal View History

2019-12-14 15:46:02 +03:00
import os
from doreah.configuration import Configuration
from doreah.configuration import types as tp
2019-12-12 23:24:13 +03:00
from ..__pkginfo__ import VERSION
2021-12-24 23:23:02 +03:00
2021-12-21 08:55:35 +03:00
2022-01-03 04:08:02 +03:00
2021-12-21 08:55:35 +03:00
# if DATA_DIRECTORY is specified, this is the directory to use for EVERYTHING, no matter what
# but with asynnetrical structure, cache and logs in subfolders
# otherwise, each directory is treated seperately
# in that case, individual settings for each are respected
# DIRECRORY_CONFIG, DIRECRORY_STATE, DIRECTORY_LOGS and DIRECTORY_CACHE
# config can only be determined by environment variable, the others can be loaded
# from the config files
# explicit settings will always be respected, fallback to default
# if default isn't usable, and config writable, find alternative and fix it in settings
2021-12-21 08:55:35 +03:00
# USEFUL FUNCS
pthj = os.path.join
def is_dir_usable(pth):
try:
os.makedirs(pth,exist_ok=True)
os.mknod(pthj(pth,".test"))
os.remove(pthj(pth,".test"))
return True
2022-04-24 20:41:55 +03:00
except Exception:
return False
def get_env_vars(key,pathsuffix=[]):
return [pthj(pth,*pathsuffix) for pth in os.environ.get(key,'').split(':') if pth != '']
2021-12-21 08:55:35 +03:00
directory_info = {
"config":{
2021-12-21 09:30:38 +03:00
"sentinel":"rules",
2021-12-21 08:55:35 +03:00
"possible_folders":[
"/etc/maloja",
os.path.expanduser("~/.local/share/maloja")
],
"setting":"directory_config"
},
"cache":{
"sentinel":"dummy",
"possible_folders":[
"/var/cache/maloja",
os.path.expanduser("~/.local/share/maloja/cache")
],
"setting":"directory_cache"
},
"state":{
"sentinel":"scrobbles",
"possible_folders":[
"/var/lib/maloja",
os.path.expanduser("~/.local/share/maloja")
],
"setting":"directory_state"
},
"logs":{
"sentinel":"dbfix",
"possible_folders":[
"/var/log/maloja",
os.path.expanduser("~/.local/share/maloja/logs")
],
"setting":"directory_logs"
}
}
# function that
2021-12-23 09:24:24 +03:00
# checks if one has been in use before and writes it to dict/config
# if not, determines which to use and writes it to dict/config
2021-12-21 08:55:35 +03:00
# returns determined folder
def find_good_folder(datatype,configobject):
info = directory_info[datatype]
# check each possible folder if its used
for p in info['possible_folders']:
if os.path.exists(pthj(p,info['sentinel'])):
2021-12-21 09:30:38 +03:00
#print(p,"has been determined as maloja's folder for",datatype)
2021-12-21 08:55:35 +03:00
configobject[info['setting']] = p
return p
2021-12-21 09:30:38 +03:00
#print("Could not find previous",datatype,"folder")
2021-12-21 08:55:35 +03:00
# check which one we can use
for p in info['possible_folders']:
if is_dir_usable(p):
2021-12-21 09:30:38 +03:00
#print(p,"has been selected as maloja's folder for",datatype)
2021-12-21 08:55:35 +03:00
configobject[info['setting']] = p
return p
2021-12-21 09:30:38 +03:00
#print("No folder can be used for",datatype)
#print("This should not happen!")
### STEP 1 - find out where the settings file is
# environment variables
maloja_dir_config = os.environ.get("MALOJA_DATA_DIRECTORY") or os.environ.get("MALOJA_DIRECTORY_CONFIG")
2021-12-21 08:55:35 +03:00
if maloja_dir_config is None:
2021-12-21 08:55:35 +03:00
maloja_dir_config = find_good_folder('config',{})
found_new_config_dir = True
else:
found_new_config_dir = False
# remember whether we had to find our config dir or it was user-specified
2021-12-23 09:24:24 +03:00
os.makedirs(maloja_dir_config,exist_ok=True)
oldsettingsfile = pthj(maloja_dir_config,"settings","settings.ini")
newsettingsfile = pthj(maloja_dir_config,"settings.ini")
2021-12-23 09:24:24 +03:00
if os.path.exists(oldsettingsfile):
os.rename(oldsettingsfile,newsettingsfile)
### STEP 2 - create settings object
malojaconfig = Configuration(
settings={
"Setup":{
"data_directory":(tp.String(), "Data Directory", None, "Folder for all user data. Overwrites all choices for specific directories."),
"directory_config":(tp.String(), "Config Directory", "/etc/maloja", "Folder for config data. Only applied when global data directory is not set."),
"directory_state":(tp.String(), "State Directory", "/var/lib/maloja", "Folder for state data. Only applied when global data directory is not set."),
"directory_logs":(tp.String(), "Log Directory", "/var/log/maloja", "Folder for log data. Only applied when global data directory is not set."),
"directory_cache":(tp.String(), "Cache Directory", "/var/cache/maloja", "Folder for cache data. Only applied when global data directory is not set."),
"skip_setup":(tp.Boolean(), "Skip Setup", False, "Make server setup process non-interactive. Vital for Docker."),
"force_password":(tp.String(), "Force Password", None, "On startup, overwrite admin password with this one. This should usually only be done via environment variable in Docker."),
"clean_output":(tp.Boolean(), "Avoid Mutable Console Output", False, "Use if console output will be redirected e.g. to a web interface.")
},
"Debug":{
"logging":(tp.Boolean(), "Enable Logging", True),
"dev_mode":(tp.Boolean(), "Enable developer mode", False),
},
"Network":{
2022-04-14 21:49:40 +03:00
"host":(tp.String(), "Host", "*", "Host for your server, e.g. '*' for dual stack, '::' for IPv6 or '0.0.0.0' for IPv4"),
"port":(tp.Integer(), "Port", 42010),
},
"Technical":{
2022-02-27 02:34:06 +03:00
"cache_expire_positive":(tp.Integer(), "Image Cache Expiration", 60, "Days until images are refetched"),
"cache_expire_negative":(tp.Integer(), "Image Cache Negative Expiration", 5, "Days until failed image fetches are reattempted"),
2022-04-25 05:28:53 +03:00
"db_max_memory":(tp.Integer(min=0,max=100), "RAM Percentage soft limit", 50, "RAM Usage in percent at which Maloja should no longer increase its database cache."),
2022-04-24 21:55:07 +03:00
"use_request_cache":(tp.Boolean(), "Use request-local DB Cache", False),
2022-04-25 04:24:16 +03:00
"use_global_cache":(tp.Boolean(), "Use global DB Cache", True)
},
"Fluff":{
"scrobbles_gold":(tp.Integer(), "Scrobbles for Gold", 250, "How many scrobbles a track needs to be considered 'Gold' status"),
"scrobbles_platinum":(tp.Integer(), "Scrobbles for Platinum", 500, "How many scrobbles a track needs to be considered 'Platinum' status"),
"scrobbles_diamond":(tp.Integer(), "Scrobbles for Diamond", 1000, "How many scrobbles a track needs to be considered 'Diamond' status"),
"name":(tp.String(), "Name", "Generic Maloja User")
},
"Third Party Services":{
"metadata_providers":(tp.List(tp.String()), "Metadata Providers", ['lastfm','spotify','deezer','musicbrainz'], "Which metadata providers should be used in what order. Musicbrainz is rate-limited and should not be used first."),
"scrobble_lastfm":(tp.Boolean(), "Proxy-Scrobble to Last.fm", False),
"lastfm_api_key":(tp.String(), "Last.fm API Key", None),
"lastfm_api_secret":(tp.String(), "Last.fm API Secret", None),
2021-12-27 20:32:33 +03:00
"lastfm_api_sk":(tp.String(), "Last.fm API Session Key", None),
"lastfm_username":(tp.String(), "Last.fm Username", None),
"lastfm_password":(tp.String(), "Last.fm Password", None),
"spotify_api_id":(tp.String(), "Spotify API ID", None),
"spotify_api_secret":(tp.String(), "Spotify API Secret", None),
"audiodb_api_key":(tp.String(), "TheAudioDB API Key", None),
"other_maloja_url":(tp.String(), "Other Maloja Instance URL", None),
"other_maloja_api_key":(tp.String(), "Other Maloja Instance API Key",None),
"track_search_provider":(tp.String(), "Track Search Provider", None),
"send_stats":(tp.Boolean(), "Send Statistics", None),
2022-03-26 07:49:30 +03:00
"proxy_images":(tp.Boolean(), "Image Proxy", True, "Whether third party images should be downloaded and served directly by Maloja (instead of just linking their URL)")
},
"Database":{
"invalid_artists":(tp.Set(tp.String()), "Invalid Artists", ["[Unknown Artist]","Unknown Artist","Spotify"], "Artists that should be discarded immediately"),
"remove_from_title":(tp.Set(tp.String()), "Remove from Title", ["(Original Mix)","(Radio Edit)","(Album Version)","(Explicit Version)","(Bonus Track)"], "Phrases that should be removed from song titles"),
"delimiters_feat":(tp.Set(tp.String()), "Featuring Delimiters", ["ft.","ft","feat.","feat","featuring"], "Delimiters used for extra artists, even when in the title field"),
"delimiters_informal":(tp.Set(tp.String()), "Informal Delimiters", ["vs.","vs","&"], "Delimiters in informal artist strings with spaces expected around them"),
"delimiters_formal":(tp.Set(tp.String()), "Formal Delimiters", [";","/","|","","",""], "Delimiters used to tag multiple artists when only one tag field is available"),
"filters_remix":(tp.Set(tp.String()), "Remix Filters", ["Remix", "Remix Edit", "Short Mix", "Extended Mix", "Soundtrack Version"], "Filters used to recognize the remix artists in the title"),
"parse_remix_artists":(tp.Boolean(), "Parse Remix Artists", False)
},
"Web Interface":{
"default_range_charts_artists":(tp.Choice({'alltime':'All Time','year':'Year','month':"Month",'week':'Week'}), "Default Range Artist Charts", "year"),
"default_range_charts_tracks":(tp.Choice({'alltime':'All Time','year':'Year','month':"Month",'week':'Week'}), "Default Range Track Charts", "year"),
"default_step_pulse":(tp.Choice({'year':'Year','month':"Month",'week':'Week','day':'Day'}), "Default Pulse Step", "month"),
"charts_display_tiles":(tp.Boolean(), "Display Chart Tiles", False),
"display_art_icons":(tp.Boolean(), "Display Album/Artist Icons", True),
"discourage_cpu_heavy_stats":(tp.Boolean(), "Discourage CPU-heavy stats", False, "Prevent visitors from mindlessly clicking on CPU-heavy options. Does not actually disable them for malicious actors!"),
"use_local_images":(tp.Boolean(), "Use Local Images", True),
2022-02-18 07:31:08 +03:00
#"local_image_rotate":(tp.Integer(), "Local Image Rotate", 3600),
"timezone":(tp.Integer(), "UTC Offset", 0),
"time_format":(tp.String(), "Time Format", "%d. %b %Y %I:%M %p"),
"theme":(tp.String(), "Theme", "maloja")
}
},
configfile=newsettingsfile,
save_endpoint="/apis/mlj_1/settings",
2022-03-06 04:00:48 +03:00
env_prefix="MALOJA_",
extra_files=["/run/secrets/maloja.yml","/run/secrets/maloja.ini"]
)
2021-12-21 08:55:35 +03:00
if found_new_config_dir:
try:
malojaconfig["DIRECTORY_CONFIG"] = maloja_dir_config
except PermissionError as e:
pass
2021-12-21 08:55:35 +03:00
# this really doesn't matter because when are we gonna load info about where
# the settings file is stored from the settings file
# but oh well
try:
malojaconfig.render_help(pthj(maloja_dir_config,"settings.md"),
top_text='''If you wish to adjust settings in the settings.ini file, do so while the server
is not running in order to avoid data being overwritten.
2021-12-21 08:55:35 +03:00
Technically, each setting can be set via environment variable or the settings
file - simply add the prefix `MALOJA_` for environment variables. It is recommended
to use the settings file where possible and not configure each aspect of your
server via environment variables!
2022-03-06 04:00:48 +03:00
You also can specify additional settings in the files`/run/secrets/maloja.yml` or
`/run/secrets/maloja.ini`, as well as their values directly in files of the respective
name in `/run/secrets/` (e.g. `/run/secrets/lastfm_api_key`).''')
except PermissionError as e:
pass
### STEP 3 - check all possible folders for files (old installation)
if not malojaconfig.readonly:
for datatype in ("state","cache","logs"):
# obviously default values shouldn't trigger this
# if user has nothing specified, we need to use this
if malojaconfig.get_specified(directory_info[datatype]['setting']) is None and malojaconfig.get_specified('DATA_DIRECTORY') is None:
find_good_folder(datatype,malojaconfig)
2021-12-21 08:55:35 +03:00
2021-12-24 09:29:59 +03:00
2021-12-23 09:24:24 +03:00
2021-12-21 08:55:35 +03:00
### STEP 4 - this is where all the guessing about previous installation ends
### we have our definite settings and are now just generating the real
### folder names for everything
if malojaconfig['DATA_DIRECTORY'] is None:
2021-12-21 08:55:35 +03:00
dir_settings = {
"config":malojaconfig['DIRECTORY_CONFIG'],
"state":malojaconfig['DIRECTORY_STATE'],
"cache":malojaconfig['DIRECTORY_CACHE'],
"logs":malojaconfig['DIRECTORY_LOGS'],
}
else:
2021-12-21 08:55:35 +03:00
dir_settings = {
"config":malojaconfig['DATA_DIRECTORY'],
"state":malojaconfig['DATA_DIRECTORY'],
"cache":pthj(malojaconfig['DATA_DIRECTORY'],"cache"),
"logs":pthj(malojaconfig['DATA_DIRECTORY'],"logs"),
}
data_directories = {
2021-12-21 08:55:35 +03:00
"auth":pthj(dir_settings['state'],"auth"),
"backups":pthj(dir_settings['state'],"backups"),
"images":pthj(dir_settings['state'],"images"),
2022-01-03 05:04:36 +03:00
"scrobbles":pthj(dir_settings['state']),
2021-12-21 08:55:35 +03:00
"rules":pthj(dir_settings['config'],"rules"),
2021-12-25 04:22:57 +03:00
"clients":pthj(dir_settings['config']),
2021-12-21 08:55:35 +03:00
"settings":pthj(dir_settings['config']),
"css":pthj(dir_settings['config'],"custom_css"),
"config":dir_settings['config'],
"state":dir_settings['state'],
"logs":dir_settings['logs'],
"cache":dir_settings['cache'],
}
2022-04-08 20:03:12 +03:00
for identifier,path in data_directories.items():
os.makedirs(path,exist_ok=True)
data_dir = {
k:lambda *x,k=k: pthj(data_directories[k],*x) for k in data_directories
}
2021-12-24 23:23:02 +03:00
### write down the last ran version
with open(pthj(dir_settings['state'],".lastmalojaversion"),"w") as filed:
2021-12-26 23:36:36 +03:00
filed.write(VERSION)
2021-12-24 23:23:02 +03:00
filed.write("\n")
### DOREAH CONFIGURATION
from doreah import config
config(
auth={
"multiuser":False,
"cookieprefix":"maloja",
2022-04-24 16:12:49 +03:00
"stylesheets":["/maloja.css"],
2020-12-25 06:52:05 +03:00
"dbfile":data_dir['auth']("auth.ddb")
},
2020-06-13 18:34:30 +03:00
logging={
"logfolder": data_dir['logs']() if malojaconfig["LOGGING"] else None
},
regular={
"offset": malojaconfig["TIMEZONE"]
2020-06-13 18:34:30 +03:00
}
)
2021-12-21 08:55:35 +03:00
2021-12-25 04:22:57 +03:00
custom_css_files = [f for f in os.listdir(data_dir['css']()) if f.lower().endswith('.css')]
2021-12-25 04:22:57 +03:00
2021-12-21 08:55:35 +03:00
# what the fuck did i just write
# this spaghetti file is proudly sponsored by the rice crackers i'm eating at the
# moment as well as some cute chinese girl whose asmr i'm listening to in the
# background. and now to bed!