handle XDG_CONFIG_HOME specification ...

+ fix bug that caused duplicate artist color entries to db
+ check if app is windows (prep for windows build)
+ remove caribou migrations lib
+ rename all api blueprints to "api"
+ unregister child directories when customizing root dirs
+ misc
This commit is contained in:
geoffrey45 2023-01-22 23:57:12 +03:00
parent 4e6e1f03dc
commit bcc4873766
18 changed files with 163 additions and 179 deletions

View File

@ -5,17 +5,8 @@ This module combines all API blueprints into a single Flask app instance.
from flask import Flask from flask import Flask
from flask_cors import CORS from flask_cors import CORS
from app.api import ( from app.api import (album, artist, favorites, folder, imgserver, playlist,
album, search, settings, track)
artist,
favorites,
folder,
playlist,
search,
track,
settings,
imgserver,
)
def create_api(): def create_api():
@ -27,14 +18,14 @@ def create_api():
with app.app_context(): with app.app_context():
app.register_blueprint(album.albumbp) app.register_blueprint(album.api)
app.register_blueprint(artist.artistbp) app.register_blueprint(artist.api)
app.register_blueprint(track.trackbp) app.register_blueprint(track.api)
app.register_blueprint(search.searchbp) app.register_blueprint(search.api)
app.register_blueprint(folder.folderbp) app.register_blueprint(folder.api)
app.register_blueprint(playlist.playlistbp) app.register_blueprint(playlist.api)
app.register_blueprint(favorites.favbp) app.register_blueprint(favorites.api)
app.register_blueprint(imgserver.imgbp) app.register_blueprint(imgserver.api)
app.register_blueprint(settings.settingsbp) app.register_blueprint(settings.api)
return app return app

View File

@ -12,15 +12,14 @@ from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store from app.db.store import Store
from app.models import FavType, Track from app.models import FavType, Track
get_album_by_id = adb.get_album_by_id get_album_by_id = adb.get_album_by_id
get_albums_by_albumartist = adb.get_albums_by_albumartist get_albums_by_albumartist = adb.get_albums_by_albumartist
check_is_fav = favdb.check_is_favorite check_is_fav = favdb.check_is_favorite
albumbp = Blueprint("album", __name__, url_prefix="") api = Blueprint("album", __name__, url_prefix="")
@albumbp.route("/album", methods=["POST"]) @api.route("/album", methods=["POST"])
def get_album(): def get_album():
"""Returns all the tracks in the given album.""" """Returns all the tracks in the given album."""
@ -88,7 +87,7 @@ def get_album():
return {"tracks": tracks, "info": album} return {"tracks": tracks, "info": album}
@albumbp.route("/album/<albumhash>/tracks", methods=["GET"]) @api.route("/album/<albumhash>/tracks", methods=["GET"])
def get_album_tracks(albumhash: str): def get_album_tracks(albumhash: str):
""" """
Returns all the tracks in the given album. Returns all the tracks in the given album.
@ -105,7 +104,7 @@ def get_album_tracks(albumhash: str):
return {"tracks": tracks} return {"tracks": tracks}
@albumbp.route("/album/from-artist", methods=["POST"]) @api.route("/album/from-artist", methods=["POST"])
def get_artist_albums(): def get_artist_albums():
data = request.get_json() data = request.get_json()

View File

@ -5,12 +5,12 @@ from collections import deque
from flask import Blueprint, request from flask import Blueprint, request
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store from app.db.store import Store
from app.models import Album, FavType, Track from app.models import Album, FavType, Track
from app.utils import remove_duplicates from app.utils import remove_duplicates
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
artistbp = Blueprint("artist", __name__, url_prefix="/") api = Blueprint("artist", __name__, url_prefix="/")
class CacheEntry: class CacheEntry:
@ -156,7 +156,7 @@ def add_albums_to_cache(artisthash: str):
# ======================================================= # =======================================================
@artistbp.route("/artist/<artisthash>", methods=["GET"]) @api.route("/artist/<artisthash>", methods=["GET"])
def get_artist(artisthash: str): def get_artist(artisthash: str):
""" """
Get artist data. Get artist data.
@ -203,7 +203,7 @@ def get_artist(artisthash: str):
return {"artist": artist, "tracks": tracks[:limit]} return {"artist": artist, "tracks": tracks[:limit]}
@artistbp.route("/artist/<artisthash>/albums", methods=["GET"]) @api.route("/artist/<artisthash>/albums", methods=["GET"])
def get_artist_albums(artisthash: str): def get_artist_albums(artisthash: str):
limit = request.args.get("limit") limit = request.args.get("limit")
@ -261,7 +261,7 @@ def get_artist_albums(artisthash: str):
} }
@artistbp.route("/artist/<artisthash>/tracks", methods=["GET"]) @api.route("/artist/<artisthash>/tracks", methods=["GET"])
def get_artist_tracks(artisthash: str): def get_artist_tracks(artisthash: str):
""" """
Returns all artists by a given artist. Returns all artists by a given artist.

View File

@ -1,17 +1,18 @@
from flask import Blueprint, request from flask import Blueprint, request
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.store import Store from app.db.store import Store
from app.models import FavType from app.models import FavType
from app.utils import UseBisection from app.utils import UseBisection
favbp = Blueprint("favorite", __name__, url_prefix="/") api = Blueprint("favorite", __name__, url_prefix="/")
def remove_none(items: list): def remove_none(items: list):
return [i for i in items if i is not None] return [i for i in items if i is not None]
@favbp.route("/favorite/add", methods=["POST"]) @api.route("/favorite/add", methods=["POST"])
def add_favorite(): def add_favorite():
""" """
Adds a favorite to the database. Adds a favorite to the database.
@ -32,7 +33,7 @@ def add_favorite():
return {"msg": "Added to favorites"} return {"msg": "Added to favorites"}
@favbp.route("/favorite/remove", methods=["POST"]) @api.route("/favorite/remove", methods=["POST"])
def remove_favorite(): def remove_favorite():
""" """
Removes a favorite from the database. Removes a favorite from the database.
@ -53,7 +54,7 @@ def remove_favorite():
return {"msg": "Removed from favorites"} return {"msg": "Removed from favorites"}
@favbp.route("/albums/favorite") @api.route("/albums/favorite")
def get_favorite_albums(): def get_favorite_albums():
limit = request.args.get("limit") limit = request.args.get("limit")
@ -77,7 +78,7 @@ def get_favorite_albums():
return {"albums": fav_albums[:limit]} return {"albums": fav_albums[:limit]}
@favbp.route("/tracks/favorite") @api.route("/tracks/favorite")
def get_favorite_tracks(): def get_favorite_tracks():
limit = request.args.get("limit") limit = request.args.get("limit")
@ -100,7 +101,7 @@ def get_favorite_tracks():
return {"tracks": tracks[:limit]} return {"tracks": tracks[:limit]}
@favbp.route("/artists/favorite") @api.route("/artists/favorite")
def get_favorite_artists(): def get_favorite_artists():
limit = request.args.get("limit") limit = request.args.get("limit")
@ -124,7 +125,7 @@ def get_favorite_artists():
return {"artists": artists[:limit]} return {"artists": artists[:limit]}
@favbp.route("/favorites") @api.route("/favorites")
def get_all_favorites(): def get_all_favorites():
""" """
Returns all the favorites in the database. Returns all the favorites in the database.
@ -191,7 +192,7 @@ def get_all_favorites():
} }
@favbp.route("/favorites/check") @api.route("/favorites/check")
def check_favorite(): def check_favorite():
""" """
Checks if a favorite exists in the database. Checks if a favorite exists in the database.

View File

@ -2,16 +2,16 @@
Contains all the folder routes. Contains all the folder routes.
""" """
import os import os
from flask import Blueprint, request from flask import Blueprint, request
from app import settings from app import settings
from app.lib.folderslib import GetFilesAndDirs from app.lib.folderslib import GetFilesAndDirs
api = Blueprint("folder", __name__, url_prefix="/")
folderbp = Blueprint("folder", __name__, url_prefix="/")
@folderbp.route("/folder", methods=["POST"]) @api.route("/folder", methods=["POST"])
def get_folder_tree(): def get_folder_tree():
""" """
Returns a list of all the folders and tracks in the given folder. Returns a list of all the folders and tracks in the given folder.
@ -21,10 +21,10 @@ def get_folder_tree():
if data is not None: if data is not None:
req_dir: str = data["folder"] req_dir: str = data["folder"]
else: else:
req_dir = settings.HOME_DIR req_dir = settings.USER_HOME_DIR
if req_dir == "$home": if req_dir == "$home":
req_dir = settings.HOME_DIR req_dir = settings.USER_HOME_DIR
tracks, folders = GetFilesAndDirs(req_dir)() tracks, folders = GetFilesAndDirs(req_dir)()
@ -34,7 +34,7 @@ def get_folder_tree():
} }
@folderbp.route("/folder/dir-browser", methods=["POST"]) @api.route("/folder/dir-browser", methods=["POST"])
def list_folders(): def list_folders():
""" """
Returns a list of all the folders in the given folder. Returns a list of all the folders in the given folder.
@ -44,10 +44,10 @@ def list_folders():
try: try:
req_dir: str = data["folder"] req_dir: str = data["folder"]
except KeyError: except KeyError:
req_dir = settings.HOME_DIR req_dir = settings.USER_HOME_DIR
if req_dir == "$home": if req_dir == "$home":
req_dir = settings.HOME_DIR req_dir = settings.USER_HOME_DIR
entries = os.scandir(req_dir) entries = os.scandir(req_dir)

View File

@ -1,14 +1,13 @@
import os
from pathlib import Path from pathlib import Path
from flask import Blueprint, send_from_directory from flask import Blueprint, send_from_directory
imgbp = Blueprint("imgserver", __name__, url_prefix="/img") from app.settings import APP_DIR
api = Blueprint("imgserver", __name__, url_prefix="/img")
SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg") SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg")
HOME = os.path.expanduser("~") APP_DIR = Path(APP_DIR)
APP_DIR = Path(HOME) / ".swing"
IMG_PATH = APP_DIR / "images" IMG_PATH = APP_DIR / "images"
ASSETS_PATH = APP_DIR / "assets" ASSETS_PATH = APP_DIR / "assets"
@ -23,7 +22,7 @@ ARTIST_SM_PATH = ARTIST_PATH / "small"
PLAYLIST_PATH = IMG_PATH / "playlists" PLAYLIST_PATH = IMG_PATH / "playlists"
@imgbp.route("/") @api.route("/")
def hello(): def hello():
return "<h1>Image Server</h1>" return "<h1>Image Server</h1>"
@ -37,7 +36,7 @@ def send_fallback_img(filename: str = "default.webp"):
return send_from_directory(ASSETS_PATH, filename) return send_from_directory(ASSETS_PATH, filename)
@imgbp.route("/t/<imgpath>") @api.route("/t/<imgpath>")
def send_lg_thumbnail(imgpath: str): def send_lg_thumbnail(imgpath: str):
fpath = LG_THUMB_PATH / imgpath fpath = LG_THUMB_PATH / imgpath
@ -47,7 +46,7 @@ def send_lg_thumbnail(imgpath: str):
return send_fallback_img() return send_fallback_img()
@imgbp.route("/t/s/<imgpath>") @api.route("/t/s/<imgpath>")
def send_sm_thumbnail(imgpath: str): def send_sm_thumbnail(imgpath: str):
fpath = SM_THUMB_PATH / imgpath fpath = SM_THUMB_PATH / imgpath
@ -57,7 +56,7 @@ def send_sm_thumbnail(imgpath: str):
return send_fallback_img() return send_fallback_img()
@imgbp.route("/a/<imgpath>") @api.route("/a/<imgpath>")
def send_lg_artist_image(imgpath: str): def send_lg_artist_image(imgpath: str):
fpath = ARTIST_LG_PATH / imgpath fpath = ARTIST_LG_PATH / imgpath
@ -67,7 +66,7 @@ def send_lg_artist_image(imgpath: str):
return send_fallback_img("artist.webp") return send_fallback_img("artist.webp")
@imgbp.route("/a/s/<imgpath>") @api.route("/a/s/<imgpath>")
def send_sm_artist_image(imgpath: str): def send_sm_artist_image(imgpath: str):
fpath = ARTIST_SM_PATH / imgpath fpath = ARTIST_SM_PATH / imgpath
@ -77,7 +76,7 @@ def send_sm_artist_image(imgpath: str):
return send_fallback_img("artist.webp") return send_fallback_img("artist.webp")
@imgbp.route("/p/<imgpath>") @api.route("/p/<imgpath>")
def send_playlist_image(imgpath: str): def send_playlist_image(imgpath: str):
fpath = PLAYLIST_PATH / imgpath fpath = PLAYLIST_PATH / imgpath

View File

@ -13,7 +13,7 @@ from app.db.store import Store
from app.lib import playlistlib from app.lib import playlistlib
from app.utils import create_new_date, remove_duplicates from app.utils import create_new_date, remove_duplicates
playlistbp = Blueprint("playlist", __name__, url_prefix="/") api = Blueprint("playlist", __name__, url_prefix="/")
PL = SQLitePlaylistMethods PL = SQLitePlaylistMethods
@ -30,7 +30,7 @@ delete_playlist = PL.delete_playlist
# get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes # get_tracks_by_trackhashes = SQLiteTrackMethods.get_tracks_by_trackhashes
@playlistbp.route("/playlists", methods=["GET"]) @api.route("/playlists", methods=["GET"])
def send_all_playlists(): def send_all_playlists():
""" """
Gets all the playlists. Gets all the playlists.
@ -46,7 +46,7 @@ def send_all_playlists():
return {"data": playlists} return {"data": playlists}
@playlistbp.route("/playlist/new", methods=["POST"]) @api.route("/playlist/new", methods=["POST"])
def create_playlist(): def create_playlist():
""" """
Creates a new playlist. Accepts POST method with a JSON body. Creates a new playlist. Accepts POST method with a JSON body.
@ -79,7 +79,7 @@ def create_playlist():
return {"playlist": playlist}, 201 return {"playlist": playlist}, 201
@playlistbp.route("/playlist/<playlist_id>/add", methods=["POST"]) @api.route("/playlist/<playlist_id>/add", methods=["POST"])
def add_track_to_playlist(playlist_id: str): def add_track_to_playlist(playlist_id: str):
""" """
Takes a playlist ID and a track hash, and adds the track to the playlist Takes a playlist ID and a track hash, and adds the track to the playlist
@ -102,7 +102,7 @@ def add_track_to_playlist(playlist_id: str):
return {"msg": "Done"}, 200 return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<playlistid>") @api.route("/playlist/<playlistid>")
def get_playlist(playlistid: str): def get_playlist(playlistid: str):
""" """
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them. Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
@ -123,7 +123,7 @@ def get_playlist(playlistid: str):
return {"info": playlist, "tracks": tracks} return {"info": playlist, "tracks": tracks}
@playlistbp.route("/playlist/<playlistid>/update", methods=["PUT"]) @api.route("/playlist/<playlistid>/update", methods=["PUT"])
def update_playlist_info(playlistid: str): def update_playlist_info(playlistid: str):
if playlistid is None: if playlistid is None:
return {"error": "Playlist ID not provided"}, 400 return {"error": "Playlist ID not provided"}, 400
@ -188,7 +188,7 @@ def update_playlist_info(playlistid: str):
# return {"data": artists} # return {"data": artists}
@playlistbp.route("/playlist/delete", methods=["POST"]) @api.route("/playlist/delete", methods=["POST"])
def remove_playlist(): def remove_playlist():
""" """
Deletes a playlist by ID. Deletes a playlist by ID.
@ -209,7 +209,7 @@ def remove_playlist():
return {"msg": "Done"}, 200 return {"msg": "Done"}, 200
@playlistbp.route("/playlist/<pid>/set-image-pos", methods=["POST"]) @api.route("/playlist/<pid>/set-image-pos", methods=["POST"])
def update_image_position(pid: int): def update_image_position(pid: int):
data = request.get_json() data = request.get_json()
message = {"msg": "No data provided"} message = {"msg": "No data provided"}

View File

@ -8,7 +8,7 @@ from app import models, utils
from app.db.store import Store from app.db.store import Store
from app.lib import searchlib from app.lib import searchlib
searchbp = Blueprint("search", __name__, url_prefix="/") api = Blueprint("search", __name__, url_prefix="/")
SEARCH_COUNT = 12 SEARCH_COUNT = 12
@ -95,7 +95,7 @@ class DoSearch:
# self.search_playlists() # self.search_playlists()
@searchbp.route("/search/tracks", methods=["GET"]) @api.route("/search/tracks", methods=["GET"])
def search_tracks(): def search_tracks():
""" """
Searches for tracks that match the search query. Searches for tracks that match the search query.
@ -113,7 +113,7 @@ def search_tracks():
} }
@searchbp.route("/search/albums", methods=["GET"]) @api.route("/search/albums", methods=["GET"])
def search_albums(): def search_albums():
""" """
Searches for albums. Searches for albums.
@ -131,7 +131,7 @@ def search_albums():
} }
@searchbp.route("/search/artists", methods=["GET"]) @api.route("/search/artists", methods=["GET"])
def search_artists(): def search_artists():
""" """
Searches for artists. Searches for artists.
@ -167,7 +167,7 @@ def search_artists():
# } # }
@searchbp.route("/search/top", methods=["GET"]) @api.route("/search/top", methods=["GET"])
def get_top_results(): def get_top_results():
""" """
Returns the top results for the search query. Returns the top results for the search query.
@ -188,7 +188,7 @@ def get_top_results():
} }
@searchbp.route("/search/loadmore") @api.route("/search/loadmore")
def search_load_more(): def search_load_more():
""" """
Returns more songs, albums or artists from a search query. Returns more songs, albums or artists from a search query.

View File

@ -1,10 +1,17 @@
from flask import Blueprint, request from flask import Blueprint, request
from app.db.sqlite.settings import SettingsSQLMethods as sdb from app.db.sqlite.settings import SettingsSQLMethods as sdb
settingsbp = Blueprint("settings", __name__, url_prefix="/") api = Blueprint("settings", __name__, url_prefix="/")
@settingsbp.route("/settings/add-root-dirs", methods=["POST"]) def get_child_dirs(parent: str, children: list[str]):
"""Returns child directories in a list, given a parent directory"""
return [dir for dir in children if dir.startswith(parent)]
@api.route("/settings/add-root-dirs", methods=["POST"])
def add_root_dirs(): def add_root_dirs():
""" """
Add custom root directories to the database. Add custom root directories to the database.
@ -17,18 +24,26 @@ def add_root_dirs():
return msg, 400 return msg, 400
try: try:
new_dirs = data["new_dirs"] new_dirs: list[str] = data["new_dirs"]
removed_dirs = data["removed"] removed_dirs: list[str] = data["removed"]
except KeyError: except KeyError:
return msg, 400 return msg, 400
# --- Unregister child directories ---
db_dirs = sdb.get_root_dirs()
for _dir in new_dirs:
children = get_child_dirs(_dir, db_dirs)
removed_dirs.extend(children)
# ------------------------------------
sdb.add_root_dirs(new_dirs) sdb.add_root_dirs(new_dirs)
sdb.remove_root_dirs(removed_dirs) sdb.remove_root_dirs(removed_dirs)
return {"msg": "Added root directories to the database."} return {"msg": "Updated!"}
@settingsbp.route("/settings/get-root-dirs", methods=["GET"]) @api.route("/settings/get-root-dirs", methods=["GET"])
def get_root_dirs(): def get_root_dirs():
""" """
Get custom root directories from the database. Get custom root directories from the database.
@ -39,7 +54,7 @@ def get_root_dirs():
# CURRENTLY UNUSED ROUTE 👇 # CURRENTLY UNUSED ROUTE 👇
@settingsbp.route("/settings/remove-root-dirs", methods=["POST"]) @api.route("/settings/remove-root-dirs", methods=["POST"])
def remove_root_dirs(): def remove_root_dirs():
""" """
Remove custom root directories from the database. Remove custom root directories from the database.

View File

@ -2,12 +2,13 @@
Contains all the track routes. Contains all the track routes.
""" """
from flask import Blueprint, send_file from flask import Blueprint, send_file
from app.db.store import Store from app.db.store import Store
trackbp = Blueprint("track", __name__, url_prefix="/") api = Blueprint("track", __name__, url_prefix="/")
@trackbp.route("/file/<trackhash>") @api.route("/file/<trackhash>")
def send_track_file(trackhash: str): def send_track_file(trackhash: str):
""" """
Returns an audio file that matches the passed id to the client. Returns an audio file that matches the passed id to the client.

View File

@ -69,13 +69,12 @@ class ProcessArtistColors:
""" """
def __init__(self) -> None: def __init__(self) -> None:
db_colors: list[tuple] = list(adb.get_all_artists())
db_artisthashes = "-".join([artist[1] for artist in db_colors])
all_artists = Store.artists all_artists = Store.artists
if all_artists is None:
return
for artist in tqdm(all_artists, desc="Processing artist colors"): for artist in tqdm(all_artists, desc="Processing artist colors"):
if len(artist.colors) == 0: if artist.artisthash not in db_artisthashes:
self.process_color(artist) self.process_color(artist)
@staticmethod @staticmethod
@ -90,5 +89,3 @@ class ProcessArtistColors:
if len(colors) > 0: if len(colors) > 0:
adb.insert_one_artist(artisthash=artist.artisthash, colors=colors) adb.insert_one_artist(artisthash=artist.artisthash, colors=colors)
Store.map_artist_color((0, artist.artisthash, json.dumps(colors))) Store.map_artist_color((0, artist.artisthash, json.dumps(colors)))
# TODO: Load album and artist colors into the store.

View File

@ -0,0 +1,3 @@
"""
Migrations module
"""

View File

@ -1,16 +1,42 @@
""" """
Contains default configs Contains default configs
""" """
import multiprocessing
import os import os
# ------- 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 = os.path.join(os.environ.get("HOME"), ".config")
if os.path.exists(alt_dir):
return alt_dir
except TypeError:
return os.path.expanduser("~")
# ------- HELPER METHODS --------
APP_VERSION = "Swing v.1.0.0.beta.1" APP_VERSION = "Swing v.1.0.0.beta.1"
# paths # paths
CONFIG_FOLDER = ".swing" XDG_CONFIG_DIR = get_xdg_config_dir()
HOME_DIR = os.path.expanduser("~") USER_HOME_DIR = os.path.expanduser("~")
APP_DIR = os.path.join(HOME_DIR, CONFIG_FOLDER) CONFIG_FOLDER = "swing" if XDG_CONFIG_DIR != USER_HOME_DIR else ".swing"
APP_DIR = os.path.join(XDG_CONFIG_DIR, CONFIG_FOLDER)
IMG_PATH = os.path.join(APP_DIR, "images") IMG_PATH = os.path.join(APP_DIR, "images")
ARTIST_IMG_PATH = os.path.join(IMG_PATH, "artists") ARTIST_IMG_PATH = os.path.join(IMG_PATH, "artists")
@ -37,8 +63,6 @@ DEFAULT_ARTIST_IMG = IMG_ARTIST_URI + "0.webp"
LAST_FM_API_KEY = "762db7a44a9e6fb5585661f5f2bdf23a" LAST_FM_API_KEY = "762db7a44a9e6fb5585661f5f2bdf23a"
CPU_COUNT = multiprocessing.cpu_count()
THUMB_SIZE = 400 THUMB_SIZE = 400
SM_THUMB_SIZE = 64 SM_THUMB_SIZE = 64
SM_ARTIST_IMG_SIZE = 64 SM_ARTIST_IMG_SIZE = 64
@ -46,24 +70,9 @@ SM_ARTIST_IMG_SIZE = 64
The size of extracted images in pixels The size of extracted images in pixels
""" """
LOGGER_ENABLE: bool = True
FILES = ["flac", "mp3", "wav", "m4a"] FILES = ["flac", "mp3", "wav", "m4a"]
SUPPORTED_FILES = tuple(f".{file}" for file in FILES) SUPPORTED_FILES = tuple(f".{file}" for file in FILES)
SUPPORTED_IMAGES = (".jpg", ".png", ".webp", ".jpeg")
SUPPORTED_DIR_IMAGES = [
"folder",
"cover",
"album",
"front",
]
# ===== DB =========
USE_MONGO = False
# ===== SQLite ===== # ===== SQLite =====
APP_DB_NAME = "swing.db" APP_DB_NAME = "swing.db"
USER_DATA_DB_NAME = "userdata.db" USER_DATA_DB_NAME = "userdata.db"

View File

@ -4,7 +4,6 @@ Contains the functions to prepare the server for use.
import os import os
import shutil import shutil
from configparser import ConfigParser from configparser import ConfigParser
import caribou # pylint: disable=import-error
from app import settings from app import settings
from app.db.sqlite import create_connection, create_tables, queries from app.db.sqlite import create_connection, create_tables, queries
@ -64,10 +63,6 @@ def create_config_dir() -> None:
""" """
Creates the config directory if it doesn't exist. Creates the config directory if it doesn't exist.
""" """
home_dir = os.path.expanduser("~")
config_folder = os.path.join(home_dir, settings.CONFIG_FOLDER)
thumb_path = os.path.join("images", "thumbnails") thumb_path = os.path.join("images", "thumbnails")
small_thumb_path = os.path.join(thumb_path, "small") small_thumb_path = os.path.join(thumb_path, "small")
large_thumb_path = os.path.join(thumb_path, "large") large_thumb_path = os.path.join(thumb_path, "large")
@ -91,7 +86,7 @@ def create_config_dir() -> None:
] ]
for _dir in dirs: for _dir in dirs:
path = os.path.join(config_folder, _dir) path = os.path.join(settings.APP_DIR, _dir)
exists = os.path.exists(path) exists = os.path.exists(path)
if not exists: if not exists:
@ -114,19 +109,6 @@ def setup_sqlite():
create_tables(app_db_conn, queries.CREATE_APPDB_TABLES) create_tables(app_db_conn, queries.CREATE_APPDB_TABLES)
create_tables(playlist_db_conn, queries.CREATE_USERDATA_TABLES) create_tables(playlist_db_conn, queries.CREATE_USERDATA_TABLES)
userdb_migrations = get_home_res_path("app") / "migrations" / "userdata"
maindb_migrations = get_home_res_path("app") / "migrations" / "main"
caribou.upgrade(
APP_DB_PATH,
maindb_migrations,
)
caribou.upgrade(
str(USERDATA_DB_PATH),
str(userdb_migrations),
)
app_db_conn.close() app_db_conn.close()
playlist_db_conn.close() playlist_db_conn.close()

View File

@ -5,6 +5,7 @@ from pathlib import Path
from datetime import datetime from datetime import datetime
import os import os
import platform
import socket as Socket import socket as Socket
import hashlib import hashlib
import threading import threading
@ -38,19 +39,22 @@ def run_fast_scandir(__dir: str, full=False) -> tuple[list[str], list[str]]:
subfolders = [] subfolders = []
files = [] files = []
for f in os.scandir(__dir): try:
if f.is_dir() and not f.name.startswith("."): for _files in os.scandir(__dir):
subfolders.append(f.path) if _files.is_dir() and not _files.name.startswith("."):
if f.is_file(): subfolders.append(_files.path)
ext = os.path.splitext(f.name)[1].lower() if _files.is_file():
ext = os.path.splitext(_files.name)[1].lower()
if ext in SUPPORTED_FILES: if ext in SUPPORTED_FILES:
files.append(f.path) files.append(_files.path)
if full or len(files) == 0: if full or len(files) == 0:
for _dir in list(subfolders): for _dir in list(subfolders):
sf, f = run_fast_scandir(_dir, full=True) sub_dirs, _files = run_fast_scandir(_dir, full=True)
subfolders.extend(sf) subfolders.extend(sub_dirs)
files.extend(f) files.extend(_files)
except PermissionError:
return [], []
return subfolders, files return subfolders, files
@ -239,3 +243,10 @@ def get_ip():
soc.close() soc.close()
return ip_address return ip_address
def is_windows():
"""
Returns True if the OS is Windows.
"""
return platform.system() == "Windows"

View File

@ -13,7 +13,7 @@ from app.functions import run_periodic_checks
from app.lib.watchdogg import Watcher as WatchDog from app.lib.watchdogg import Watcher as WatchDog
from app.settings import APP_VERSION, HELP_MESSAGE, TCOLOR from app.settings import APP_VERSION, HELP_MESSAGE, TCOLOR
from app.setup import run_setup from app.setup import run_setup
from app.utils import background, get_home_res_path, get_ip from app.utils import background, get_home_res_path, get_ip, is_windows
werkzeug = logging.getLogger("werkzeug") werkzeug = logging.getLogger("werkzeug")
werkzeug.setLevel(logging.ERROR) werkzeug.setLevel(logging.ERROR)
@ -80,6 +80,8 @@ class HandleArgs:
config["DEFAULT"]["BUILD"] = "True" config["DEFAULT"]["BUILD"] = "True"
config.write(file) config.write(file)
_s = ";" if is_windows() else ":"
bundler.run( bundler.run(
[ [
"manage.py", "manage.py",
@ -87,9 +89,10 @@ class HandleArgs:
"--name", "--name",
"swingmusic", "swingmusic",
"--clean", "--clean",
"--add-data=assets:assets", f"--add-data=assets{_s}assets",
"--add-data=client:client", f"--add-data=client{_s}client",
"--add-data=pyinstaller.config.ini:.", f"--add-data=app/migrations{_s}app/migrations",
f"--add-data=pyinstaller.config.ini{_s}.",
"-y", "-y",
] ]
) )

46
poetry.lock generated
View File

@ -12,23 +12,11 @@ files = [
{file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, {file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"},
] ]
[[package]]
name = "argparse"
version = "1.4.0"
description = "Python command-line parsing library"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "argparse-1.4.0-py2.py3-none-any.whl", hash = "sha256:c31647edb69fd3d465a847ea3157d37bed1f95f19760b11a47aa91c04b666314"},
{file = "argparse-1.4.0.tar.gz", hash = "sha256:62b089a55be1d8949cd2bc7e0df0bddb9e028faefc8c32038cc84862aefdd6e4"},
]
[[package]] [[package]]
name = "astroid" name = "astroid"
version = "2.12.12" version = "2.12.12"
description = "An abstract syntax tree for Python with inference support." description = "An abstract syntax tree for Python with inference support."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
@ -107,20 +95,6 @@ d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"] uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "caribou"
version = "0.3"
description = "python migrations for sqlite databases"
category = "main"
optional = false
python-versions = "*"
files = [
{file = "caribou-0.3.0.tar.gz", hash = "sha256:5ca6e6e6ad7d3175137c68d809e203fbd931f79163a5613808a789449fef7863"},
]
[package.dependencies]
argparse = ">=1.0.0"
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2022.5.18.1" version = "2022.5.18.1"
@ -194,7 +168,7 @@ pillow = ">=3.3.1"
name = "dill" name = "dill"
version = "0.3.6" version = "0.3.6"
description = "serialize all of python" description = "serialize all of python"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@ -351,7 +325,7 @@ files = [
name = "isort" name = "isort"
version = "5.10.1" version = "5.10.1"
description = "A Python utility / library to sort Python imports." description = "A Python utility / library to sort Python imports."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6.1,<4.0" python-versions = ">=3.6.1,<4.0"
files = [ files = [
@ -399,7 +373,7 @@ i18n = ["Babel (>=2.7)"]
name = "lazy-object-proxy" name = "lazy-object-proxy"
version = "1.8.0" version = "1.8.0"
description = "A fast and thorough lazy object proxy." description = "A fast and thorough lazy object proxy."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@ -493,7 +467,7 @@ files = [
name = "mccabe" name = "mccabe"
version = "0.7.0" version = "0.7.0"
description = "McCabe checker, plugin for flake8" description = "McCabe checker, plugin for flake8"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@ -630,7 +604,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
name = "platformdirs" name = "platformdirs"
version = "2.5.2" version = "2.5.2"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@ -720,7 +694,7 @@ files = [
name = "pylint" name = "pylint"
version = "2.15.5" version = "2.15.5"
description = "python code static checker" description = "python code static checker"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.7.2" python-versions = ">=3.7.2"
files = [ files = [
@ -988,7 +962,7 @@ files = [
name = "tomlkit" name = "tomlkit"
version = "0.11.6" version = "0.11.6"
description = "Style preserving TOML library" description = "Style preserving TOML library"
category = "main" category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
files = [ files = [
@ -1106,7 +1080,7 @@ watchdog = ["watchdog"]
name = "wrapt" name = "wrapt"
version = "1.14.1" version = "1.14.1"
description = "Module for decorators, wrappers and monkey patching." description = "Module for decorators, wrappers and monkey patching."
category = "main" category = "dev"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
files = [ files = [
@ -1179,4 +1153,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.10,<3.12" python-versions = ">=3.10,<3.12"
content-hash = "83ce6d95a8e6bdefff93a3badca9e7b7b53174eaea85f908e9cea9a37afaec7c" content-hash = "6f6126fbaa8f114c90cef637e0bc996d63d3dc93f6ed2e90e36ef696cdd0cfc5"

View File

@ -20,7 +20,6 @@ hypothesis = "^6.56.3"
pytest = "^7.1.3" pytest = "^7.1.3"
Unidecode = "^1.3.6" Unidecode = "^1.3.6"
pyinstaller = "^5.7.0" pyinstaller = "^5.7.0"
caribou = "^0.3.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pylint = "^2.15.5" pylint = "^2.15.5"