mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-06 19:25:34 +00:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f66bca67ac | ||
![]() |
f0a8aa02a9 | ||
![]() |
077661b73c | ||
![]() |
5a918d078e | ||
![]() |
bc51150e47 | ||
![]() |
c153247918 | ||
![]() |
632447bdba | ||
![]() |
e0581259a6 | ||
![]() |
d411f04e98 | ||
![]() |
27a277cc5b | ||
![]() |
9bf005668b | ||
![]() |
cb47a4bc6e | ||
![]() |
8c44aeff3d | ||
![]() |
cb1842cc2b | ||
![]() |
77f22c2b84 |
1
.github/workflows/release.yml
vendored
1
.github/workflows/release.yml
vendored
@ -118,6 +118,7 @@ jobs:
|
||||
- name: Upload binaries to GitHub Release
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
allowUpdates: true
|
||||
artifacts: "./linux/swingmusic, ./win32/swingmusic.exe"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ format('v{0}',inputs.tag) }}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ logs.txt
|
||||
TODO.md
|
||||
testdata.py
|
||||
test.py
|
||||
nohup.out
|
||||
|
4
.trunk/configs/.hadolint.yaml
Normal file
4
.trunk/configs/.hadolint.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
# Following source doesn't work in most setups
|
||||
ignored:
|
||||
- SC1090
|
||||
- SC1091
|
2
.trunk/configs/.isort.cfg
Normal file
2
.trunk/configs/.isort.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
[settings]
|
||||
profile=black
|
10
.trunk/configs/.markdownlint.yaml
Normal file
10
.trunk/configs/.markdownlint.yaml
Normal 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
|
7
.trunk/configs/.shellcheckrc
Normal file
7
.trunk/configs/.shellcheckrc
Normal 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
|
10
.trunk/configs/.yamllint.yaml
Normal file
10
.trunk/configs/.yamllint.yaml
Normal 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
5
.trunk/configs/ruff.toml
Normal 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"]
|
14
.trunk/configs/svgo.config.js
Normal file
14
.trunk/configs/svgo.config.js
Normal 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
46
.trunk/trunk.yaml
Normal 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
|
@ -6,6 +6,8 @@ COPY ./dist/swingmusic /swingmusic
|
||||
|
||||
RUN chmod +x /swingmusic
|
||||
|
||||
RUN apt update && apt install -y tzdata
|
||||
|
||||
EXPOSE 1970/tcp
|
||||
|
||||
VOLUME /music
|
||||
|
@ -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
|
||||
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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),
|
||||
|
@ -207,7 +207,7 @@ class Album:
|
||||
dates = (int(t.date) for t in tracks if t.date)
|
||||
try:
|
||||
self.date = datetime.datetime.fromtimestamp(min(dates)).year
|
||||
except ValueError:
|
||||
except:
|
||||
self.date = datetime.datetime.now().year
|
||||
|
||||
def set_count(self, count: int):
|
||||
|
@ -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 []
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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:]
|
||||
|
@ -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))
|
||||
|
18
build.sh
18
build.sh
@ -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
16
dev_build.sh
Normal 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
|
Loading…
x
Reference in New Issue
Block a user