mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-07 03:35:35 +00:00
A LOTTTT ...
+ fix help text + run populate once when -nps flag is used + update app version + sort tracks by track and disc no. when saving to playlist + serialize search results + update tags.artist -> tags.artists + update tags.albumartist -> tags.albumartists + remove artist images from serialized albums + add function to serialize artists for cards + misc
This commit is contained in:
parent
5cf188dd38
commit
0a703dcc0f
@ -4,15 +4,17 @@ Contains all the album routes.
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as adb
|
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as adb
|
||||||
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
||||||
from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as lastfmdb
|
from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as lastfmdb
|
||||||
|
from app.lib.albumslib import sort_by_track_no
|
||||||
from app.models import FavType, Track
|
from app.models import FavType, Track
|
||||||
from app.serializers.album import serialize_for_card
|
from app.serializers.album import serialize_for_card
|
||||||
from app.serializers.track import track_serializer
|
from app.serializers.track import serialize_track
|
||||||
from app.store.albums import AlbumStore
|
from app.store.albums import AlbumStore
|
||||||
from app.store.tracks import TrackStore
|
from app.store.tracks import TrackStore
|
||||||
from app.utils.hashing import create_hash
|
from app.utils.hashing import create_hash
|
||||||
@ -83,7 +85,7 @@ def get_album_tracks_and_info():
|
|||||||
album.is_favorite = check_is_fav(albumhash, FavType.album)
|
album.is_favorite = check_is_fav(albumhash, FavType.album)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"tracks": [track_serializer(t, remove_disc=False) for t in tracks],
|
"tracks": [serialize_track(t, remove_disc=False) for t in tracks],
|
||||||
"info": album,
|
"info": album,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,13 +96,7 @@ def get_album_tracks(albumhash: str):
|
|||||||
Returns all the tracks in the given album, sorted by disc and track number.
|
Returns all the tracks in the given album, sorted by disc and track number.
|
||||||
"""
|
"""
|
||||||
tracks = TrackStore.get_tracks_by_albumhash(albumhash)
|
tracks = TrackStore.get_tracks_by_albumhash(albumhash)
|
||||||
tracks = [asdict(t) for t in tracks]
|
sort_by_track_no(tracks)
|
||||||
|
|
||||||
for t in tracks:
|
|
||||||
track = str(t["track"]).zfill(3)
|
|
||||||
t["_pos"] = int(f"{t['disc']}{track}")
|
|
||||||
|
|
||||||
tracks = sorted(tracks, key=lambda t: t["_pos"])
|
|
||||||
|
|
||||||
return {"tracks": tracks}
|
return {"tracks": tracks}
|
||||||
|
|
||||||
@ -210,5 +206,3 @@ def get_similar_albums():
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return {"albums": [serialize_for_card(a) for a in albums[:limit]]}
|
return {"albums": [serialize_for_card(a) for a in albums[:limit]]}
|
||||||
|
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ from showinfm import show_in_file_manager
|
|||||||
from app import settings
|
from app import settings
|
||||||
from app.db.sqlite.settings import SettingsSQLMethods as db
|
from app.db.sqlite.settings import SettingsSQLMethods as db
|
||||||
from app.lib.folderslib import GetFilesAndDirs, get_folders
|
from app.lib.folderslib import GetFilesAndDirs, get_folders
|
||||||
from app.serializers.track import track_serializer
|
from app.serializers.track import serialize_track
|
||||||
from app.store.tracks import TrackStore as store
|
from app.store.tracks import TrackStore as store
|
||||||
from app.utils.wintools import is_windows, win_replace_slash
|
from app.utils.wintools import is_windows, win_replace_slash
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ def get_tracks_in_path():
|
|||||||
|
|
||||||
tracks = store.get_tracks_in_path(path)
|
tracks = store.get_tracks_in_path(path)
|
||||||
tracks = sorted(tracks, key=lambda i: i.last_mod)
|
tracks = sorted(tracks, key=lambda i: i.last_mod)
|
||||||
tracks = (track_serializer(t) for t in tracks if Path(t.filepath).exists())
|
tracks = (serialize_track(t) for t in tracks if Path(t.filepath).exists())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"tracks": list(tracks)[:300],
|
"tracks": list(tracks)[:300],
|
||||||
|
@ -11,6 +11,7 @@ from PIL import UnidentifiedImageError, Image
|
|||||||
from app import models
|
from app import models
|
||||||
from app.db.sqlite.playlists import SQLitePlaylistMethods
|
from app.db.sqlite.playlists import SQLitePlaylistMethods
|
||||||
from app.lib import playlistlib
|
from app.lib import playlistlib
|
||||||
|
from app.lib.albumslib import sort_by_track_no
|
||||||
from app.models.track import Track
|
from app.models.track import Track
|
||||||
from app.store.albums import AlbumStore
|
from app.store.albums import AlbumStore
|
||||||
from app.store.tracks import TrackStore
|
from app.store.tracks import TrackStore
|
||||||
@ -69,7 +70,6 @@ def send_all_playlists():
|
|||||||
"""
|
"""
|
||||||
Gets all the playlists.
|
Gets all the playlists.
|
||||||
"""
|
"""
|
||||||
# get the no_images query param
|
|
||||||
no_images = request.args.get("no_images", False)
|
no_images = request.args.get("no_images", False)
|
||||||
|
|
||||||
playlists = PL.get_all_playlists()
|
playlists = PL.get_all_playlists()
|
||||||
@ -141,7 +141,9 @@ def get_album_trackhashes(albumhash: str):
|
|||||||
Returns a list of trackhashes in an album.
|
Returns a list of trackhashes in an album.
|
||||||
"""
|
"""
|
||||||
tracks = TrackStore.get_tracks_by_albumhash(albumhash)
|
tracks = TrackStore.get_tracks_by_albumhash(albumhash)
|
||||||
return [t.trackhash for t in tracks]
|
tracks = sort_by_track_no(tracks)
|
||||||
|
|
||||||
|
return [t["trackhash"] for t in tracks]
|
||||||
|
|
||||||
|
|
||||||
def get_artist_trackhashes(artisthash: str):
|
def get_artist_trackhashes(artisthash: str):
|
||||||
@ -410,7 +412,6 @@ def save_item_as_playlist():
|
|||||||
trackhashes = get_album_trackhashes(itemhash)
|
trackhashes = get_album_trackhashes(itemhash)
|
||||||
elif itemtype == "artist":
|
elif itemtype == "artist":
|
||||||
trackhashes = get_artist_trackhashes(itemhash)
|
trackhashes = get_artist_trackhashes(itemhash)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
trackhashes = []
|
trackhashes = []
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class HandleArgs:
|
|||||||
"""
|
"""
|
||||||
Runs Pyinstaller.
|
Runs Pyinstaller.
|
||||||
"""
|
"""
|
||||||
if ALLARGS.build.value in ARGS:
|
if ALLARGS.build in ARGS:
|
||||||
with open("pyinstaller.config.ini", "w", encoding="utf-8") as file:
|
with open("pyinstaller.config.ini", "w", encoding="utf-8") as file:
|
||||||
config["DEFAULT"]["BUILD"] = "True"
|
config["DEFAULT"]["BUILD"] = "True"
|
||||||
config.write(file)
|
config.write(file)
|
||||||
@ -73,8 +73,8 @@ class HandleArgs:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_port():
|
def handle_port():
|
||||||
if ALLARGS.port.value in ARGS:
|
if ALLARGS.port in ARGS:
|
||||||
index = ARGS.index(ALLARGS.port.value)
|
index = ARGS.index(ALLARGS.port)
|
||||||
try:
|
try:
|
||||||
port = ARGS[index + 1]
|
port = ARGS[index + 1]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
@ -89,8 +89,8 @@ class HandleArgs:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_host():
|
def handle_host():
|
||||||
if ALLARGS.host.value in ARGS:
|
if ALLARGS.host in ARGS:
|
||||||
index = ARGS.index(ALLARGS.host.value)
|
index = ARGS.index(ALLARGS.host)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
host = ARGS[index + 1]
|
host = ARGS[index + 1]
|
||||||
@ -105,8 +105,8 @@ class HandleArgs:
|
|||||||
"""
|
"""
|
||||||
Modifies the config path.
|
Modifies the config path.
|
||||||
"""
|
"""
|
||||||
if ALLARGS.config.value in ARGS:
|
if ALLARGS.config in ARGS:
|
||||||
index = ARGS.index(ALLARGS.config.value)
|
index = ARGS.index(ALLARGS.config)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
config_path = ARGS[index + 1]
|
config_path = ARGS[index + 1]
|
||||||
@ -125,34 +125,34 @@ class HandleArgs:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_no_feat():
|
def handle_no_feat():
|
||||||
# if ArgsEnum.no_feat in ARGS:
|
# if ArgsEnum.no_feat in ARGS:
|
||||||
if any((a in ARGS for a in ALLARGS.show_feat.value)):
|
if any((a in ARGS for a in ALLARGS.show_feat)):
|
||||||
settings.FromFlags.EXTRACT_FEAT = False
|
settings.FromFlags.EXTRACT_FEAT = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_remove_prod():
|
def handle_remove_prod():
|
||||||
if any((a in ARGS for a in ALLARGS.show_prod.value)):
|
if any((a in ARGS for a in ALLARGS.show_prod)):
|
||||||
settings.FromFlags.REMOVE_PROD = False
|
settings.FromFlags.REMOVE_PROD = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_cleaning_albums():
|
def handle_cleaning_albums():
|
||||||
if any((a in ARGS for a in ALLARGS.dont_clean_albums.value)):
|
if any((a in ARGS for a in ALLARGS.dont_clean_albums)):
|
||||||
settings.FromFlags.CLEAN_ALBUM_TITLE = False
|
settings.FromFlags.CLEAN_ALBUM_TITLE = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_cleaning_tracks():
|
def handle_cleaning_tracks():
|
||||||
if any((a in ARGS for a in ALLARGS.dont_clean_tracks.value)):
|
if any((a in ARGS for a in ALLARGS.dont_clean_tracks)):
|
||||||
settings.FromFlags.REMOVE_REMASTER_FROM_TRACK = False
|
settings.FromFlags.REMOVE_REMASTER_FROM_TRACK = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_periodic_scan():
|
def handle_periodic_scan():
|
||||||
if any((a in ARGS for a in ALLARGS.no_periodic_scan.value)):
|
if any((a in ARGS for a in ALLARGS.no_periodic_scan)):
|
||||||
settings.FromFlags.DO_PERIODIC_SCANS = False
|
settings.FromFlags.DO_PERIODIC_SCANS = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_periodic_scan_interval():
|
def handle_periodic_scan_interval():
|
||||||
if any((a in ARGS for a in ALLARGS.periodic_scan_interval.value)):
|
if any((a in ARGS for a in ALLARGS.periodic_scan_interval)):
|
||||||
index = [
|
index = [
|
||||||
ARGS.index(a) for a in ALLARGS.periodic_scan_interval.value if a in ARGS
|
ARGS.index(a) for a in ALLARGS.periodic_scan_interval if a in ARGS
|
||||||
][0]
|
][0]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -177,12 +177,12 @@ class HandleArgs:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_help():
|
def handle_help():
|
||||||
if any((a in ARGS for a in ALLARGS.help.value)):
|
if any((a in ARGS for a in ALLARGS.help)):
|
||||||
print(HELP_MESSAGE)
|
print(HELP_MESSAGE)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def handle_version():
|
def handle_version():
|
||||||
if any((a in ARGS for a in ALLARGS.version.value)):
|
if any((a in ARGS for a in ALLARGS.version)):
|
||||||
print(settings.Release.APP_VERSION)
|
print(settings.Release.APP_VERSION)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
@ -42,7 +42,15 @@ class SQLiteTrackMethods:
|
|||||||
:date, :disc, :duration, :filepath, :folder, :genre, :last_mod, :title, :track, :trackhash)
|
:date, :disc, :duration, :filepath, :folder, :genre, :last_mod, :title, :track, :trackhash)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#
|
||||||
track = OrderedDict(sorted(track.items()))
|
track = OrderedDict(sorted(track.items()))
|
||||||
|
|
||||||
|
track["artist"] = track["artists"]
|
||||||
|
track["albumartist"] = track["albumartists"]
|
||||||
|
|
||||||
|
del track["artists"]
|
||||||
|
del track["albumartists"]
|
||||||
|
|
||||||
cur.execute(sql, track)
|
cur.execute(sql, track)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
Contains methods relating to albums.
|
Contains methods relating to albums.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from dataclasses import asdict
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from alive_progress import alive_bar
|
from alive_progress import alive_bar
|
||||||
|
|
||||||
|
from app.logger import log
|
||||||
|
from app.models.track import Track
|
||||||
from app.store.albums import AlbumStore
|
from app.store.albums import AlbumStore
|
||||||
from app.store.tracks import TrackStore
|
from app.store.tracks import TrackStore
|
||||||
from app.logger import log
|
|
||||||
|
|
||||||
|
|
||||||
def validate_albums():
|
def validate_albums():
|
||||||
@ -25,3 +29,15 @@ def validate_albums():
|
|||||||
if album.albumhash not in album_hashes:
|
if album.albumhash not in album_hashes:
|
||||||
AlbumStore.remove_album(album)
|
AlbumStore.remove_album(album)
|
||||||
bar()
|
bar()
|
||||||
|
|
||||||
|
|
||||||
|
def sort_by_track_no(tracks: list[Track]) -> list[dict[str, Any]]:
|
||||||
|
tracks = [asdict(t) for t in tracks]
|
||||||
|
|
||||||
|
for t in tracks:
|
||||||
|
track = str(t["track"]).zfill(3)
|
||||||
|
t["_pos"] = int(f"{t['disc']}{track}")
|
||||||
|
|
||||||
|
tracks = sorted(tracks, key=lambda t: t["_pos"])
|
||||||
|
|
||||||
|
return tracks
|
||||||
|
@ -144,7 +144,7 @@ def get_artists_from_tracks(tracks: list[Track]) -> set[str]:
|
|||||||
"""
|
"""
|
||||||
artists = set()
|
artists = set()
|
||||||
|
|
||||||
master_artist_list = [[x.name for x in t.artist] for t in tracks]
|
master_artist_list = [[x.name for x in t.artists] for t in tracks]
|
||||||
artists = artists.union(*master_artist_list)
|
artists = artists.union(*master_artist_list)
|
||||||
|
|
||||||
return artists
|
return artists
|
||||||
|
@ -164,11 +164,11 @@ class Populate:
|
|||||||
if not AlbumStore.album_exists(track.albumhash):
|
if not AlbumStore.album_exists(track.albumhash):
|
||||||
AlbumStore.add_album(AlbumStore.create_album(track))
|
AlbumStore.add_album(AlbumStore.create_album(track))
|
||||||
|
|
||||||
for artist in track.artist:
|
for artist in track.artists:
|
||||||
if not ArtistStore.artist_exists(artist.artisthash):
|
if not ArtistStore.artist_exists(artist.artisthash):
|
||||||
ArtistStore.add_artist(Artist(artist.name))
|
ArtistStore.add_artist(Artist(artist.name))
|
||||||
|
|
||||||
for artist in track.albumartist:
|
for artist in track.albumartists:
|
||||||
if not ArtistStore.artist_exists(artist.artisthash):
|
if not ArtistStore.artist_exists(artist.artisthash):
|
||||||
ArtistStore.add_artist(Artist(artist.name))
|
ArtistStore.add_artist(Artist(artist.name))
|
||||||
|
|
||||||
|
@ -7,14 +7,17 @@ from rapidfuzz import process, utils
|
|||||||
from unidecode import unidecode
|
from unidecode import unidecode
|
||||||
|
|
||||||
from app import models
|
from app import models
|
||||||
|
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
||||||
from app.models.enums import FavType
|
from app.models.enums import FavType
|
||||||
from app.models.track import Track
|
from app.models.track import Track
|
||||||
|
from app.serializers.album import serialize_for_card as serialize_album
|
||||||
|
from app.serializers.album import serialize_for_card_many as serialize_albums
|
||||||
|
from app.serializers.artist import serialize_for_cards
|
||||||
|
from app.serializers.track import serialize_track, serialize_tracks
|
||||||
from app.store.albums import AlbumStore
|
from app.store.albums import AlbumStore
|
||||||
from app.store.artists import ArtistStore
|
from app.store.artists import ArtistStore
|
||||||
from app.store.tracks import TrackStore
|
from app.store.tracks import TrackStore
|
||||||
|
|
||||||
from app.utils.remove_duplicates import remove_duplicates
|
from app.utils.remove_duplicates import remove_duplicates
|
||||||
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
|
||||||
|
|
||||||
# ratio = fuzz.ratio
|
# ratio = fuzz.ratio
|
||||||
# wratio = fuzz.WRatio
|
# wratio = fuzz.WRatio
|
||||||
@ -303,6 +306,8 @@ class TopResults:
|
|||||||
else:
|
else:
|
||||||
top_tracks = TopResults.get_track_items(result, query, limit=tracks_limit)
|
top_tracks = TopResults.get_track_items(result, query, limit=tracks_limit)
|
||||||
|
|
||||||
|
top_tracks = serialize_tracks(top_tracks)
|
||||||
|
|
||||||
if tracks_only:
|
if tracks_only:
|
||||||
return top_tracks
|
return top_tracks
|
||||||
|
|
||||||
@ -311,10 +316,19 @@ class TopResults:
|
|||||||
else:
|
else:
|
||||||
albums = TopResults.get_album_items(result, query, limit=albums_limit)
|
albums = TopResults.get_album_items(result, query, limit=albums_limit)
|
||||||
|
|
||||||
|
albums = serialize_albums(albums)
|
||||||
|
|
||||||
if albums_only:
|
if albums_only:
|
||||||
return albums
|
return albums
|
||||||
|
|
||||||
artists = SearchArtists(query)()[:artists_limit]
|
artists = SearchArtists(query)()[:artists_limit]
|
||||||
|
artists = serialize_for_cards(artists)
|
||||||
|
|
||||||
|
if result["type"] == "track":
|
||||||
|
result["item"] = serialize_track(result["item"])
|
||||||
|
|
||||||
|
if result["type"] == "album":
|
||||||
|
result["item"] = serialize_album(result["item"])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"top_result": result,
|
"top_result": result,
|
||||||
|
@ -7,12 +7,10 @@ from tinytag import TinyTag
|
|||||||
|
|
||||||
from app.settings import Defaults, Paths
|
from app.settings import Defaults, Paths
|
||||||
from app.utils.hashing import create_hash
|
from app.utils.hashing import create_hash
|
||||||
from app.utils.parsers import (parse_artist_from_filename,
|
from app.utils.parsers import parse_artist_from_filename, parse_title_from_filename
|
||||||
parse_title_from_filename)
|
|
||||||
from app.utils.wintools import win_replace_slash
|
from app.utils.wintools import win_replace_slash
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def parse_album_art(filepath: str):
|
def parse_album_art(filepath: str):
|
||||||
"""
|
"""
|
||||||
Returns the album art for a given audio file.
|
Returns the album art for a given audio file.
|
||||||
@ -163,6 +161,9 @@ def get_tags(filepath: str):
|
|||||||
tags.filetype = filetype
|
tags.filetype = filetype
|
||||||
tags.last_mod = last_mod
|
tags.last_mod = last_mod
|
||||||
|
|
||||||
|
tags.artists = tags.artist
|
||||||
|
tags.albumartists = tags.albumartist
|
||||||
|
|
||||||
tags = tags.__dict__
|
tags = tags.__dict__
|
||||||
|
|
||||||
# delete all tag properties that start with _ (tinytag internals)
|
# delete all tag properties that start with _ (tinytag internals)
|
||||||
@ -182,6 +183,8 @@ def get_tags(filepath: str):
|
|||||||
"track_total",
|
"track_total",
|
||||||
"year",
|
"year",
|
||||||
"bitdepth",
|
"bitdepth",
|
||||||
|
"artist",
|
||||||
|
"albumartist",
|
||||||
]
|
]
|
||||||
|
|
||||||
for tag in to_delete:
|
for tag in to_delete:
|
||||||
|
@ -176,7 +176,7 @@ def add_track(filepath: str) -> None:
|
|||||||
album.set_colors(colors)
|
album.set_colors(colors)
|
||||||
AlbumStore.add_album(album)
|
AlbumStore.add_album(album)
|
||||||
|
|
||||||
artists: list[Artist] = track.artist + track.albumartist # type: ignore
|
artists: list[Artist] = track.artists + track.albumartists # type: ignore
|
||||||
|
|
||||||
for artist in artists:
|
for artist in artists:
|
||||||
if not ArtistStore.artist_exists(artist.artisthash):
|
if not ArtistStore.artist_exists(artist.artisthash):
|
||||||
@ -202,7 +202,7 @@ def remove_track(filepath: str) -> None:
|
|||||||
if empty_album:
|
if empty_album:
|
||||||
AlbumStore.remove_album_by_hash(track.albumhash)
|
AlbumStore.remove_album_by_hash(track.albumhash)
|
||||||
|
|
||||||
artists: list[Artist] = track.artist + track.albumartist # type: ignore
|
artists: list[Artist] = track.artists + track.albumartists # type: ignore
|
||||||
|
|
||||||
for artist in artists:
|
for artist in artists:
|
||||||
empty_artist = not ArtistStore.artist_has_tracks(artist.artisthash)
|
empty_artist = not ArtistStore.artist_has_tracks(artist.artisthash)
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from app.settings import get_flag, ParserFlags
|
from app.settings import ParserFlags, get_flag
|
||||||
from app.utils.hashing import create_hash
|
from app.utils.hashing import create_hash
|
||||||
from app.utils.parsers import (
|
from app.utils.parsers import (clean_title, get_base_title_and_versions,
|
||||||
split_artists,
|
parse_feat_from_title, remove_prod,
|
||||||
remove_prod,
|
split_artists)
|
||||||
parse_feat_from_title,
|
|
||||||
clean_title,
|
|
||||||
get_base_title_and_versions,
|
|
||||||
)
|
|
||||||
from .artist import ArtistMinimal
|
from .artist import ArtistMinimal
|
||||||
|
|
||||||
|
|
||||||
@ -19,9 +16,9 @@ class Track:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
album: str
|
album: str
|
||||||
albumartist: str | list[ArtistMinimal]
|
albumartists: str | list[ArtistMinimal]
|
||||||
albumhash: str
|
albumhash: str
|
||||||
artist: str | list[ArtistMinimal]
|
artists: str | list[ArtistMinimal]
|
||||||
bitrate: int
|
bitrate: int
|
||||||
copyright: str
|
copyright: str
|
||||||
date: int
|
date: int
|
||||||
@ -48,8 +45,8 @@ class Track:
|
|||||||
self.og_album = self.album
|
self.og_album = self.album
|
||||||
self.last_mod = int(self.last_mod)
|
self.last_mod = int(self.last_mod)
|
||||||
|
|
||||||
if self.artist is not None:
|
if self.artists is not None:
|
||||||
artists = split_artists(self.artist)
|
artists = split_artists(self.artists)
|
||||||
new_title = self.title
|
new_title = self.title
|
||||||
|
|
||||||
if get_flag(ParserFlags.EXTRACT_FEAT):
|
if get_flag(ParserFlags.EXTRACT_FEAT):
|
||||||
@ -78,12 +75,11 @@ class Track:
|
|||||||
self.recreate_albumhash()
|
self.recreate_albumhash()
|
||||||
|
|
||||||
self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists)
|
self.artist_hashes = "-".join(create_hash(a, decode=True) for a in artists)
|
||||||
self.artist = [ArtistMinimal(a) for a in artists]
|
self.artists = [ArtistMinimal(a) for a in artists]
|
||||||
|
|
||||||
albumartists = split_artists(self.albumartist)
|
albumartists = split_artists(self.albumartists)
|
||||||
self.albumartist = [ArtistMinimal(a) for a in albumartists]
|
self.albumartists = [ArtistMinimal(a) for a in albumartists]
|
||||||
|
|
||||||
self.filetype = self.filepath.rsplit(".", maxsplit=1)[-1]
|
|
||||||
self.image = self.albumhash + ".webp"
|
self.image = self.albumhash + ".webp"
|
||||||
|
|
||||||
if self.genre is not None and self.genre != "":
|
if self.genre is not None and self.genre != "":
|
||||||
@ -102,20 +98,20 @@ class Track:
|
|||||||
return
|
return
|
||||||
|
|
||||||
self.trackhash = create_hash(
|
self.trackhash = create_hash(
|
||||||
", ".join([a.name for a in self.artist]), self.og_album, self.title
|
", ".join([a.name for a in self.artists]), self.og_album, self.title
|
||||||
)
|
)
|
||||||
|
|
||||||
def recreate_artists_hash(self):
|
def recreate_artists_hash(self):
|
||||||
"""
|
"""
|
||||||
Recreates a track's artist hashes if the artist list was altered
|
Recreates a track's artist hashes if the artist list was altered
|
||||||
"""
|
"""
|
||||||
self.artist_hashes = "-".join(a.artisthash for a in self.artist)
|
self.artist_hashes = "-".join(a.artisthash for a in self.artists)
|
||||||
|
|
||||||
def recreate_albumhash(self):
|
def recreate_albumhash(self):
|
||||||
"""
|
"""
|
||||||
Recreates an albumhash of a track to merge all versions of an album.
|
Recreates an albumhash of a track to merge all versions of an album.
|
||||||
"""
|
"""
|
||||||
self.albumhash = create_hash(self.album, self.albumartist)
|
self.albumhash = create_hash(self.album, self.albumartists)
|
||||||
|
|
||||||
def rename_album(self, new_album: str):
|
def rename_album(self, new_album: str):
|
||||||
"""
|
"""
|
||||||
@ -126,7 +122,7 @@ class Track:
|
|||||||
def add_artists(self, artists: list[str], new_album_title: str):
|
def add_artists(self, artists: list[str], new_album_title: str):
|
||||||
for artist in artists:
|
for artist in artists:
|
||||||
if create_hash(artist) not in self.artist_hashes:
|
if create_hash(artist) not in self.artist_hashes:
|
||||||
self.artist.append(ArtistMinimal(artist))
|
self.artists.append(ArtistMinimal(artist))
|
||||||
|
|
||||||
self.recreate_artists_hash()
|
self.recreate_artists_hash()
|
||||||
self.rename_album(new_album_title)
|
self.rename_album(new_album_title)
|
||||||
|
@ -21,6 +21,11 @@ def run_periodic_scans():
|
|||||||
# ValidateAlbumThumbs()
|
# ValidateAlbumThumbs()
|
||||||
# ValidatePlaylistThumbs()
|
# ValidatePlaylistThumbs()
|
||||||
|
|
||||||
|
try:
|
||||||
|
Populate(key=get_random_str())
|
||||||
|
except PopulateCancelledError:
|
||||||
|
pass
|
||||||
|
|
||||||
while get_flag(ParserFlags.DO_PERIODIC_SCANS):
|
while get_flag(ParserFlags.DO_PERIODIC_SCANS):
|
||||||
try:
|
try:
|
||||||
Populate(key=get_random_str())
|
Populate(key=get_random_str())
|
||||||
|
@ -8,20 +8,20 @@ Usage: swingmusic [options]
|
|||||||
Swing Music is a beautiful, self-hosted music player for your local audio files. Like a cooler Spotify ... but bring your own music.
|
Swing Music is a beautiful, self-hosted music player for your local audio files. Like a cooler Spotify ... but bring your own music.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
{', '.join(args.help.value)}: Show this help message
|
{', '.join(args.help)}: Show this help message
|
||||||
{', '.join(args.version.value)}: Show the app version
|
{', '.join(args.version)}: Show the app version
|
||||||
|
|
||||||
{args.host}: Set the host
|
{args.host}: Set the host
|
||||||
{args.port}: Set the port
|
{args.port}: Set the port
|
||||||
{args.config}: Set the config path
|
{args.config}: Set the config path
|
||||||
|
|
||||||
{', '.join(args.show_feat.value)}: Do not extract featured artists from the song title
|
{', '.join(args.show_feat)}: Do not extract featured artists from the song title
|
||||||
{', '.join(args.show_prod.value)}: Do not hide producers in the song title
|
{', '.join(args.show_prod)}: Do not hide producers in the song title
|
||||||
{', '.join(args.dont_clean_albums.value)}: Don't clean album titles. Cleaning is done by removing information in
|
{', '.join(args.dont_clean_albums)}: Don't clean album titles. Cleaning is done by removing information in
|
||||||
parentheses and showing it separately
|
parentheses and showing it separately
|
||||||
{', '.join(args.dont_clean_tracks.value)}: Don't remove remaster information from track titles
|
{', '.join(args.dont_clean_tracks)}: Don't remove remaster information from track titles
|
||||||
{', '.join(args.no_periodic_scan.value)}: Disable periodic scan
|
{', '.join(args.no_periodic_scan)}: Disable periodic scan
|
||||||
{', '.join(args.periodic_scan_interval.value)}: Set the periodic scan interval in seconds. Default is 300 seconds (5
|
{', '.join(args.periodic_scan_interval)}: Set the periodic scan interval in seconds. Default is 300 seconds (5
|
||||||
minutes)
|
minutes)
|
||||||
|
|
||||||
{args.build}: Build the application (in development)
|
{args.build}: Build the application (in development)
|
||||||
|
@ -9,6 +9,10 @@ def album_serializer(album: Album, to_remove: set[str]) -> dict:
|
|||||||
for key in to_remove:
|
for key in to_remove:
|
||||||
album_dict.pop(key, None)
|
album_dict.pop(key, None)
|
||||||
|
|
||||||
|
# remove artist images
|
||||||
|
for artist in album_dict["albumartists"]:
|
||||||
|
artist.pop("image", None)
|
||||||
|
|
||||||
return album_dict
|
return album_dict
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +20,7 @@ def serialize_for_card(album: Album):
|
|||||||
props_to_remove = {
|
props_to_remove = {
|
||||||
"duration",
|
"duration",
|
||||||
"count",
|
"count",
|
||||||
"albumartist_hashes",
|
"albumartists_hashes",
|
||||||
"og_title",
|
"og_title",
|
||||||
"base_title",
|
"base_title",
|
||||||
"genres",
|
"genres",
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
# from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
|
|
||||||
|
from app.models.artist import Artist
|
||||||
|
|
||||||
|
|
||||||
# def album_serializer(album: Artist, to_remove: set[str]) -> ArtistMinimal:
|
def serialize_for_card(artist: Artist):
|
||||||
# album_dict = asdict(album)
|
artist_dict = asdict(artist)
|
||||||
|
|
||||||
# to_remove.update(key for key in album_dict.keys() if key.startswith("is_"))
|
props_to_remove = {
|
||||||
# for key in to_remove:
|
"is_favorite",
|
||||||
# album_dict.pop(key, None)
|
"trackcount",
|
||||||
|
"duration",
|
||||||
|
"albumcount",
|
||||||
|
}
|
||||||
|
|
||||||
# return album_dict
|
for key in props_to_remove:
|
||||||
|
artist_dict.pop(key, None)
|
||||||
|
|
||||||
|
return artist_dict
|
||||||
|
|
||||||
|
|
||||||
# Traceback (most recent call last):
|
def serialize_for_cards(artists: list[Artist]):
|
||||||
# File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
|
return [serialize_for_card(a) for a in artists]
|
||||||
# self.run()
|
|
||||||
# File "/usr/lib/python3.10/threading.py", line 953, in run
|
|
||||||
# self._target(*self._args, **self._kwargs)
|
|
||||||
# File "/usr/lib/python3.10/multiprocessing/pool.py", line 579, in _handle_results
|
|
||||||
# task = get()
|
|
||||||
# File "/usr/lib/python3.10/multiprocessing/connection.py", line 251, in recv
|
|
||||||
# return _ForkingPickler.loads(buf.getbuffer())
|
|
||||||
# File "/home/cwilvx/.cache/pypoetry/virtualenvs/swing_music_player-xIXBgWdk-py3.10/lib/python3.10/site-packages/requests/exceptions.py", line 41, in __init__
|
|
||||||
# CompatJSONDecodeError.__init__(self, *args)
|
|
||||||
# TypeError: JSONDecodeError.__init__() missing 2 required positional arguments: 'doc' and 'pos'
|
|
||||||
|
@ -3,7 +3,7 @@ from dataclasses import asdict
|
|||||||
from app.models.track import Track
|
from app.models.track import Track
|
||||||
|
|
||||||
|
|
||||||
def track_serializer(track: Track, to_remove: set = {}, remove_disc=True) -> dict:
|
def serialize_track(track: Track, to_remove: set = {}, remove_disc=True) -> dict:
|
||||||
album_dict = asdict(track)
|
album_dict = asdict(track)
|
||||||
props = {
|
props = {
|
||||||
"date",
|
"date",
|
||||||
@ -14,6 +14,7 @@ def track_serializer(track: Track, to_remove: set = {}, remove_disc=True) -> dic
|
|||||||
"copyright",
|
"copyright",
|
||||||
"disc",
|
"disc",
|
||||||
"track",
|
"track",
|
||||||
|
"artist_hashes",
|
||||||
}.union(to_remove)
|
}.union(to_remove)
|
||||||
|
|
||||||
if not remove_disc:
|
if not remove_disc:
|
||||||
@ -26,10 +27,15 @@ def track_serializer(track: Track, to_remove: set = {}, remove_disc=True) -> dic
|
|||||||
for key in props:
|
for key in props:
|
||||||
album_dict.pop(key, None)
|
album_dict.pop(key, None)
|
||||||
|
|
||||||
|
to_remove_images = ["artists", "albumartists"]
|
||||||
|
for key in to_remove_images:
|
||||||
|
for artist in album_dict[key]:
|
||||||
|
artist.pop("image", None)
|
||||||
|
|
||||||
return album_dict
|
return album_dict
|
||||||
|
|
||||||
|
|
||||||
def serialize_tracks(
|
def serialize_tracks(
|
||||||
tracks: list[Track], _remove: set = {}, remove_disc=True
|
tracks: list[Track], _remove: set = {}, remove_disc=True
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
return [track_serializer(t, _remove, remove_disc) for t in tracks]
|
return [serialize_track(t, _remove, remove_disc) for t in tracks]
|
||||||
|
@ -2,13 +2,12 @@
|
|||||||
Contains default configs
|
Contains default configs
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from enum import Enum
|
|
||||||
|
|
||||||
join = os.path.join
|
join = os.path.join
|
||||||
|
|
||||||
|
|
||||||
class Release:
|
class Release:
|
||||||
APP_VERSION = "1.3.0.beta"
|
APP_VERSION = "1.3.0"
|
||||||
|
|
||||||
|
|
||||||
class Paths:
|
class Paths:
|
||||||
@ -129,7 +128,7 @@ class FLASKVARS:
|
|||||||
cls.FLASK_HOST = host
|
cls.FLASK_HOST = host
|
||||||
|
|
||||||
|
|
||||||
class ALLARGS(Enum):
|
class ALLARGS:
|
||||||
"""
|
"""
|
||||||
Enumerates the possible app arguments.
|
Enumerates the possible app arguments.
|
||||||
"""
|
"""
|
||||||
@ -171,7 +170,7 @@ class FromFlags:
|
|||||||
MERGE_ALBUM_VERSIONS = False
|
MERGE_ALBUM_VERSIONS = False
|
||||||
|
|
||||||
|
|
||||||
class ParserFlags(Enum):
|
class ParserFlags():
|
||||||
EXTRACT_FEAT = "EXTRACT_FEAT"
|
EXTRACT_FEAT = "EXTRACT_FEAT"
|
||||||
REMOVE_PROD = "REMOVE_PROD"
|
REMOVE_PROD = "REMOVE_PROD"
|
||||||
CLEAN_ALBUM_TITLE = "CLEAN_ALBUM_TITLE"
|
CLEAN_ALBUM_TITLE = "CLEAN_ALBUM_TITLE"
|
||||||
@ -183,7 +182,7 @@ class ParserFlags(Enum):
|
|||||||
|
|
||||||
|
|
||||||
def get_flag(flag: ParserFlags) -> bool:
|
def get_flag(flag: ParserFlags) -> bool:
|
||||||
return getattr(FromFlags, flag.value)
|
return getattr(FromFlags, flag)
|
||||||
|
|
||||||
|
|
||||||
def get_scan_sleep_time() -> int:
|
def get_scan_sleep_time() -> int:
|
||||||
|
@ -3,10 +3,11 @@ import random
|
|||||||
|
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from app.models import Album, Track
|
|
||||||
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as aldb
|
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as aldb
|
||||||
from .tracks import TrackStore
|
from app.models import Album, Track
|
||||||
|
|
||||||
from ..utils.hashing import create_hash
|
from ..utils.hashing import create_hash
|
||||||
|
from .tracks import TrackStore
|
||||||
|
|
||||||
|
|
||||||
class AlbumStore:
|
class AlbumStore:
|
||||||
@ -19,7 +20,7 @@ class AlbumStore:
|
|||||||
"""
|
"""
|
||||||
return Album(
|
return Album(
|
||||||
albumhash=track.albumhash,
|
albumhash=track.albumhash,
|
||||||
albumartists=track.albumartist, # type: ignore
|
albumartists=track.albumartists, # type: ignore
|
||||||
title=track.og_album,
|
title=track.og_album,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -6,8 +6,9 @@ from app.db.sqlite.artistcolors import SQLiteArtistMethods as ardb
|
|||||||
from app.lib.artistlib import get_all_artists
|
from app.lib.artistlib import get_all_artists
|
||||||
from app.models import Artist
|
from app.models import Artist
|
||||||
from app.utils.bisection import UseBisection
|
from app.utils.bisection import UseBisection
|
||||||
from .tracks import TrackStore
|
|
||||||
from .albums import AlbumStore
|
from .albums import AlbumStore
|
||||||
|
from .tracks import TrackStore
|
||||||
|
|
||||||
|
|
||||||
class ArtistStore:
|
class ArtistStore:
|
||||||
@ -92,7 +93,7 @@ class ArtistStore:
|
|||||||
|
|
||||||
for track in TrackStore.tracks:
|
for track in TrackStore.tracks:
|
||||||
artists.update(track.artist_hashes)
|
artists.update(track.artist_hashes)
|
||||||
album_artists: list[str] = [a.artisthash for a in track.albumartist]
|
album_artists: list[str] = [a.artisthash for a in track.albumartists]
|
||||||
artists.update(album_artists)
|
artists.update(album_artists)
|
||||||
|
|
||||||
master_hash = "-".join(artists)
|
master_hash = "-".join(artists)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user