2019-12-14 15:46:02 +03:00
import os
2020-06-06 17:46:25 +03:00
from doreah . settings import get_settings
from doreah . settings import config as settingsconfig
2021-12-12 20:43:24 +03:00
from doreah . configuration import Configuration
from doreah . configuration import types as tp
2019-12-12 23:24:13 +03:00
2020-12-25 06:41:33 +03:00
pthj = os . path . join
# 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. if there are none:
# first check if there is any indication of one of the possibilities being populated already
# if not, use the first we have permissions for
# after we decide which to use, fix it in settings to avoid future heuristics
2020-12-25 18:52:25 +03:00
try :
HOME_DIR = os . environ [ " XDG_DATA_HOME " ] . split ( " : " ) [ 0 ]
assert os . path . exists ( HOME_DIR )
except :
HOME_DIR = os . path . join ( os . environ [ " HOME " ] , " .local/share/ " )
2020-12-25 06:41:33 +03:00
usrfol = pthj ( HOME_DIR , " maloja " )
etccfg = ' /etc/maloja '
varlib = ' /var/lib/maloja '
varcac = ' /var/cache/maloja '
varlog = ' /var/log/maloja '
dir_settings = {
" config " : None ,
" state " : None ,
" logs " : None ,
" cache " : None ,
# "clients":None,
# "rules":None,
# "settings":None,
# "auth":None,
# "backups":None,
# "images":None,
# "scrobbles":None,
# "logs":None,
# "cache":None
}
dir_options = {
" config " : [
" /etc/maloja " ,
2020-12-25 18:52:25 +03:00
usrfol
2020-12-25 06:41:33 +03:00
] ,
" state " : [
" /var/lib/maloja " ,
" /etc/maloja " ,
2020-12-25 18:52:25 +03:00
usrfol
2020-12-25 06:41:33 +03:00
] ,
" logs " : [
" /var/log/maloja " ,
" /etc/maloja/logs " ,
2020-12-25 18:52:25 +03:00
pthj ( usrfol , " logs " )
2020-12-25 06:41:33 +03:00
] ,
" cache " : [
" /var/cache/maloja " ,
" /etc/maloja/cache " ,
2020-12-25 18:52:25 +03:00
pthj ( usrfol , " cache " )
2020-12-25 06:41:33 +03:00
]
}
sentinels = {
" config " : " settings " ,
" state " : " scrobbles " ,
" logs " : None ,
" cache " : None ,
}
# check environ variables
stng_data = get_settings ( " DATA_DIRECTORY " , files = [ ] , environ_prefix = " MALOJA_ " )
if stng_data is not None :
dir_settings [ ' config ' ] = stng_data
dir_settings [ ' state ' ] = stng_data
dir_settings [ ' cache ' ] = pthj ( stng_data , ' cache ' )
dir_settings [ ' logs ' ] = pthj ( stng_data , ' logs ' )
2020-06-06 17:46:25 +03:00
else :
2020-12-25 06:41:33 +03:00
dir_settings [ ' config ' ] , dir_settings [ ' state ' ] , dir_settings [ ' cache ' ] , dir_settings [ ' logs ' ] = get_settings ( " DIRECTORY_CONFIG " , " DIRECTORY_STATE " , " DIRECTORY_LOGS " , " DIRECTORY_CACHE " , files = [ ] , environ_prefix = " MALOJA_ " )
# as soon as we know the config directory, we can load from settings file
if dir_settings [ ' config ' ] is not None :
settingsfiles = [ pthj ( dir_settings [ ' config ' ] , ' settings ' , ' default.ini ' ) , pthj ( dir_settings [ ' config ' ] , ' settings ' , ' settings.ini ' ) ]
dir_settings [ ' config ' ] , dir_settings [ ' state ' ] , dir_settings [ ' cache ' ] , dir_settings [ ' logs ' ] = get_settings ( " DIRECTORY_CONFIG " , " DIRECTORY_STATE " , " DIRECTORY_LOGS " , " DIRECTORY_CACHE " , files = settingsfiles , environ_prefix = " MALOJA_ " )
# now to the stuff no setting has explicitly defined
for dirtype in dir_settings :
if dir_settings [ dirtype ] is None :
for option in dir_options [ dirtype ] :
if os . path . exists ( option ) :
# check if this is really the directory used for this category (/etc/maloja could be used for state or just config)
if sentinels [ dirtype ] is None or os . path . exists ( pthj ( option , sentinels [ dirtype ] ) ) :
dir_settings [ dirtype ] = option
break
# if no directory seems to exist, use the first writable one
for dirtype in dir_settings :
if dir_settings [ dirtype ] is None :
for option in dir_options [ dirtype ] :
try :
os . makedirs ( option , exist_ok = True )
os . mknod ( pthj ( option , " .test " ) )
os . remove ( pthj ( option , " .test " ) )
dir_settings [ dirtype ] = option
break
except :
pass
2020-12-25 19:11:09 +03:00
assert all ( ( dir_settings [ s ] is not None ) for s in dir_settings )
2020-12-25 06:41:33 +03:00
data_directories = {
" auth " : pthj ( dir_settings [ ' state ' ] , " auth " ) ,
" backups " : pthj ( dir_settings [ ' state ' ] , " backups " ) ,
" images " : pthj ( dir_settings [ ' state ' ] , " images " ) ,
" scrobbles " : pthj ( dir_settings [ ' state ' ] , " scrobbles " ) ,
" rules " : pthj ( dir_settings [ ' config ' ] , " rules " ) ,
" clients " : pthj ( dir_settings [ ' config ' ] , " clients " ) ,
" settings " : pthj ( dir_settings [ ' config ' ] , " settings " ) ,
2021-12-12 15:53:02 +03:00
" css " : pthj ( dir_settings [ ' config ' ] , " custom_css " ) ,
2020-12-25 06:41:33 +03:00
" logs " : pthj ( dir_settings [ ' logs ' ] ) ,
" cache " : pthj ( dir_settings [ ' cache ' ] ) ,
}
data_dir = {
2020-12-25 07:24:59 +03:00
k : lambda * x , k = k : pthj ( data_directories [ k ] , * x ) for k in data_directories
2020-12-25 06:41:33 +03:00
}
2019-12-15 17:18:33 +03:00
2020-06-06 17:46:25 +03:00
2019-12-15 17:18:33 +03:00
### DOREAH CONFIGURATION
from doreah import config
config (
settings = {
" files " : [
2020-12-25 06:52:05 +03:00
data_dir [ ' settings ' ] ( " default.ini " ) ,
data_dir [ ' settings ' ] ( " settings.ini " )
2020-02-28 18:22:57 +03:00
] ,
" environ_prefix " : " MALOJA_ "
2019-12-15 17:18:33 +03:00
} ,
caching = {
2020-12-25 06:52:05 +03:00
" folder " : data_dir [ ' cache ' ] ( )
2019-12-15 17:18:33 +03:00
} ,
2020-07-29 16:52:01 +03:00
auth = {
" multiuser " : False ,
" cookieprefix " : " maloja " ,
" stylesheets " : [ " /style.css " ] ,
2020-12-25 06:52:05 +03:00
" dbfile " : data_dir [ ' auth ' ] ( " auth.ddb " )
2019-12-15 17:18:33 +03:00
}
)
2020-06-06 17:46:25 +03:00
# because we loaded a doreah module already before setting the config, we need to to that manually
settingsconfig . _readpreconfig ( )
2019-12-15 17:18:33 +03:00
2020-06-13 18:34:30 +03:00
config (
logging = {
2020-12-25 06:52:05 +03:00
" logfolder " : data_dir [ ' logs ' ] ( ) if get_settings ( " LOGGING " ) else None
2020-12-12 19:23:29 +03:00
} ,
regular = {
" autostart " : False ,
" offset " : get_settings ( " TIMEZONE " )
2020-06-13 18:34:30 +03:00
}
)
settingsconfig . _readpreconfig ( )
2019-12-15 17:18:33 +03:00
# thumbor
2019-12-12 23:24:13 +03:00
THUMBOR_SERVER , THUMBOR_SECRET = get_settings ( " THUMBOR_SERVER " , " THUMBOR_SECRET " )
try :
USE_THUMBOR = THUMBOR_SERVER is not None and THUMBOR_SECRET is not None
if USE_THUMBOR :
from libthumbor import CryptoURL
THUMBOR_GENERATOR = CryptoURL ( key = THUMBOR_SECRET )
OWNURL = get_settings ( " PUBLIC_URL " )
assert OWNURL is not None
except :
USE_THUMBOR = False
log ( " Thumbor could not be initialized. Is libthumbor installed? " )
2021-12-12 20:43:24 +03:00
# new config
malojaconfig = Configuration (
settings = {
" Setup " : {
2021-12-19 23:04:43 +03:00
" 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. " ) ,
2021-12-20 01:40:30 +03:00
" 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. " )
2021-12-12 20:43:24 +03:00
} ,
" Debug " : {
" logging " : ( tp . Boolean ( ) , " Enable Logging " , True ) ,
" dev_mode " : ( tp . Boolean ( ) , " Enable developer mode " , False ) ,
} ,
" Network " : {
2021-12-20 01:40:30 +03:00
" host " : ( tp . String ( ) , " Host " , " :: " , " Host for your server - most likely :: for IPv6 or 0.0.0.0 for IPv4 " ) ,
2021-12-12 20:43:24 +03:00
" port " : ( tp . Integer ( ) , " Port " , 42010 ) ,
} ,
" Technical " : {
2021-12-20 01:40:30 +03:00
" cache_expire_positive " : ( tp . Integer ( ) , " Image Cache Expiration " , 300 , " Days until images are refetched " ) ,
" cache_expire_negative " : ( tp . Integer ( ) , " Image Cache Negative Expiration " , 30 , " Days until failed image fetches are reattempted " ) ,
2021-12-12 20:43:24 +03:00
" use_db_cache " : ( tp . Boolean ( ) , " Use DB Cache " , True ) ,
" cache_database_short " : ( tp . Boolean ( ) , " Use volatile Database Cache " , True ) ,
" cache_database_perm " : ( tp . Boolean ( ) , " Use permanent Database Cache " , True ) ,
" db_cache_entries " : ( tp . Integer ( ) , " Maximal Cache entries " , 10000 ) ,
2021-12-20 01:40:30 +03:00
" db_max_memory " : ( tp . Integer ( max = 100 , min = 20 ) , " RAM Percentage Theshold " , 75 , " Maximal percentage of RAM that should be used by whole system before Maloja discards cache entries. Use a higher number if your Maloja runs on a dedicated instance (e.g. a container) " )
2021-12-12 20:43:24 +03:00
} ,
" Fluff " : {
2021-12-20 01:40:30 +03:00
" 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 " )
2021-12-12 20:43:24 +03:00
} ,
" Third Party Services " : {
2021-12-20 01:40:30 +03:00
" 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. " ) ,
2021-12-12 20:43:24 +03:00
" 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 ) ,
" spotify_api_id " : ( tp . String ( ) , " Spotify API ID " , None ) ,
" spotify_api_secret " : ( tp . String ( ) , " Spotify API Secret " , None ) ,
" lastfm_api_key " : ( tp . String ( ) , " Last.fm API Key " , None ) ,
2021-12-20 00:15:28 +03:00
" audiodb_api_key " : ( tp . String ( ) , " TheAudioDB API Key " , None ) ,
" track_search_provider " : ( tp . String ( ) , " Track Search Provider " , None ) ,
" send_stats " : ( tp . Boolean ( ) , " Send Statistics " , None ) ,
2021-12-12 20:43:24 +03:00
} ,
" Database " : {
2021-12-20 01:40:30 +03:00
" 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 " , " 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 " )
2021-12-12 20:43:24 +03:00
} ,
" 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 ) ,
2021-12-20 01:40:30 +03:00
" 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! " ) ,
2021-12-12 20:43:24 +03:00
" use_local_images " : ( tp . Boolean ( ) , " Use Local Images " , True ) ,
2021-12-14 23:19:15 +03:00
" local_image_rotate " : ( tp . Integer ( ) , " Local Image Rotate " , 3600 ) ,
2021-12-12 20:43:24 +03:00
" timezone " : ( tp . Integer ( ) , " UTC Offset " , 0 ) ,
" time_format " : ( tp . String ( ) , " Time Format " , " %d . % b % Y % I: % M % p " )
}
2021-12-14 23:19:15 +03:00
} ,
configfile = data_dir [ ' settings ' ] ( " settings.ini " ) ,
2021-12-20 02:15:22 +03:00
save_endpoint = " /apis/mlj_1/settings " ,
env_prefix = " MALOJA_ "
2021-12-12 20:43:24 +03:00
)