Compare commits

...

13 Commits

Author SHA1 Message Date
thecookingsenpai
f66bca67ac Testing with SHA1 gives positive results, also changed the truncation rule 2024-02-02 16:31:18 +01:00
thecookingsenpai
f0a8aa02a9 opted for sha1 2024-02-01 20:32:31 +01:00
thecookingsenpai
077661b73c lighter md5 method to be nicer on older cpus 2024-02-01 20:24:42 +01:00
thecookingsenpai
5a918d078e added some handy launchers 2024-01-31 19:59:36 +01:00
thecookingsenpai
bc51150e47 failproof online check (fixes vpns and dns tunnels) 2024-01-31 18:25:20 +01:00
thecookingsenpai
c153247918 API switch to unrestricted ones 2024-01-31 18:25:01 +01:00
thecookingsenpai
632447bdba Experimental Kerve API use 2024-01-30 15:51:46 +01:00
thecookingsenpai
e0581259a6 Just some formatting change and utilities for the next step 2024-01-30 15:51:05 +01:00
thecookingsenpai
d411f04e98 Added Trunk configuration for those using trunk (suggested: makes the code way cleaner and easier to read) 2024-01-30 15:49:47 +01:00
thecookingsenpai
27a277cc5b Organization and usefulness for the final user 2024-01-30 15:49:06 +01:00
Mungai Njoroge
9bf005668b
Install tzdata in Dockerfile 2024-01-30 08:55:20 +03:00
Mungai Njoroge
cb47a4bc6e
Update hashing.py 2024-01-22 22:38:01 +03:00
mungai-njoroge
8c44aeff3d update create_hash docstring 2024-01-22 22:35:27 +03:00
20 changed files with 182 additions and 21 deletions

3
.gitignore vendored
View File

@ -29,4 +29,5 @@ logs.txt
TODO.md
testdata.py
test.py
test.py
nohup.out

View File

@ -0,0 +1,4 @@
# Following source doesn't work in most setups
ignored:
- SC1090
- SC1091

View File

@ -0,0 +1,2 @@
[settings]
profile=black

View File

@ -0,0 +1,10 @@
# Autoformatter friendly markdownlint config (all formatting rules disabled)
default: true
blank_lines: false
bullet: false
html: false
indentation: false
line_length: false
spaces: false
url: false
whitespace: false

View File

@ -0,0 +1,7 @@
enable=all
source-path=SCRIPTDIR
disable=SC2154
# If you're having issues with shellcheck following source, disable the errors via:
# disable=SC1090
# disable=SC1091

View File

@ -0,0 +1,10 @@
rules:
quoted-strings:
required: only-when-needed
extra-allowed: ["{|}"]
empty-values:
forbid-in-block-mappings: true
forbid-in-flow-mappings: true
key-duplicates: {}
octal-values:
forbid-implicit-octal: true

5
.trunk/configs/ruff.toml Normal file
View File

@ -0,0 +1,5 @@
# Generic, formatter-friendly config.
select = ["B", "D3", "E", "F"]
# Never enforce `E501` (line length violations). This should be handled by formatters.
ignore = ["E501"]

View File

@ -0,0 +1,14 @@
module.exports = {
plugins: [
{
name: "preset-default",
params: {
overrides: {
removeViewBox: false, // https://github.com/svg/svgo/issues/1128
sortAttrs: true,
removeOffCanvasPaths: true,
},
},
},
],
};

46
.trunk/trunk.yaml Normal file
View File

@ -0,0 +1,46 @@
# This file controls the behavior of Trunk: https://docs.trunk.io/cli
# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml
version: 0.1
cli:
version: 1.19.0
# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins)
plugins:
sources:
- id: trunk
ref: v1.4.2
uri: https://github.com/trunk-io/plugins
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
runtimes:
enabled:
- go@1.21.0
- node@18.12.1
- python@3.10.8
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
lint:
enabled:
- actionlint@1.6.26
- bandit@1.7.7
- black@24.1.1
- checkov@3.2.1
- git-diff-check
- hadolint@2.12.0
- isort@5.13.2
- markdownlint@0.39.0
- osv-scanner@1.6.1
- prettier@3.2.4
- ruff@0.1.15
- shellcheck@0.9.0
- shfmt@3.6.0
- svgo@3.2.0
- taplo@0.8.1
- terrascan@1.18.11
- trivy@0.48.3
- trufflehog@3.66.1
- yamllint@1.33.0
actions:
disabled:
- trunk-announce
- trunk-check-pre-push
- trunk-fmt-pre-commit
enabled:
- trunk-upgrade-available

View File

@ -6,10 +6,12 @@ COPY ./dist/swingmusic /swingmusic
RUN chmod +x /swingmusic
RUN apt update && apt install -y tzdata
EXPOSE 1970/tcp
VOLUME /music
VOLUME /config
ENTRYPOINT ["/swingmusic", "--host", "0.0.0.0", "--config", "/config"]
ENTRYPOINT ["/swingmusic", "--host", "0.0.0.0", "--config", "/config"]

View File

@ -54,7 +54,8 @@ def rebuild_store(db_dirs: list[str]):
try:
populate.Populate(instance_key=instance_key)
except populate.PopulateCancelledError:
except populate.PopulateCancelledError as e:
print(e)
reload_everything(instance_key)
return
@ -63,7 +64,7 @@ def rebuild_store(db_dirs: list[str]):
log.info("Rebuilding library... ✅")
# I freaking don't know what this function does anymore
# I freaking don't know what this function does anymore
def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]):
"""
Params:

View File

@ -56,8 +56,8 @@ class HandleArgs:
value = settings.Keys.get(key)
if not value:
log.error(f"ERROR: {key} not set in environment")
sys.exit(0)
log.error(f"WARNING: {key} not set in environment")
#sys.exit(0)
lines.append(f'{key} = "{value}"\n')

View File

@ -104,8 +104,9 @@ class Populate:
log.error(
"Internet connection lost. Downloading artist images stopped."
)
log.error(e) # REVIEW More informations = good
else:
log.warning(f"No internet connection. Downloading artist images stopped!")
log.warning("No internet connection. Downloading artist images stopped!")
# Re-process the new artist images.
if tried_to_download_new_images:
@ -113,6 +114,7 @@ class Populate:
if has_connection():
try:
print("Attempting to download similar artists...")
FetchSimilarArtistsLastFM(instance_key)
except PopulateCancelledError as e:
log.warn(e)
@ -135,6 +137,7 @@ class Populate:
unmodified_paths.add(track.filepath)
continue
except (FileNotFoundError, OSError) as e:
log.warning(e) # REVIEW More informations = good
TrackStore.remove_track_obj(track)
remove_tracks_by_filepaths(track.filepath)
@ -286,6 +289,7 @@ def save_similar_artists(_map: tuple[str, Artist]):
instance_key, artist = _map
if POPULATE_KEY != instance_key:
print("Warning: Populate key changed")
raise PopulateCancelledError(
"'FetchSimilarArtistsLastFM': Populate key changed"
)
@ -321,6 +325,7 @@ class FetchSimilarArtistsLastFM:
with ThreadPoolExecutor(max_workers=CPU_COUNT) as executor:
try:
print("Processing similar artists")
results = list(
tqdm(
executor.map(save_similar_artists, key_artist_map),

View File

@ -6,7 +6,7 @@ import urllib.parse
import requests
from requests import ConnectionError, HTTPError, ReadTimeout
from app import settings
#from app import settings
from app.utils.hashing import create_hash
@ -14,8 +14,11 @@ def fetch_similar_artists(name: str):
"""
Fetches similar artists from Last.fm
"""
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"
# 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()
@ -25,7 +28,8 @@ def fetch_similar_artists(name: str):
data = response.json()
try:
artists = data["similarartists"]["artist"]
#artists = data["similarartists"]["artist"]
artists = data["results"]["artist"]
except KeyError:
return []

View File

@ -242,6 +242,9 @@ 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.LASTFM_API_KEY = configs.LASTFM_API_KEY
cls.PLUGIN_LYRICS_AUTHORITY = configs.PLUGIN_LYRICS_AUTHORITY
@ -253,8 +256,9 @@ class Keys:
@classmethod
def verify_keys(cls):
if not cls.LASTFM_API_KEY:
print("ERROR: LASTFM_API_KEY not set in environment")
sys.exit(0)
# REVIEW Ideally, this shouldn't be fatal
print("WARNING: LASTFM_API_KEY not set in environment. Experimental API calls will be implemented")
#sys.exit(0)
@classmethod
def get(cls, key: str):

View File

@ -5,7 +5,16 @@ from unidecode import unidecode
def create_hash(*args: str, decode=False, limit=10) -> str:
"""
Creates a simple hash for an album
This function creates a case-insensitive, non-alphanumeric chars ignoring hash from the given arguments.
Example use case:
- Creating computable IDs for duplicate artists. eg. Juice WRLD and Juice Wrld should have the same ID.
:param args: The arguments to hash.
:param decode: Whether to decode the arguments before hashing.
:param limit: The number of characters to return.
:return: The hash.
"""
def remove_non_alnum(token: str) -> str:
@ -23,5 +32,11 @@ def create_hash(*args: str, decode=False, limit=10) -> str:
str_ = unidecode(str_)
str_ = str_.encode("utf-8")
str_ = hashlib.sha256(str_).hexdigest()
return str_[-limit:]
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:]

View File

@ -1,12 +1,14 @@
import socket as Socket
def has_connection(host="8.8.8.8", port=53, timeout=3):
def has_connection(host="google.it", port=80, timeout=3):
"""
# REVIEW Was:
Host: 8.8.8.8 (google-public-dns-a.google.com)
OpenPort: 53/tcp
Service: domain (DNS/TCP)
"""
try:
Socket.setdefaulttimeout(timeout)
Socket.socket(Socket.AF_INET, Socket.SOCK_STREAM).connect((host, port))

View File

@ -1,9 +1,19 @@
#!/bin/zsh
#!/bin/bash
# REVIEW Above: bash is way more compatible than other shells
# builds the latest version of the client and server
cd ../swingmusic-client
yarn build --outDir ../swingmusic/client
# REVIEW These are not useful if you dont have the source code
#cd ../swingmusic-client
#yarn build --outDir ../swingmusic/client
#../swingmusic
cd ../swingmusic
# REVIEW Cleaning up
rm -rf build dist
# REVIEW Install poetry & requirements
poetry || pip install poetry
poetry install --no-root
# Build the app
poetry run python manage.py --build

16
dev_build.sh Normal file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# REVIEW Above: bash is way more compatible than other shells
# builds the latest version of the client and server
# 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
# REVIEW Optional cleaning up
# rm -rf build dist
# REVIEW Install poetry & requirements
# Build the app
poetry run python manage.py --build

3
run Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
poetry run python manage.py