redesign progressbars

+ hide some progressbars
+ rewrite telemetry into a class
+ remove obsolete start info logs
+ update contributing.md to include contributing.md
+ send posthog event in a bg thread
+ related side effects
This commit is contained in:
mungai-njoroge 2023-09-27 17:18:52 +03:00
parent 54714a224b
commit 4f757e989f
20 changed files with 144 additions and 130 deletions

View File

@ -39,7 +39,9 @@ cd swingmusic
poetry install
```
Run the server. You can use a different port if you have another Swing Music instance running on the default port `1970`.
You need a LastFM API key which you can get on the [API accounts page](https://www.last.fm/api/accounts). Then set it as an environment variable under the name: `LASTFM_API_KEY`.
Finally, run the server. You can use a different port if you have another Swing Music instance running on port `1970`.
```sh
poetry run python manage.py --port 1980

View File

@ -15,17 +15,23 @@ from app.serializers.track import serialize_tracks
from app.store.albums import AlbumStore
from app.store.artists import ArtistStore
from app.store.tracks import TrackStore
from app import telemetry
from app.telemetry import Telemetry
from app.utils.threading import background
api = Blueprint("artist", __name__, url_prefix="/")
@background
def send_event():
Telemetry.send_artist_visited()
@api.route("/artist/<artisthash>", methods=["GET"])
def get_artist(artisthash: str):
"""
Get artist data.
"""
telemetry.send_artist_visited()
send_event()
limit = request.args.get("limit")
if limit is None:

View File

@ -34,6 +34,9 @@ class HandleArgs:
Runs Pyinstaller.
"""
if ALLARGS.build not in ARGS:
return
if settings.IS_BUILD:
print("Catch me if you can! 😆💬")
sys.exit(0)
@ -49,7 +52,6 @@ class HandleArgs:
log.error("ERROR: POSTHOG_API_KEY not set in environment")
sys.exit(0)
if ALLARGS.build in ARGS:
try:
with open("./app/configs.py", "w", encoding="utf-8") as file:
# copy the api keys to the config file

View File

@ -28,8 +28,6 @@ class SettingsSQLMethods:
if settings is None:
return []
# print
# omit id, root_dirs, and exclude_dirs
return settings[3:]

View File

@ -8,12 +8,12 @@ import requests
from PIL import Image, UnidentifiedImageError
from requests.exceptions import ConnectionError as RequestConnectionError
from requests.exceptions import ReadTimeout
from tqdm import tqdm
from app import settings
from app.models import Album, Artist, Track
from app.store import artists as artist_store
from app.utils.hashing import create_hash
from app.utils.progressbar import tqdm
CHECK_ARTIST_IMAGES_KEY = ""

View File

@ -6,7 +6,6 @@ import json
from pathlib import Path
import colorgram
from tqdm import tqdm
from app import settings
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as aldb
@ -17,6 +16,7 @@ from app.store.artists import ArtistStore
from app.store.albums import AlbumStore
from app.logger import log
from app.lib.errors import PopulateCancelledError
from app.utils.progressbar import tqdm
PROCESS_ALBUM_COLORS_KEY = ""
PROCESS_ARTIST_COLORS_KEY = ""

View File

@ -5,7 +5,6 @@ from typing import Generator
from requests import ConnectionError as RequestConnectionError
from requests import ReadTimeout
from tqdm import tqdm
from app import settings
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
@ -28,6 +27,7 @@ from app.store.artists import ArtistStore
from app.store.tracks import TrackStore
from app.utils.filesystem import run_fast_scandir
from app.utils.network import has_connection
from app.utils.progressbar import tqdm
get_all_tracks = SQLiteTrackMethods.get_all_tracks
insert_many_tracks = SQLiteTrackMethods.insert_many_tracks

View File

@ -3,11 +3,10 @@ This library contains all the functions related to tracks.
"""
import os
from tqdm import tqdm
from app.db.sqlite.tracks import SQLiteTrackMethods as tdb
from app.store.tracks import TrackStore
from app.utils.progressbar import tqdm
def validate_tracks() -> None:
"""

View File

@ -2,6 +2,7 @@
Logger module
"""
from app.settings import IS_BUILD
import logging

View File

@ -104,7 +104,7 @@ class Album:
"""
keywords = ["motion picture", "soundtrack"]
for keyword in keywords:
if keyword in self.title.lower():
if keyword in self.og_title.lower():
return True
return False
@ -145,7 +145,7 @@ class Album:
"""
keywords = ["live from", "live at", "live in", "live on", "mtv unplugged"]
for keyword in keywords:
if keyword in self.title.lower():
if keyword in self.og_title.lower():
return True
return False
@ -165,7 +165,7 @@ class Album:
show_albums_as_singles = get_flag(SessionVarKeys.SHOW_ALBUMS_AS_SINGLES)
for keyword in keywords:
if keyword in self.title.lower():
if keyword in self.og_title.lower():
self.is_single = True
return

View File

@ -13,7 +13,7 @@ help_args_list = [
[
"--scan-interval",
"-psi",
"Set the periodic scan interval in seconds. Default is 600 seconds (10 minutes)",
"Set the scan interval in seconds. Default 600s (10 minutes)",
],
[
"--build",
@ -26,9 +26,7 @@ HELP_MESSAGE = f"""
Swing Music is a beautiful, self-hosted music player for your
local audio files. Like a cooler Spotify ... but bring your own music.
Usage: swingmusic [options]
Usage: swingmusic [options] [args]
{tabulate(help_args_list, headers=["Option", "Short", "Description"], tablefmt="markdown", maxcolwidths=[None, None, 44])}
{tabulate(help_args_list, headers=["Option", "Short", "Description"], tablefmt="simple_grid", maxcolwidths=[None, None, 40])}
"""
"80s, 90s, the noughties and today"

View File

@ -6,7 +6,6 @@ import sys
from typing import Any
from app import configs
from app.logger import log
join = os.path.join
@ -241,14 +240,10 @@ class Keys:
cls.LASTFM_API = configs.LASTFM_API_KEY
cls.POSTHOG_API_KEY = configs.POSTHOG_API_KEY
cls.verify_exists()
cls.verify_keys()
@classmethod
def verify_exists(cls):
def verify_keys(cls):
if not cls.LASTFM_API:
log.error("ERROR: LASTFM_API_KEY not set in environment")
sys.exit(0)
if not cls.POSTHOG_API_KEY:
log.error("ERROR: POSTHOG_API_KEY not set in environment")
print("ERROR: LASTFM_API_KEY not set in environment")
sys.exit(0)

View File

@ -1,7 +1,6 @@
import os
from app.settings import (FLASKVARS, TCOLOR, Paths, Release, SessionVarKeys,
get_flag)
from app.settings import FLASKVARS, TCOLOR, Paths, Release
from app.utils.network import get_ip
@ -25,27 +24,8 @@ def log_startup_info():
f"{TCOLOR.OKGREEN}http://{address}:{FLASKVARS.get_flask_port()}{TCOLOR.ENDC}"
)
print(lines)
print("\n")
print(lines+"\n")
to_print = [
[
"Extract featured artists from titles",
get_flag(SessionVarKeys.EXTRACT_FEAT)
],
[
"Remove prod. from titles",
get_flag(SessionVarKeys.REMOVE_PROD)
]
]
for item in to_print:
print(
f"{item[0]}: {TCOLOR.FAIL}{item[1]}{TCOLOR.ENDC}"
)
print(
f"{TCOLOR.YELLOW}Data folder: {Paths.get_app_dir()}{TCOLOR.ENDC}"
)
print(f"{TCOLOR.YELLOW}Data folder: {Paths.get_app_dir()}{TCOLOR.ENDC}")
print("\n")

View File

@ -1,13 +1,13 @@
import json
import random
from tqdm import tqdm
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as aldb
from app.models import Album, Track
from ..utils.hashing import create_hash
from .tracks import TrackStore
from app.utils.progressbar import tqdm
ALBUM_LOAD_KEY = ""
@ -49,7 +49,7 @@ class AlbumStore:
db_albums: list[tuple] = aldb.get_all_albums()
for album in tqdm(db_albums, desc="Mapping album colors"):
for album in db_albums:
albumhash = album[1]
colors = json.loads(album[2])

View File

@ -1,11 +1,10 @@
import json
from tqdm import tqdm
from app.db.sqlite.artistcolors import SQLiteArtistMethods as ardb
from app.lib.artistlib import get_all_artists
from app.models import Artist
from app.utils.bisection import UseBisection
from app.utils.progressbar import tqdm
from .albums import AlbumStore
from .tracks import TrackStore
@ -26,9 +25,7 @@ class ArtistStore:
cls.artists = get_all_artists(TrackStore.tracks, AlbumStore.albums)
# db_artists: list[tuple] = list(ardb.get_all_artists())
for artist in tqdm(ardb.get_all_artists(), desc="Loading artists"):
for artist in ardb.get_all_artists():
if instance_key != ARTIST_LOAD_KEY:
return

View File

@ -1,10 +1,11 @@
from tqdm import tqdm
# from tqdm import tqdm
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.sqlite.tracks import SQLiteTrackMethods as tdb
from app.models import Track
from app.utils.bisection import UseBisection
from app.utils.remove_duplicates import remove_duplicates
from app.utils.progressbar import tqdm
TRACKS_LOAD_KEY = ""
@ -25,6 +26,7 @@ class TrackStore:
fav_hashes = favdb.get_fav_tracks()
fav_hashes = " ".join([t[1] for t in fav_hashes])
print("\n") # adds space between progress bars and startup info
for track in tqdm(cls.tracks, desc="Loading tracks"):
if instance_key != TRACKS_LOAD_KEY:
return

View File

@ -2,55 +2,69 @@ import sys
import uuid as UUID
from posthog import Posthog
from app.settings import Paths, Keys
from app.logger import log
from app.settings import Keys, Paths, Release
from app.utils.hashing import create_hash
from app.utils.network import has_connection
from app.logger import log
USER_ID = ""
class Telemetry:
"""
Handles sending telemetry data to posthog.
"""
try:
posthog = Posthog(
user_id = ""
off = False
@classmethod
def init(cls) -> None:
try:
cls.posthog = Posthog(
project_api_key=Keys.POSTHOG_API_KEY,
host="https://app.posthog.com",
disable_geoip=False,
timeout=30,
)
except AssertionError:
log.error("ERROR: POSTHOG_API_KEY not set in environment")
sys.exit(0)
cls.create_userid()
except AssertionError:
cls.disable_telemetry()
def create_userid():
@classmethod
def create_userid(cls):
"""
Creates a unique user id for the user and saves it to a file.
"""
uuid_path = Paths.get_app_dir() + "/userid.txt"
global USER_ID
try:
with open(uuid_path, "r") as f:
USER_ID = f.read().strip()
cls.user_id = f.read().strip()
except FileNotFoundError:
uuid = str(UUID.uuid4())
USER_ID = "user_" + create_hash(uuid, limit=15)
cls.user_id = "user_" + create_hash(uuid, limit=15)
with open(uuid_path, "w") as f:
f.write(USER_ID)
f.write(cls.user_id)
@classmethod
def disable_telemetry(cls):
cls.off = True
def send_event(event: str):
@classmethod
def send_event(cls, event: str):
"""
Sends an event to posthog.
"""
global USER_ID
if cls.off:
return
if has_connection():
posthog.capture(USER_ID, event=f"v1.3.0-{event}")
cls.posthog.capture(cls.user_id, event=f"v{Release.APP_VERSION}-{event}")
def send_artist_visited():
@classmethod
def send_artist_visited(cls):
"""
Sends an event to posthog when an artist page is visited.
"""
send_event("artist-page-visited")
cls.send_event("artist-page-visited")

16
app/utils/progressbar.py Normal file
View File

@ -0,0 +1,16 @@
from tqdm import tqdm as _tqdm
def tqdm(*args, **kwargs):
"""
Wrapper for tqdm that sets globals.
"""
bar_format = "{percentage:3.0f}%|{bar:45}|{n_fmt}/{total_fmt}{desc}"
kwargs["bar_format"] = bar_format
if "desc" in kwargs:
print(f'INFO|{kwargs["desc"].capitalize()} ...')
kwargs["desc"] = ""
return _tqdm(*args, **kwargs)

View File

@ -3,8 +3,7 @@ import threading
def background(func):
"""
a threading decorator
use @background above the function you want to run in the background
Runs the decorated function in a background thread.
"""
def background_func(*a, **kw):

View File

@ -8,7 +8,7 @@ import os
import setproctitle
from flask import request
from app import telemetry
from app.telemetry import Telemetry
from app.api import create_api
from app.arg_handler import HandleArgs
from app.lib.watchdogg import Watcher as WatchDog
@ -79,13 +79,18 @@ def start_watchdog():
WatchDog().run()
@background
def init_telemetry():
Telemetry.init()
def run_swingmusic():
Keys.load()
HandleArgs()
log_startup_info()
bg_run_setup()
start_watchdog()
telemetry.create_userid()
init_telemetry()
setproctitle.setproctitle(
f"swingmusic - {FLASKVARS.FLASK_HOST}:{FLASKVARS.FLASK_PORT}"