From 1e4484b1c80f14738c5280836436088bbabc35bc Mon Sep 17 00:00:00 2001 From: geoffrey45 Date: Sun, 9 Apr 2023 01:01:48 +0300 Subject: [PATCH] add --config flag to modify config path + use getters instead of constants in settings classes + refactor previous references + move get_xdg_config_dir() from settings.py to app.utils.xdg_utils.py --- app/api/imgserver.py | 72 +++--------- app/arg_handler.py | 29 ++++- app/config.py | 2 +- app/db/sqlite/__init__.py | 8 -- app/db/sqlite/utils.py | 4 +- app/lib/artistlib.py | 6 +- app/lib/colorlib.py | 2 +- app/lib/playlistlib.py | 6 +- app/lib/taglib.py | 4 +- .../__preinit/move_to_xdg_folder.py | 2 +- app/models/playlist.py | 2 +- app/print_help.py | 3 +- app/settings.py | 108 ++++++++++-------- app/setup/files.py | 4 +- app/setup/sqlite.py | 4 +- app/start_info_logger.py | 2 +- app/utils/xdg_utils.py | 21 ++++ 17 files changed, 149 insertions(+), 130 deletions(-) create mode 100644 app/utils/xdg_utils.py diff --git a/app/api/imgserver.py b/app/api/imgserver.py index ad66986..c429897 100644 --- a/app/api/imgserver.py +++ b/app/api/imgserver.py @@ -5,21 +5,6 @@ from flask import Blueprint, send_from_directory from app.settings import Paths api = Blueprint("imgserver", __name__, url_prefix="/img") -SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg") - -APP_DIR = Path(Paths.APP_DIR) -IMG_PATH = APP_DIR / "images" -ASSETS_PATH = APP_DIR / "assets" - -THUMB_PATH = IMG_PATH / "thumbnails" -LG_THUMB_PATH = THUMB_PATH / "large" -SM_THUMB_PATH = THUMB_PATH / "small" - -ARTIST_PATH = IMG_PATH / "artists" -ARTIST_LG_PATH = ARTIST_PATH / "large" -ARTIST_SM_PATH = ARTIST_PATH / "small" - -PLAYLIST_PATH = IMG_PATH / "playlists" @api.route("/") @@ -28,86 +13,65 @@ def hello(): def send_fallback_img(filename: str = "default.webp"): - img = ASSETS_PATH / filename + path = Paths.get_assets_path() + img = Path(path) / filename if not img.exists(): return "", 404 - return send_from_directory(ASSETS_PATH, filename) + return send_from_directory(path, filename) @api.route("/t/") def send_lg_thumbnail(imgpath: str): - fpath = LG_THUMB_PATH / imgpath + path = Paths.get_lg_thumb_path() + fpath = Path(path) / imgpath if fpath.exists(): - return send_from_directory(LG_THUMB_PATH, imgpath) + return send_from_directory(path, imgpath) return send_fallback_img() @api.route("/t/s/") def send_sm_thumbnail(imgpath: str): - fpath = SM_THUMB_PATH / imgpath + path = Paths.get_sm_thumb_path() + fpath = Path(path) / imgpath if fpath.exists(): - return send_from_directory(SM_THUMB_PATH, imgpath) + return send_from_directory(path, imgpath) return send_fallback_img() @api.route("/a/") def send_lg_artist_image(imgpath: str): - fpath = ARTIST_LG_PATH / imgpath + path = Paths.get_artist_img_lg_path() + fpath = Path(path) / imgpath if fpath.exists(): - return send_from_directory(ARTIST_LG_PATH, imgpath) + return send_from_directory(path, imgpath) return send_fallback_img("artist.webp") @api.route("/a/s/") def send_sm_artist_image(imgpath: str): - fpath = ARTIST_SM_PATH / imgpath + path = Paths.get_artist_img_sm_path() + fpath = Path(path) / imgpath if fpath.exists(): - return send_from_directory(ARTIST_SM_PATH, imgpath) + return send_from_directory(path, imgpath) return send_fallback_img("artist.webp") @api.route("/p/") def send_playlist_image(imgpath: str): - fpath = PLAYLIST_PATH / imgpath + path = Paths.get_playlist_img_path() + fpath = Path(path) / imgpath if fpath.exists(): - return send_from_directory(PLAYLIST_PATH, imgpath) + return send_from_directory(path, imgpath) return send_fallback_img("playlist.svg") - - -# @app.route("/raw") -# @app.route("/raw/") -# def send_from_filepath(imgpath: str = ""): -# imgpath = "/" + imgpath -# filename = path.basename(imgpath) - -# def verify_is_image(): -# _, ext = path.splitext(filename) -# return ext in SUPPORTED_IMAGES - -# verified = verify_is_image() - -# if not verified: -# return imgpath, 404 - -# exists = path.exists(imgpath) - -# if verified and exists: -# return send_from_directory(path.dirname(imgpath), filename) - -# return imgpath, 404 - - -# def serve_imgs(): -# app.run(threaded=True, port=1971, host="0.0.0.0", debug=True) diff --git a/app/arg_handler.py b/app/arg_handler.py index cd82f3e..ac0a368 100644 --- a/app/arg_handler.py +++ b/app/arg_handler.py @@ -1,7 +1,7 @@ """ Handles arguments passed to the program. """ - +import os.path import sys from configparser import ConfigParser @@ -10,6 +10,10 @@ import PyInstaller.__main__ as bundler from app import settings from app.print_help import HELP_MESSAGE from app.utils.wintools import is_windows +from app.logger import log +from app.utils.xdg_utils import get_xdg_config_dir + +# from app.api.imgserver import set_app_dir config = ConfigParser() config.read("pyinstaller.config.ini") @@ -23,6 +27,7 @@ class HandleArgs: self.handle_build() self.handle_host() self.handle_port() + self.handle_config_path() self.handle_no_feat() self.handle_remove_prod() self.handle_help() @@ -89,6 +94,28 @@ class HandleArgs: settings.FLASKVARS.FLASK_HOST = host # type: ignore + @staticmethod + def handle_config_path(): + """ + Modifies the config path. + """ + if ALLARGS.config in ARGS: + index = ARGS.index(ALLARGS.config) + + try: + config_path = ARGS[index + 1] + + if os.path.exists(config_path): + settings.Paths.set_config_dir(config_path) + return + + log.warn(f"Config path {config_path} doesn't exist") + sys.exit(0) + except IndexError: + pass + + settings.Paths.set_config_dir(get_xdg_config_dir()) + @staticmethod def handle_no_feat(): # if ArgsEnum.no_feat in ARGS: diff --git a/app/config.py b/app/config.py index 8ea6e8c..999fdcd 100644 --- a/app/config.py +++ b/app/config.py @@ -57,5 +57,5 @@ class ConfigManager: self.write_config(config_data) -settings = ConfigManager(Db.JSON_CONFIG_PATH) +settings = ConfigManager(Db.get_json_config_path()) a = settings.get_value(ConfigKeys.ROOT_DIRS) diff --git a/app/db/sqlite/__init__.py b/app/db/sqlite/__init__.py index f7c5d97..25eaee9 100644 --- a/app/db/sqlite/__init__.py +++ b/app/db/sqlite/__init__.py @@ -17,14 +17,6 @@ def create_connection(db_file: str) -> SqlConn: return conn -def get_sqlite_conn(): - """ - It opens a connection to the database - :return: A connection to the database. - """ - return create_connection(Db.APP_DB_PATH) - - def create_tables(conn: SqlConn, sql_query: str): """ Executes the specifiend SQL file to create database tables. diff --git a/app/db/sqlite/utils.py b/app/db/sqlite/utils.py index 6c2c87c..53a4dae 100644 --- a/app/db/sqlite/utils.py +++ b/app/db/sqlite/utils.py @@ -78,10 +78,10 @@ class SQLiteManager: if self.conn is not None: return self.conn.cursor() - db_path = Db.APP_DB_PATH + db_path = Db.get_app_db_path() if self.userdata_db: - db_path = Db.USERDATA_DB_PATH + db_path = Db.get_userdata_db_path() self.conn = sqlite3.connect( db_path, diff --git a/app/lib/artistlib.py b/app/lib/artistlib.py index e36d70c..f519fc8 100644 --- a/app/lib/artistlib.py +++ b/app/lib/artistlib.py @@ -43,8 +43,8 @@ def get_artist_image_link(artist: str): # TODO: Move network calls to utils/network.py class DownloadImage: def __init__(self, url: str, name: str) -> None: - sm_path = Path(settings.Paths.ARTIST_IMG_SM_PATH) / name - lg_path = Path(settings.Paths.ARTIST_IMG_LG_PATH) / name + sm_path = Path(settings.Paths.get_artist_img_sm_path()) / name + lg_path = Path(settings.Paths.get_artist_img_lg_path()) / name img = self.download(url) @@ -90,7 +90,7 @@ class CheckArtistImages: :param artist: The artist name """ - img_path = Path(settings.Paths.ARTIST_IMG_SM_PATH) / f"{artist.artisthash}.webp" + img_path = Path(settings.Paths.get_artist_img_sm_path()) / f"{artist.artisthash}.webp" if img_path.exists(): return diff --git a/app/lib/colorlib.py b/app/lib/colorlib.py index a7999cb..245dfe7 100644 --- a/app/lib/colorlib.py +++ b/app/lib/colorlib.py @@ -34,7 +34,7 @@ def get_image_colors(image: str, count=1) -> list[str]: def process_color(item_hash: str, is_album=True): - path = settings.Paths.SM_THUMB_PATH if is_album else settings.Paths.ARTIST_IMG_SM_PATH + path = settings.Paths.get_sm_thumb_path() if is_album else settings.Paths.get_artist_img_sm_path() path = Path(path) / (item_hash + ".webp") if not path.exists(): diff --git a/app/lib/playlistlib.py b/app/lib/playlistlib.py index b5c50ed..dfceb0d 100644 --- a/app/lib/playlistlib.py +++ b/app/lib/playlistlib.py @@ -16,7 +16,7 @@ def create_thumbnail(image: Any, img_path: str) -> str: Creates a 250 x 250 thumbnail from a playlist image """ thumb_path = "thumb_" + img_path - full_thumb_path = os.path.join(settings.Paths.APP_DIR, "images", "playlists", thumb_path) + full_thumb_path = os.path.join(settings.Paths.get_app_dir(), "images", "playlists", thumb_path) aspect_ratio = image.width / image.height @@ -33,7 +33,7 @@ def create_gif_thumbnail(image: Any, img_path: str): Creates a 250 x 250 thumbnail from a playlist image """ thumb_path = "thumb_" + img_path - full_thumb_path = os.path.join(settings.Paths.APP_DIR, "images", "playlists", thumb_path) + full_thumb_path = os.path.join(settings.Paths.get_app_dir(), "images", "playlists", thumb_path) frames = [] @@ -60,7 +60,7 @@ def save_p_image(file, pid: str): filename = pid + str(random_str) + ".webp" - full_img_path = os.path.join(settings.Paths.PLAYLIST_IMG_PATH, filename) + full_img_path = os.path.join(settings.Paths.get_playlist_img_path(), filename) if file.content_type == "image/gif": frames = [] diff --git a/app/lib/taglib.py b/app/lib/taglib.py index 832f114..d0f5cab 100644 --- a/app/lib/taglib.py +++ b/app/lib/taglib.py @@ -27,8 +27,8 @@ def extract_thumb(filepath: str, webp_path: str) -> bool: """ Extracts the thumbnail from an audio file. Returns the path to the thumbnail. """ - img_path = os.path.join(Paths.LG_THUMBS_PATH, webp_path) - sm_img_path = os.path.join(Paths.SM_THUMB_PATH, webp_path) + img_path = os.path.join(Paths.get_lg_thumb_path(), webp_path) + sm_img_path = os.path.join(Paths.get_sm_thumb_path(), webp_path) tsize = Defaults.THUMB_SIZE sm_tsize = Defaults.SM_THUMB_SIZE diff --git a/app/migrations/__preinit/move_to_xdg_folder.py b/app/migrations/__preinit/move_to_xdg_folder.py index 0d8cc45..0c7ab01 100644 --- a/app/migrations/__preinit/move_to_xdg_folder.py +++ b/app/migrations/__preinit/move_to_xdg_folder.py @@ -17,7 +17,7 @@ class MoveToXdgFolder: @staticmethod def migrate(): old_config_dir = os.path.join(Paths.USER_HOME_DIR, ".swing") - new_config_dir = Paths.APP_DIR + new_config_dir = Paths.get_app_dir() if not os.path.exists(old_config_dir): log.info("No old config folder found. Skipping migration.") diff --git a/app/models/playlist.py b/app/models/playlist.py index 8e4fefd..2d1e3c9 100644 --- a/app/models/playlist.py +++ b/app/models/playlist.py @@ -33,7 +33,7 @@ class Playlist: self.count = len(self.trackhashes) self.has_gif = bool(int(self.has_gif)) - self.has_image = (Path(settings.Paths.PLAYLIST_IMG_PATH) / str(self.image)).exists() + self.has_image = (Path(settings.Paths.get_playlist_img_path()) / str(self.image)).exists() if self.image is not None: self.thumb = "thumb_" + self.image diff --git a/app/print_help.py b/app/print_help.py index 016724b..4a53797 100644 --- a/app/print_help.py +++ b/app/print_help.py @@ -9,7 +9,8 @@ Options: {args.build}: Build the application (in development) {args.host}: Set the host {args.port}: Set the port - + {args.config}: Set the config path + {', '.join(args.show_feat)}: Do not extract featured artists from the song title {', '.join(args.show_prod)}: Do not hide producers in the song title diff --git a/app/settings.py b/app/settings.py index a5f2c0e..5cb8d4b 100644 --- a/app/settings.py +++ b/app/settings.py @@ -6,67 +6,71 @@ import os join = os.path.join -# ------- HELPER METHODS -------- -def get_xdg_config_dir(): - """ - Returns the XDG_CONFIG_HOME environment variable if it exists, otherwise - returns the default config directory. If none of those exist, returns the - user's home directory. - """ - xdg_config_home = os.environ.get("XDG_CONFIG_HOME") - - if xdg_config_home: - return xdg_config_home - - try: - alt_dir = join(os.environ.get("HOME"), ".config") - - if os.path.exists(alt_dir): - return alt_dir - except TypeError: - return os.path.expanduser("~") - - -# !------- HELPER METHODS --------! - class Release: - APP_VERSION = "v1.2.0" + APP_VERSION = "v1.2.1" class Paths: - XDG_CONFIG_DIR = get_xdg_config_dir() + XDG_CONFIG_DIR = "" USER_HOME_DIR = os.path.expanduser("~") - CONFIG_FOLDER = "swingmusic" if XDG_CONFIG_DIR != USER_HOME_DIR else ".swingmusic" + # TODO: Break this down into getter methods for each path - APP_DIR = join(XDG_CONFIG_DIR, CONFIG_FOLDER) - IMG_PATH = join(APP_DIR, "images") + @classmethod + def set_config_dir(cls, path: str): + cls.XDG_CONFIG_DIR = path - ARTIST_IMG_PATH = join(IMG_PATH, "artists") - ARTIST_IMG_SM_PATH = join(ARTIST_IMG_PATH, "small") - ARTIST_IMG_LG_PATH = join(ARTIST_IMG_PATH, "large") + @classmethod + def get_config_dir(cls): + return cls.XDG_CONFIG_DIR - PLAYLIST_IMG_PATH = join(IMG_PATH, "playlists") + @classmethod + def get_config_folder(cls): + return "swingmusic" if cls.get_config_dir() != cls.USER_HOME_DIR else ".swingmusic" - THUMBS_PATH = join(IMG_PATH, "thumbnails") - SM_THUMB_PATH = join(THUMBS_PATH, "small") - LG_THUMBS_PATH = join(THUMBS_PATH, "large") + @classmethod + def get_app_dir(cls): + return join(cls.get_config_dir(), cls.get_config_folder()) - # TEST_DIR = "/home/cwilvx/Downloads/Telegram Desktop" - # TEST_DIR = "/mnt/dfc48e0f-103b-426e-9bf9-f25d3743bc96/Music/Chill/Wolftyla Radio" - # HOME_DIR = TEST_DIR + @classmethod + def get_img_path(cls): + return join(cls.get_app_dir(), "images") + @classmethod + def get_artist_img_path(cls): + return join(cls.get_img_path(), "artists") -class Urls: - IMG_BASE_URI = "http://127.0.0.1:8900/images/" - IMG_ARTIST_URI = IMG_BASE_URI + "artists/" - IMG_THUMB_URI = IMG_BASE_URI + "thumbnails/" - IMG_PLAYLIST_URI = IMG_BASE_URI + "playlists/" + @classmethod + def get_artist_img_sm_path(cls): + return join(cls.get_artist_img_path(), "small") + + @classmethod + def get_artist_img_lg_path(cls): + return join(cls.get_artist_img_path(), "large") + + @classmethod + def get_playlist_img_path(cls): + return join(cls.get_img_path(), "playlists") + + @classmethod + def get_thumbs_path(cls): + return join(cls.get_img_path(), "thumbnails") + + @classmethod + def get_sm_thumb_path(cls): + return join(cls.get_thumbs_path(), "small") + + @classmethod + def get_lg_thumb_path(cls): + return join(cls.get_thumbs_path(), "large") + + @classmethod + def get_assets_path(cls): + return join(Paths.get_app_dir(), "assets") # defaults class Defaults: - DEFAULT_ARTIST_IMG = Urls.IMG_ARTIST_URI + "0.webp" THUMB_SIZE = 400 SM_THUMB_SIZE = 64 SM_ARTIST_IMG_SIZE = 64 @@ -83,9 +87,18 @@ SUPPORTED_FILES = tuple(f".{file}" for file in FILES) class Db: APP_DB_NAME = "swing.db" USER_DATA_DB_NAME = "userdata.db" - APP_DB_PATH = join(Paths.APP_DIR, APP_DB_NAME) - USERDATA_DB_PATH = join(Paths.APP_DIR, USER_DATA_DB_NAME) - JSON_CONFIG_PATH = join(Paths.APP_DIR, "config.json") + + @classmethod + def get_app_db_path(cls): + return join(Paths.get_app_dir(), cls.APP_DB_NAME) + + @classmethod + def get_userdata_db_path(cls): + return join(Paths.get_app_dir(), cls.USER_DATA_DB_NAME) + + @classmethod + def get_json_config_path(cls): + return join(Paths.get_app_dir(), "config.json") class FLASKVARS: @@ -101,6 +114,7 @@ class ALLARGS: build = "--build" port = "--port" host = "--host" + config = "--config" show_feat = ["--show-feat", "-sf"] show_prod = ["--show-prod", "-sp"] help = ["--help", "-h"] diff --git a/app/setup/files.py b/app/setup/files.py index d54273c..bcdbb9c 100644 --- a/app/setup/files.py +++ b/app/setup/files.py @@ -33,7 +33,7 @@ class CopyFiles: files = [ { "src": assets_dir, - "dest": os.path.join(settings.Paths.APP_DIR, "assets"), + "dest": os.path.join(settings.Paths.get_app_dir(), "assets"), "is_dir": True, } ] @@ -83,7 +83,7 @@ def create_config_dir() -> None: ] for _dir in dirs: - path = os.path.join(settings.Paths.APP_DIR, _dir) + path = os.path.join(settings.Paths.get_app_dir(), _dir) exists = os.path.exists(path) if not exists: diff --git a/app/setup/sqlite.py b/app/setup/sqlite.py index a1c158e..f558e73 100644 --- a/app/setup/sqlite.py +++ b/app/setup/sqlite.py @@ -19,8 +19,8 @@ def setup_sqlite(): run_preinit_migrations() - app_db_conn = create_connection(Db.APP_DB_PATH) - playlist_db_conn = create_connection(Db.USERDATA_DB_PATH) + app_db_conn = create_connection(Db.get_app_db_path()) + playlist_db_conn = create_connection(Db.get_userdata_db_path()) create_tables(app_db_conn, queries.CREATE_APPDB_TABLES) create_tables(playlist_db_conn, queries.CREATE_USERDATA_TABLES) diff --git a/app/start_info_logger.py b/app/start_info_logger.py index 89b0818..4d43753 100644 --- a/app/start_info_logger.py +++ b/app/start_info_logger.py @@ -44,7 +44,7 @@ def log_startup_info(): ) print( - f"{TCOLOR.YELLOW}Data folder: {Paths.APP_DIR}{TCOLOR.ENDC}" + f"{TCOLOR.YELLOW}Data folder: {Paths.get_app_dir()}{TCOLOR.ENDC}" ) print("\n") diff --git a/app/utils/xdg_utils.py b/app/utils/xdg_utils.py new file mode 100644 index 0000000..5037d11 --- /dev/null +++ b/app/utils/xdg_utils.py @@ -0,0 +1,21 @@ +import os + + +def get_xdg_config_dir(): + """ + Returns the XDG_CONFIG_HOME environment variable if it exists, otherwise + returns the default config directory. If none of those exist, returns the + user's home directory. + """ + xdg_config_home = os.environ.get("XDG_CONFIG_HOME") + + if xdg_config_home: + return xdg_config_home + + try: + alt_dir = os.path.join(os.environ.get("HOME"), ".config") + + if os.path.exists(alt_dir): + return alt_dir + except TypeError: + return os.path.expanduser("~")