diff --git a/app/api/folder.py b/app/api/folder.py index 7b2b5843..12fb1fa2 100644 --- a/app/api/folder.py +++ b/app/api/folder.py @@ -64,12 +64,16 @@ def get_folder_tree(body: FolderTree): else: req_dir = "/" + req_dir + "/" if not req_dir.startswith("/") else req_dir + "/" - tracks, folders = GetFilesAndDirs(req_dir, tracks_only=tracks_only)() + res = GetFilesAndDirs(req_dir, tracks_only=tracks_only)() + res['folders'] = sorted(res['folders'], key=lambda i: i.name) - return { - "tracks": tracks, - "folders": sorted(folders, key=lambda i: i.name), - } + return res + + # return { + # "path": req_dir, + # "tracks": tracks, + # "folders": sorted(folders, key=lambda i: i.name), + # } def get_all_drives(is_win: bool = False): diff --git a/app/arg_handler.py b/app/arg_handler.py index 7cae6f7d..dec203ac 100644 --- a/app/arg_handler.py +++ b/app/arg_handler.py @@ -58,7 +58,7 @@ class HandleArgs: if not value: log.error(f"WARNING: {key} not set in environment") - #sys.exit(0) + sys.exit(0) lines.append(f'{key} = "{value}"\n') @@ -132,12 +132,13 @@ class HandleArgs: try: config_path = ARGS[index + 1] + resolved = os.path.abspath(config_path) - if os.path.exists(config_path): - settings.Paths.set_config_dir(config_path) + if os.path.exists(resolved): + settings.Paths.set_config_dir(resolved) return - log.warn(f"Config path {config_path} doesn't exist") + log.warn(f"Config path {resolved} doesn't exist") sys.exit(0) except IndexError: pass diff --git a/app/lib/folderslib.py b/app/lib/folderslib.py index e43206df..26c2d9ab 100644 --- a/app/lib/folderslib.py +++ b/app/lib/folderslib.py @@ -48,11 +48,20 @@ class GetFilesAndDirs: self.path = path self.tracks_only = tracks_only - def __call__(self) -> tuple[list[Track], list[Folder]]: + def get_files_and_dirs(self, path: str, skip_empty_folders=True): + """ + Given a path, returns a list of tracks and folders in that immediate path. + + Can recursively call itself to skip through empty folders. + """ try: - entries = os.scandir(self.path) + entries = os.scandir(path) except FileNotFoundError: - return [], [] + return { + "path": path, + "tracks": [], + "folders": [], + } dirs, files = [], [] @@ -86,4 +95,17 @@ class GetFilesAndDirs: if not self.tracks_only: folders = get_folders(dirs) - return tracks, folders + if skip_empty_folders and len(folders) == 1 and len(tracks) == 0: + # INFO: When we only have one folder and no tracks, + # skip through empty folders. + # Call recursively with the first folder in the list. + return self.get_files_and_dirs(folders[0].path) + + return { + "path": path, + "tracks": tracks, + "folders": folders, + } + + def __call__(self): + return self.get_files_and_dirs(self.path) diff --git a/app/migrations/v1_4_9/__init__.py b/app/migrations/v1_4_9/__init__.py index 4ebd0eae..a925db34 100644 --- a/app/migrations/v1_4_9/__init__.py +++ b/app/migrations/v1_4_9/__init__.py @@ -10,15 +10,21 @@ class AddTimestampToFavoritesTable(Migration): @staticmethod def migrate(): # INFO: add timestamp column with automatic current timestamp - sql = f"ALTER TABLE favorites ADD COLUMN timestamp INTEGER NOT NULL DEFAULT 0" + sql = f"ALTER TABLE favorites ADD COLUMN IF NOT EXISTS timestamp INTEGER NOT NULL DEFAULT 0" # INFO: execute the sql with SQLiteManager(userdata_db=True) as cur: - cur.execute(sql) + try: + # INFO: Add the timestamp column to the favorites table + cur.execute(sql) - # INFO: Update the timestamp column with the current timestamp - cur.execute("UPDATE favorites SET timestamp = strftime('%s', 'now')") - cur.close() + # INFO: Set all the timestamps to the current time + cur.execute("UPDATE favorites SET timestamp = strftime('%s', 'now')") + except Exception as e: + # INFO: timestamp column already exists + pass + finally: + cur.close() class MoveHashesToSha1(Migration): @@ -28,7 +34,8 @@ class MoveHashesToSha1(Migration): Thanks to [@tcsenpai](https:github.com/tcsenpai) for the contribution. """ + pass # INFO: Apparentlly, every single table is affected by this migration. - # NOTE: Use generators to avoid memory issues. \ No newline at end of file + # NOTE: Use generators to avoid memory issues. diff --git a/app/requests/artists.py b/app/requests/artists.py index cca86563..05e26336 100644 --- a/app/requests/artists.py +++ b/app/requests/artists.py @@ -1,12 +1,12 @@ """ Requests related to artists """ + import urllib.parse import requests from requests import ConnectionError, HTTPError, ReadTimeout -#from app import settings from app.utils.hashing import create_hash @@ -16,10 +16,6 @@ def fetch_similar_artists(name: str): """ url = f"https://kerve.last.fm/kerve/similarartists?artist={urllib.parse.quote_plus(name, safe='')}&autocorrect=1&tracks=1&image_size=large&limit=250&format=json" - # REVIEW This is the old way of doing it. The new way is to use the Kerve API. - #url = f"https://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&artist={urllib.parse.quote_plus(name, safe='')}&api_key={settings.Keys.LASTFM_API_KEY}&format=json&limit=250" - # TODO Cannot be tested due to PR message - url = f"https://kerve.last.fm/kerve/similarartists?artist={urllib.parse.quote_plus(name, safe='')}&autocorrect=1&tracks=1&image_size=large&limit=250&format=json" try: response = requests.get(url, timeout=10) response.raise_for_status() diff --git a/app/settings.py b/app/settings.py index 5d66643c..5cddc750 100644 --- a/app/settings.py +++ b/app/settings.py @@ -275,9 +275,6 @@ class Keys: @classmethod def load(cls): - # TODO Remove this. Just an handy flag to test the app without the API key - # IS_BUILD = True - if IS_BUILD: cls.SWINGMUSIC_APP_VERSION = configs.SWINGMUSIC_APP_VERSION cls.GIT_LATEST_COMMIT_HASH = configs.GIT_LATEST_COMMIT_HASH diff --git a/app/utils/hashing.py b/app/utils/hashing.py index e9ed147d..7e4f1b27 100644 --- a/app/utils/hashing.py +++ b/app/utils/hashing.py @@ -33,10 +33,9 @@ def create_hash(*args: str, decode=False, limit=10) -> str: str_ = str_.encode("utf-8") str_ = hashlib.sha1(str_).hexdigest() - # REVIEW Switched to sha1 hashlib.sha256(str_).hexdigest() - # REVIEW Take the first limit/2 and last limit/2 characters - # This is to avoid collisions - return str_[:limit // 2] + str_[-limit // 2:] if limit % 2 == 0 else str_[:limit // 2] + str_[-limit // 2 - 1:] - - # return str_[-limit:] + return ( + str_[: limit // 2] + str_[-limit // 2 :] + if limit % 2 == 0 + else str_[: limit // 2] + str_[-limit // 2 - 1 :] + ) diff --git a/app/utils/xdg_utils.py b/app/utils/xdg_utils.py index 5037d111..ecdddded 100644 --- a/app/utils/xdg_utils.py +++ b/app/utils/xdg_utils.py @@ -1,7 +1,7 @@ import os -def get_xdg_config_dir(): +def get_xdg_config_dir() -> str: """ Returns the XDG_CONFIG_HOME environment variable if it exists, otherwise returns the default config directory. If none of those exist, returns the @@ -19,3 +19,6 @@ def get_xdg_config_dir(): return alt_dir except TypeError: return os.path.expanduser("~") + + # Fallback to current directory + return os.path.abspath(".") diff --git a/dev_build.sh b/dev_build.sh old mode 100644 new mode 100755 index edc8e958..dc9f3b71 --- a/dev_build.sh +++ b/dev_build.sh @@ -3,7 +3,7 @@ # builds the latest version of the client and server -# NOTE Changes directory to the webclient directory and builds it +# NOTE Changes directory to the webclient directory and builds it cd ../swingmusic-client || exit # REVIEW Failsafe exit yarn build --outDir ../swingmusic/client cd ../swingmusic || exit # REVIEW Failsafe exit