mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-04 12:33:03 +00:00
refactor most things to use the database directly
This commit is contained in:
@@ -3,25 +3,10 @@ This module contains all the Flask Blueprints and API routes. It also contains a
|
||||
that are used through-out the app. It handles the initialization of the watchdog,
|
||||
checking and creating config dirs and starting the re-indexing process using a background thread.
|
||||
"""
|
||||
from typing import List
|
||||
from typing import Set
|
||||
|
||||
from app import functions
|
||||
from app import helpers
|
||||
from app import instances
|
||||
from app import models
|
||||
from app import prep
|
||||
from app.lib import albumslib
|
||||
from app.lib import folderslib
|
||||
from app.lib import playlistlib
|
||||
|
||||
DB_TRACKS = instances.tracks_instance.get_all_tracks()
|
||||
VALID_FOLDERS: Set[str] = set()
|
||||
|
||||
ALBUMS: List[models.Album] = []
|
||||
TRACKS: List[models.Track] = []
|
||||
PLAYLISTS: List[models.Playlist] = []
|
||||
FOLDERS: List[models.Folder] = List
|
||||
|
||||
|
||||
@helpers.background
|
||||
@@ -31,9 +16,6 @@ def initialize() -> None:
|
||||
"""
|
||||
functions.start_watchdog()
|
||||
prep.create_config_dir()
|
||||
albumslib.create_everything()
|
||||
folderslib.run_scandir()
|
||||
playlistlib.create_all_playlists()
|
||||
functions.reindex_tracks()
|
||||
|
||||
|
||||
|
||||
+20
-22
@@ -1,16 +1,17 @@
|
||||
"""
|
||||
Contains all the album routes.
|
||||
"""
|
||||
from pprint import pprint
|
||||
from typing import List
|
||||
|
||||
from app import api
|
||||
from app import helpers
|
||||
from app import models
|
||||
from app.lib import albumslib
|
||||
from app.lib import trackslib
|
||||
from flask import Blueprint
|
||||
from flask import request
|
||||
from app.functions import FetchAlbumBio
|
||||
from app import instances
|
||||
|
||||
album_bp = Blueprint("album", __name__, url_prefix="")
|
||||
|
||||
@@ -35,31 +36,31 @@ def get_albums():
|
||||
return {"albums": albums}
|
||||
|
||||
|
||||
@album_bp.route("/album/tracks", methods=["POST"])
|
||||
@album_bp.route("/album", methods=["POST"])
|
||||
def get_album():
|
||||
"""Returns all the tracks in the given album."""
|
||||
data = request.get_json()
|
||||
|
||||
album = data["album"]
|
||||
artist = data["artist"]
|
||||
|
||||
songs = trackslib.get_album_tracks(album, artist)
|
||||
album, artist = data["album"], data["artist"]
|
||||
albumhash = helpers.create_album_hash(album, artist)
|
||||
index = albumslib.find_album(api.ALBUMS, albumhash)
|
||||
album: models.Album = api.ALBUMS[index]
|
||||
|
||||
album.count = len(songs)
|
||||
album.duration = albumslib.get_album_duration(songs)
|
||||
tracks = instances.tracks_instance.find_tracks_by_hash(albumhash)
|
||||
tracks = [models.Track(t) for t in tracks]
|
||||
|
||||
album = instances.album_instance.find_album_by_hash(albumhash)
|
||||
album = models.Album(album)
|
||||
|
||||
album.count = len(tracks)
|
||||
album.duration = albumslib.get_album_duration(tracks)
|
||||
|
||||
if (
|
||||
album.count == 1
|
||||
and songs[0].title == album.title
|
||||
and songs[0].tracknumber == 1
|
||||
and songs[0].disknumber == 1
|
||||
and tracks[0].title == album.title
|
||||
and tracks[0].tracknumber == 1
|
||||
and tracks[0].disknumber == 1
|
||||
):
|
||||
album.is_single = True
|
||||
|
||||
return {"songs": songs, "info": album}
|
||||
return {"tracks": tracks, "info": album}
|
||||
|
||||
|
||||
@album_bp.route("/album/bio", methods=["POST"])
|
||||
@@ -80,14 +81,11 @@ def get_albumartists():
|
||||
"""Returns a list of artists featured in a given album."""
|
||||
data = request.get_json()
|
||||
|
||||
album = data["album"]
|
||||
artist = data["artist"]
|
||||
album, artist = data["album"], data["artist"]
|
||||
albumhash = helpers.create_album_hash(album, artist)
|
||||
|
||||
tracks: List[models.Track] = []
|
||||
|
||||
for track in api.TRACKS:
|
||||
if track.album == album and track.albumartist == artist:
|
||||
tracks.append(track)
|
||||
tracks = instances.tracks_instance.find_tracks_by_hash(albumhash)
|
||||
tracks = [models.Track(t) for t in tracks]
|
||||
|
||||
artists = []
|
||||
|
||||
|
||||
+32
-33
@@ -22,8 +22,12 @@ TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist
|
||||
|
||||
@playlist_bp.route("/playlists", methods=["GET"])
|
||||
def get_all_playlists():
|
||||
"""Returns all the playlists."""
|
||||
dbplaylists = instances.playlist_instance.get_all_playlists()
|
||||
dbplaylists = [models.Playlist(p) for p in dbplaylists]
|
||||
|
||||
playlists = [
|
||||
serializer.Playlist(p, construct_last_updated=False) for p in api.PLAYLISTS
|
||||
serializer.Playlist(p, construct_last_updated=False) for p in dbplaylists
|
||||
]
|
||||
playlists.sort(
|
||||
key=lambda p: datetime.strptime(p.lastUpdated, "%Y-%m-%d %H:%M:%S"),
|
||||
@@ -36,7 +40,7 @@ def get_all_playlists():
|
||||
def create_playlist():
|
||||
data = request.get_json()
|
||||
|
||||
playlist = {
|
||||
data = {
|
||||
"name": data["name"],
|
||||
"description": "",
|
||||
"pre_tracks": [],
|
||||
@@ -45,21 +49,16 @@ def create_playlist():
|
||||
"thumb": "",
|
||||
}
|
||||
|
||||
try:
|
||||
for pl in api.PLAYLISTS:
|
||||
if pl.name == playlist["name"]:
|
||||
raise PlaylistExists("Playlist already exists.")
|
||||
dbp = instances.playlist_instance.get_playlist_by_name(data["name"])
|
||||
|
||||
except PlaylistExists as e:
|
||||
return {"error": str(e)}, 409
|
||||
if dbp is not None:
|
||||
return {"message": "Playlist already exists."}, 409
|
||||
|
||||
upsert_id = instances.playlist_instance.insert_playlist(playlist)
|
||||
upsert_id = instances.playlist_instance.insert_playlist(data)
|
||||
p = instances.playlist_instance.get_playlist_by_id(upsert_id)
|
||||
pp = models.Playlist(p)
|
||||
playlist = models.Playlist(p)
|
||||
|
||||
api.PLAYLISTS.append(pp)
|
||||
|
||||
return {"playlist": pp}, 201
|
||||
return {"playlist": playlist}, 201
|
||||
|
||||
|
||||
@playlist_bp.route("/playlist/<playlist_id>/add", methods=["POST"])
|
||||
@@ -70,22 +69,22 @@ def add_track_to_playlist(playlist_id: str):
|
||||
|
||||
try:
|
||||
playlistlib.add_track(playlist_id, trackid)
|
||||
except TrackExistsInPlaylist as e:
|
||||
except TrackExistsInPlaylist:
|
||||
return {"error": "Track already exists in playlist"}, 409
|
||||
|
||||
return {"msg": "I think It's done"}, 200
|
||||
|
||||
|
||||
@playlist_bp.route("/playlist/<playlistid>")
|
||||
def get_single_p_info(playlistid: str):
|
||||
p = UseBisection(api.PLAYLISTS, "playlistid", [playlistid])()
|
||||
playlist: models.Playlist = p[0]
|
||||
def get_playlist(playlistid: str):
|
||||
p = instances.playlist_instance.get_playlist_by_id(playlistid)
|
||||
if p is None:
|
||||
return {"info": {}, "tracks": []}
|
||||
|
||||
if playlist is not None:
|
||||
tracks = playlist.get_tracks()
|
||||
return {"info": serializer.Playlist(playlist), "tracks": tracks}
|
||||
playlist = models.Playlist(p)
|
||||
|
||||
return {"info": {}, "tracks": []}
|
||||
tracks = playlistlib.create_playlist_tracks(playlist.pretracks)
|
||||
return {"info": serializer.Playlist(playlist), "tracks": tracks}
|
||||
|
||||
|
||||
@playlist_bp.route("/playlist/<playlistid>/update", methods=["PUT"])
|
||||
@@ -109,21 +108,21 @@ def update_playlist(playlistid: str):
|
||||
p: models.Playlist = p[0]
|
||||
|
||||
if playlist is not None:
|
||||
if image:
|
||||
image_, thumb_ = playlistlib.save_p_image(image, playlistid)
|
||||
playlist["image"] = image_
|
||||
playlist["thumb"] = thumb_
|
||||
if image:
|
||||
image_, thumb_ = playlistlib.save_p_image(image, playlistid)
|
||||
playlist["image"] = image_
|
||||
playlist["thumb"] = thumb_
|
||||
|
||||
else:
|
||||
playlist["image"] = p.image.split("/")[-1]
|
||||
playlist["thumb"] = p.thumb.split("/")[-1]
|
||||
else:
|
||||
playlist["image"] = p.image.split("/")[-1]
|
||||
playlist["thumb"] = p.thumb.split("/")[-1]
|
||||
|
||||
p.update_playlist(playlist)
|
||||
instances.playlist_instance.update_playlist(playlistid, playlist)
|
||||
p.update_playlist(playlist)
|
||||
instances.playlist_instance.update_playlist(playlistid, playlist)
|
||||
|
||||
return {
|
||||
"data": serializer.Playlist(p),
|
||||
}
|
||||
return {
|
||||
"data": serializer.Playlist(p),
|
||||
}
|
||||
|
||||
return {"msg": "Something shady happened"}, 500
|
||||
|
||||
|
||||
@@ -95,18 +95,9 @@ def search():
|
||||
|
||||
return {
|
||||
"data": [
|
||||
{
|
||||
"tracks": tracks[:5],
|
||||
"more": len(tracks) > 5
|
||||
},
|
||||
{
|
||||
"albums": albums[:6],
|
||||
"more": len(albums) > 6
|
||||
},
|
||||
{
|
||||
"artists": artists_dicts[:6],
|
||||
"more": len(artists_dicts) > 6
|
||||
},
|
||||
{"tracks": tracks[:5], "more": len(tracks) > 5},
|
||||
{"albums": albums[:6], "more": len(albums) > 6},
|
||||
{"artists": artists_dicts[:6], "more": len(artists_dicts) > 6},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -121,18 +112,18 @@ def search_load_more():
|
||||
|
||||
if type == "tracks":
|
||||
return {
|
||||
"tracks": SEARCH_RESULTS["tracks"][index:index + 5],
|
||||
"tracks": SEARCH_RESULTS["tracks"][index : index + 5],
|
||||
"more": len(SEARCH_RESULTS["tracks"]) > index + 5,
|
||||
}
|
||||
|
||||
elif type == "albums":
|
||||
return {
|
||||
"albums": SEARCH_RESULTS["albums"][index:index + 6],
|
||||
"albums": SEARCH_RESULTS["albums"][index : index + 6],
|
||||
"more": len(SEARCH_RESULTS["albums"]) > index + 6,
|
||||
}
|
||||
|
||||
elif type == "artists":
|
||||
return {
|
||||
"artists": SEARCH_RESULTS["artists"][index:index + 6],
|
||||
"artists": SEARCH_RESULTS["artists"][index : index + 6],
|
||||
"more": len(SEARCH_RESULTS["artists"]) > index + 6,
|
||||
}
|
||||
|
||||
+8
-12
@@ -6,6 +6,8 @@ from app import instances
|
||||
from flask import Blueprint
|
||||
from flask import send_file
|
||||
|
||||
from app import models
|
||||
|
||||
track_bp = Blueprint("track", __name__, url_prefix="/")
|
||||
|
||||
|
||||
@@ -14,21 +16,15 @@ def send_track_file(trackid):
|
||||
"""
|
||||
Returns an audio file that matches the passed id to the client.
|
||||
"""
|
||||
try:
|
||||
files = []
|
||||
for f in api.DB_TRACKS:
|
||||
try:
|
||||
if f["_id"]["$oid"] == trackid:
|
||||
files.append(f["filepath"])
|
||||
except KeyError:
|
||||
# Bug: some albums are not found although they exist in `api.ALBUMS`. It has something to do with the bisection method used or sorting. Not sure yet.
|
||||
pass
|
||||
track = instances.tracks_instance.get_track_by_id(trackid)
|
||||
|
||||
filepath = files[0]
|
||||
except IndexError:
|
||||
if track is None:
|
||||
return "File not found", 404
|
||||
|
||||
return send_file(filepath, mimetype="audio/mp3")
|
||||
track = models.Track(track)
|
||||
type = track.filepath.split(".")[-1]
|
||||
|
||||
return send_file(track.filepath, mimetype=f"audio/{type}")
|
||||
|
||||
|
||||
@track_bp.route("/sample")
|
||||
|
||||
@@ -200,3 +200,15 @@ class TrackMethods:
|
||||
Removes a track from the database. Returns a boolean indicating success or failure of the operation.
|
||||
"""
|
||||
pass
|
||||
|
||||
def find_tracks_by_hash():
|
||||
"""
|
||||
Returns all the tracks matching the passed hash.
|
||||
"""
|
||||
pass
|
||||
|
||||
def find_tracks_inside_path_regex():
|
||||
"""
|
||||
Returns a list of all the tracks matching the path in the query params.
|
||||
"""
|
||||
pass
|
||||
|
||||
@@ -21,16 +21,17 @@ class Albums(MongoAlbums):
|
||||
"""
|
||||
album = album.__dict__
|
||||
return self.collection.update_one(
|
||||
{
|
||||
"album": album["title"],
|
||||
"artist": album["artist"]
|
||||
},
|
||||
{
|
||||
"$set": album
|
||||
},
|
||||
{"album": album["title"], "artist": album["artist"]},
|
||||
{"$set": album},
|
||||
upsert=True,
|
||||
).upserted_id
|
||||
|
||||
def insert_many(self, albums: list):
|
||||
"""
|
||||
Inserts multiple albums into the database.
|
||||
"""
|
||||
return self.collection.insert_many(albums)
|
||||
|
||||
def get_all_albums(self) -> list:
|
||||
"""
|
||||
Returns all the albums in the database.
|
||||
@@ -52,9 +53,9 @@ class Albums(MongoAlbums):
|
||||
album = self.collection.find_one({"album": name, "artist": artist})
|
||||
return convert_one(album)
|
||||
|
||||
def get_album_by_artist(self, name: str) -> dict:
|
||||
def find_album_by_hash(self, hash: str) -> dict:
|
||||
"""
|
||||
Returns a single album matching the artist in the query params.
|
||||
Returns a single album matching the hash in the query params.
|
||||
"""
|
||||
album = self.collection.find_one({"albumartist": name})
|
||||
album = self.collection.find_one({"hash": hash})
|
||||
return convert_one(album)
|
||||
|
||||
@@ -18,12 +18,8 @@ class Playlists(MongoPlaylists):
|
||||
Inserts a new playlist object into the database.
|
||||
"""
|
||||
return self.collection.update_one(
|
||||
{
|
||||
"name": playlist["name"]
|
||||
},
|
||||
{
|
||||
"$set": playlist
|
||||
},
|
||||
{"name": playlist["name"]},
|
||||
{"$set": playlist},
|
||||
upsert=True,
|
||||
).upserted_id
|
||||
|
||||
@@ -41,25 +37,28 @@ class Playlists(MongoPlaylists):
|
||||
playlist = self.collection.find_one({"_id": ObjectId(id)})
|
||||
return convert_one(playlist)
|
||||
|
||||
def add_track_to_playlist(self, playlistid: str, track: dict) -> None:
|
||||
def set_last_updated(self, playlistid: str) -> None:
|
||||
"""
|
||||
Adds a track to a playlist.
|
||||
Sets the lastUpdated field to the current date.
|
||||
"""
|
||||
date = create_new_date()
|
||||
|
||||
return self.collection.update_one(
|
||||
{"_id": ObjectId(playlistid)},
|
||||
{"$set": {"lastUpdated": date}},
|
||||
)
|
||||
|
||||
def add_track_to_playlist(self, playlistid: str, track: dict) -> None:
|
||||
"""
|
||||
Adds a track to a playlist.
|
||||
"""
|
||||
self.collection.update_one(
|
||||
{
|
||||
"_id": ObjectId(playlistid),
|
||||
},
|
||||
{
|
||||
"$push": {
|
||||
"pre_tracks": track
|
||||
},
|
||||
"$set": {
|
||||
"lastUpdated": date
|
||||
}
|
||||
},
|
||||
{"$push": {"pre_tracks": track}},
|
||||
)
|
||||
self.set_last_updated(playlistid)
|
||||
|
||||
def get_playlist_by_name(self, name: str) -> dict:
|
||||
"""
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
This file contains the AllSongs class for interacting with track documents in MongoDB.
|
||||
"""
|
||||
import pymongo
|
||||
from app.db.mongodb import convert_many
|
||||
from app.db.mongodb import convert_one
|
||||
from app.db.mongodb import MongoTracks
|
||||
from app.db.mongodb import MongoTracks, convert_many, convert_one
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
@@ -24,17 +22,23 @@ class Tracks(MongoTracks):
|
||||
{"filepath": song_obj["filepath"]}, {"$set": song_obj}, upsert=True
|
||||
).upserted_id
|
||||
|
||||
def insert_many(self, songs: list):
|
||||
"""
|
||||
Inserts multiple songs into the database.
|
||||
"""
|
||||
return self.collection.insert_many(songs)
|
||||
|
||||
def get_all_tracks(self) -> list:
|
||||
"""
|
||||
Returns all tracks in the database.
|
||||
"""
|
||||
return convert_many(self.collection.find())
|
||||
|
||||
def get_song_by_id(self, file_id: str) -> dict:
|
||||
def get_track_by_id(self, id: str) -> dict:
|
||||
"""
|
||||
Returns a track object by its mongodb id.
|
||||
"""
|
||||
song = self.collection.find_one({"_id": ObjectId(file_id)})
|
||||
song = self.collection.find_one({"_id": ObjectId(id)})
|
||||
return convert_one(song)
|
||||
|
||||
def get_song_by_album(self, name: str, artist: str) -> dict:
|
||||
@@ -81,11 +85,20 @@ class Tracks(MongoTracks):
|
||||
|
||||
def find_songs_by_folder_og(self, query: str) -> list:
|
||||
"""
|
||||
Returns an unsorted list of all the tracks exactly matching the folder in the query params
|
||||
Returns an unsorted list of all the track matching the folder in the query params
|
||||
"""
|
||||
songs = self.collection.find({"folder": query})
|
||||
return convert_many(songs)
|
||||
|
||||
def find_tracks_inside_path_regex(self, path: str) -> list:
|
||||
"""
|
||||
Returns a list of all the tracks matching the path in the query params.
|
||||
"""
|
||||
songs = self.collection.find(
|
||||
{"filepath": {"$regex": f"^{path}", "$options": "i"}}
|
||||
)
|
||||
return convert_many(songs)
|
||||
|
||||
def find_songs_by_artist(self, query: str) -> list:
|
||||
"""
|
||||
Returns a list of all the tracks exactly matching the artists in the query params.
|
||||
@@ -128,3 +141,21 @@ class Tracks(MongoTracks):
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def find_tracks_by_hash(self, hash: str) -> list:
|
||||
"""
|
||||
Returns a list of all the tracks matching the hash in the query params.
|
||||
"""
|
||||
songs = self.collection.find({"albumhash": hash})
|
||||
return convert_many(songs)
|
||||
|
||||
def find_track_by_title_artists_album(
|
||||
self, title: str, artist: str, album: str
|
||||
) -> dict:
|
||||
"""
|
||||
Returns a single track matching the title, artist, and album in the query params.
|
||||
"""
|
||||
song = self.collection.find_one(
|
||||
{"title": title, "artists": artist, "album": album}
|
||||
)
|
||||
return convert_one(song)
|
||||
|
||||
@@ -14,7 +14,9 @@ from app.lib.populate import Populate
|
||||
from PIL import Image
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from app.lib.trackslib import create_all_tracks
|
||||
from app.lib.trackslib import validate_tracks
|
||||
from app.lib import trackslib
|
||||
from server.app import instances, models
|
||||
|
||||
|
||||
@helpers.background
|
||||
@@ -24,6 +26,8 @@ def reindex_tracks():
|
||||
"""
|
||||
|
||||
while True:
|
||||
trackslib.validate_tracks()
|
||||
|
||||
populate()
|
||||
CheckArtistImages()()
|
||||
|
||||
@@ -42,10 +46,6 @@ def populate():
|
||||
pop = Populate()
|
||||
pop.run()
|
||||
|
||||
tracks = create_all_tracks()
|
||||
api.TRACKS.clear()
|
||||
api.TRACKS.extend(tracks)
|
||||
|
||||
|
||||
class getArtistImage:
|
||||
"""
|
||||
@@ -104,11 +104,11 @@ class CheckArtistImages:
|
||||
"""
|
||||
Loops through all the tracks and gathers all the artists.
|
||||
"""
|
||||
ar = instances.tracks_instance.get_all_tracks()
|
||||
tracks = [models.Track(t) for t in ar]
|
||||
|
||||
for song in api.DB_TRACKS:
|
||||
this_artists: list = song["artists"].split(", ")
|
||||
|
||||
for artist in this_artists:
|
||||
for t in tracks:
|
||||
for artist in t.artists:
|
||||
if artist not in self.artists:
|
||||
self.artists.append(artist)
|
||||
|
||||
|
||||
@@ -4,12 +4,8 @@ This library contains all the functions related to albums.
|
||||
import random
|
||||
from typing import List
|
||||
|
||||
from app import api
|
||||
from app import helpers
|
||||
from app import instances
|
||||
from app import models
|
||||
from app.lib import taglib
|
||||
from app.lib import trackslib
|
||||
from app import api, helpers, instances, models
|
||||
from app.lib import taglib, trackslib
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
@@ -30,21 +26,12 @@ def get_all_albums() -> List[models.Album]:
|
||||
return albums
|
||||
|
||||
|
||||
def create_everything() -> List[models.Track]:
|
||||
def validate() -> None:
|
||||
"""
|
||||
Creates album objects for all albums and returns
|
||||
a list of track objects
|
||||
"""
|
||||
albums: list[models.Album] = get_all_albums()
|
||||
|
||||
api.ALBUMS = albums
|
||||
api.ALBUMS.sort(key=lambda x: x.hash)
|
||||
|
||||
tracks = trackslib.create_all_tracks()
|
||||
|
||||
api.TRACKS.clear()
|
||||
api.TRACKS.extend(tracks)
|
||||
api.TRACKS.sort(key=lambda x: x.title)
|
||||
|
||||
|
||||
def find_album(albums: List[models.Album], hash: str) -> int | None:
|
||||
@@ -142,8 +129,6 @@ class GetAlbumTracks:
|
||||
self.tracks.remove(track)
|
||||
index = trackslib.find_track(self.tracks, self.hash)
|
||||
|
||||
# self.tracks.extend(tracks)
|
||||
# self.tracks.sort(key=lambda x: x["albumhash"])
|
||||
return tracks
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
from os import scandir
|
||||
from time import time
|
||||
from typing import List
|
||||
from typing import Set
|
||||
from typing import Tuple
|
||||
|
||||
from app import api
|
||||
from app import helpers
|
||||
from app.models import Folder
|
||||
from app.models import Track
|
||||
from tqdm import tqdm
|
||||
|
||||
from app import instances
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -18,20 +14,12 @@ class Dir:
|
||||
is_sym: bool
|
||||
|
||||
|
||||
def get_valid_folders() -> None:
|
||||
for track in api.TRACKS:
|
||||
api.VALID_FOLDERS.add(track.folder)
|
||||
|
||||
|
||||
def get_folder_track_count(foldername: str) -> int:
|
||||
"""
|
||||
Returns the number of files associated with a folder.
|
||||
"""
|
||||
count = 0
|
||||
for track in api.TRACKS:
|
||||
if foldername in track.folder:
|
||||
count += 1
|
||||
return count
|
||||
tracks = instances.tracks_instance.find_tracks_inside_path_regex(foldername)
|
||||
return len(tracks)
|
||||
|
||||
|
||||
def create_folder(dir: Dir) -> Folder:
|
||||
@@ -46,51 +34,6 @@ def create_folder(dir: Dir) -> Folder:
|
||||
return Folder(folder)
|
||||
|
||||
|
||||
def create_all_folders() -> Set[Folder]:
|
||||
folders: List[Folder] = []
|
||||
|
||||
for foldername in tqdm(api.VALID_FOLDERS, desc="Creating folders"):
|
||||
folder = create_folder(foldername)
|
||||
folders.append(folder)
|
||||
|
||||
return folders
|
||||
|
||||
|
||||
def get_subdirs(foldername: str) -> List[Folder]:
|
||||
"""
|
||||
Finds and Creates Folder objects for each sub-directory string in the foldername passed.
|
||||
"""
|
||||
subdirs = set()
|
||||
|
||||
for folder in api.VALID_FOLDERS:
|
||||
if foldername in folder:
|
||||
str0 = folder.replace(foldername, "")
|
||||
|
||||
try:
|
||||
str1 = str0.split("/")[1]
|
||||
except IndexError:
|
||||
str1 = None
|
||||
|
||||
if str1 is not None:
|
||||
subdirs.add(foldername + "/" + str1)
|
||||
return [create_folder(dir) for dir in subdirs]
|
||||
|
||||
|
||||
@helpers.background
|
||||
def run_scandir():
|
||||
"""
|
||||
Initiates the creation of all folder objects for each folder with a track in it.
|
||||
|
||||
Runs in a background thread after every 5 minutes.
|
||||
It calls the
|
||||
"""
|
||||
get_valid_folders()
|
||||
# folders_ = create_all_folders()
|
||||
"""Create all the folder objects before clearing api.FOLDERS"""
|
||||
|
||||
# api.FOLDERS = folders_
|
||||
|
||||
|
||||
class getFnF:
|
||||
"""
|
||||
Get files and folders from a directory.
|
||||
@@ -99,15 +42,6 @@ class getFnF:
|
||||
def __init__(self, path: str) -> None:
|
||||
self.path = path
|
||||
|
||||
@classmethod
|
||||
def get_tracks(cls, files: List[str]) -> List[Track]:
|
||||
"""
|
||||
Returns a list of Track objects for each file in the given list.
|
||||
"""
|
||||
tracks = helpers.UseBisection(api.TRACKS, "filepath", files)()
|
||||
tracks = filter(lambda t: t is not None, tracks)
|
||||
return list(tracks)
|
||||
|
||||
def __call__(self) -> Tuple[Track, Folder]:
|
||||
try:
|
||||
all = scandir(self.path)
|
||||
@@ -125,9 +59,11 @@ class getFnF:
|
||||
dirs.append(Dir(**dir))
|
||||
elif entry.is_file() and entry.name.endswith((".mp3", ".flac")):
|
||||
files.append(entry.path)
|
||||
tracks = self.get_tracks(files)
|
||||
tracks = instances.tracks_instance.find_songs_by_folder(self.path)
|
||||
tracks = [Track(track) for track in tracks]
|
||||
|
||||
folders = [create_folder(dir) for dir in dirs]
|
||||
|
||||
folders = filter(lambda f: f.trackcount > 0, folders)
|
||||
|
||||
return tracks, folders
|
||||
|
||||
@@ -5,6 +5,7 @@ import os
|
||||
import random
|
||||
import string
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
from tqdm import tqdm
|
||||
|
||||
@@ -13,55 +14,37 @@ from app import exceptions
|
||||
from app import instances
|
||||
from app import models
|
||||
from app import settings
|
||||
from app.lib import trackslib
|
||||
from PIL import Image
|
||||
from PIL import ImageSequence
|
||||
from progress.bar import Bar
|
||||
from werkzeug import datastructures
|
||||
|
||||
from app.lib import trackslib
|
||||
|
||||
TrackExistsInPlaylist = exceptions.TrackExistsInPlaylist
|
||||
|
||||
|
||||
def add_track(playlistid: str, trackid: str):
|
||||
"""
|
||||
Adds a track to a playlist in the api.PLAYLISTS dict and to the database.
|
||||
Adds a track to a playlist to the database.
|
||||
"""
|
||||
for playlist in api.PLAYLISTS:
|
||||
if playlist.playlistid == playlistid:
|
||||
tt = trackslib.get_track_by_id(trackid)
|
||||
tt = instances.tracks_instance.get_track_by_id(trackid)
|
||||
|
||||
track = {
|
||||
"title": tt.title,
|
||||
"artists": tt.artists,
|
||||
"album": tt.album,
|
||||
}
|
||||
if tt is None:
|
||||
return
|
||||
|
||||
try:
|
||||
playlist.add_track(track)
|
||||
instances.playlist_instance.add_track_to_playlist(
|
||||
playlistid, track)
|
||||
return
|
||||
except TrackExistsInPlaylist as error:
|
||||
raise error
|
||||
track = models.Track(tt)
|
||||
|
||||
playlist = instances.playlist_instance.get_playlist_by_id(playlistid)
|
||||
|
||||
def get_playlist_tracks(pid: str):
|
||||
for p in api.PLAYLISTS:
|
||||
if p.playlistid == pid:
|
||||
return p.tracks
|
||||
track = {
|
||||
"title": track.title,
|
||||
"artists": tt["artists"],
|
||||
"album": track.album,
|
||||
}
|
||||
if track in playlist["pre_tracks"]:
|
||||
raise TrackExistsInPlaylist
|
||||
|
||||
|
||||
def create_all_playlists():
|
||||
"""
|
||||
Gets all playlists from the database.
|
||||
"""
|
||||
playlists = instances.playlist_instance.get_all_playlists()
|
||||
|
||||
|
||||
for playlist in tqdm(playlists, desc="Creating playlists"):
|
||||
api.PLAYLISTS.append(models.Playlist(playlist))
|
||||
|
||||
validate_images()
|
||||
instances.playlist_instance.add_track_to_playlist(playlistid, track)
|
||||
|
||||
|
||||
def create_thumbnail(image: any, img_path: str) -> str:
|
||||
@@ -69,8 +52,7 @@ def create_thumbnail(image: any, img_path: str) -> str:
|
||||
Creates a 250 x 250 thumbnail from a playlist image
|
||||
"""
|
||||
thumb_path = "thumb_" + img_path
|
||||
full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists",
|
||||
thumb_path)
|
||||
full_thumb_path = os.path.join(settings.APP_DIR, "images", "playlists", thumb_path)
|
||||
|
||||
aspect_ratio = image.width / image.height
|
||||
|
||||
@@ -88,13 +70,11 @@ def save_p_image(file: datastructures.FileStorage, pid: str):
|
||||
"""
|
||||
img = Image.open(file)
|
||||
|
||||
random_str = "".join(
|
||||
random.choices(string.ascii_letters + string.digits, k=5))
|
||||
random_str = "".join(random.choices(string.ascii_letters + string.digits, k=5))
|
||||
|
||||
img_path = pid + str(random_str) + ".webp"
|
||||
|
||||
full_img_path = os.path.join(settings.APP_DIR, "images", "playlists",
|
||||
img_path)
|
||||
full_img_path = os.path.join(settings.APP_DIR, "images", "playlists", img_path)
|
||||
|
||||
if file.content_type == "image/gif":
|
||||
frames = []
|
||||
@@ -118,8 +98,10 @@ def validate_images():
|
||||
Removes all unused images in the images/playlists folder.
|
||||
"""
|
||||
images = []
|
||||
p = instances.playlist_instance.get_all_playlists()
|
||||
playlists = [models.Playlist(p) for p in p]
|
||||
|
||||
for playlist in api.PLAYLISTS:
|
||||
for playlist in playlists:
|
||||
if playlist.image:
|
||||
img_path = playlist.image.split("/")[-1]
|
||||
thumb_path = playlist.thumb.split("/")[-1]
|
||||
@@ -136,3 +118,18 @@ def validate_images():
|
||||
|
||||
def create_new_date():
|
||||
return datetime.now()
|
||||
|
||||
|
||||
def create_playlist_tracks(playlist_tracks: List) -> List[models.Track]:
|
||||
"""
|
||||
Creates a list of model.Track objects from a list of playlist track dicts.
|
||||
"""
|
||||
tracks: List[models.Track] = []
|
||||
|
||||
for t in playlist_tracks:
|
||||
track = trackslib.get_p_track(t)
|
||||
|
||||
if track is not None:
|
||||
tracks.append(models.Track(track))
|
||||
|
||||
return tracks
|
||||
|
||||
+16
-43
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
from pprint import pprint
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from copy import deepcopy
|
||||
@@ -12,7 +13,6 @@ from app.helpers import create_album_hash
|
||||
from app.helpers import run_fast_scandir
|
||||
from app.instances import album_instance
|
||||
from app.instances import tracks_instance
|
||||
from app.lib import folderslib
|
||||
from app.lib.albumslib import create_album
|
||||
from app.lib.albumslib import find_album
|
||||
from app.lib.taglib import get_tags
|
||||
@@ -44,6 +44,7 @@ class Populate:
|
||||
self.db_tracks = tracks_instance.get_all_tracks()
|
||||
self.tag_count = 0
|
||||
self.exist_count = 0
|
||||
self.tracks = []
|
||||
|
||||
def run(self):
|
||||
self.check_untagged()
|
||||
@@ -53,17 +54,14 @@ class Populate:
|
||||
return
|
||||
|
||||
self.tagged_tracks.sort(key=lambda x: x["albumhash"])
|
||||
self.tracks = deepcopy(self.tagged_tracks)
|
||||
|
||||
self.pre_albums = self.create_pre_albums(self.tagged_tracks)
|
||||
self.create_albums(self.pre_albums)
|
||||
|
||||
self.albums.sort(key=lambda x: x.hash)
|
||||
api.ALBUMS.sort(key=lambda x: x.hash)
|
||||
|
||||
self.save_albums()
|
||||
self.create_tracks()
|
||||
# self.create_folders()
|
||||
|
||||
self.save_all()
|
||||
|
||||
def check_untagged(self):
|
||||
"""
|
||||
@@ -84,7 +82,6 @@ class Populate:
|
||||
|
||||
t["albumhash"] = create_album_hash(t["album"], t["albumartist"])
|
||||
self.tagged_tracks.append(t)
|
||||
api.DB_TRACKS.append(t)
|
||||
|
||||
self.folders.add(t["folder"])
|
||||
|
||||
@@ -95,8 +92,7 @@ class Populate:
|
||||
folder = tags["folder"]
|
||||
self.folders.add(folder)
|
||||
|
||||
tags["albumhash"] = create_album_hash(tags["album"],
|
||||
tags["albumartist"])
|
||||
tags["albumhash"] = create_album_hash(tags["album"], tags["albumartist"])
|
||||
self.tagged_tracks.append(tags)
|
||||
api.DB_TRACKS.append(tags)
|
||||
|
||||
@@ -110,13 +106,6 @@ class Populate:
|
||||
with ThreadPoolExecutor() as executor:
|
||||
executor.map(self.get_tags, self.files)
|
||||
|
||||
# with Pool(maxtasksperchild=10, processes=10) as p:
|
||||
# tags = p.map(get_tags, tqdm(self.files))
|
||||
# self.process_tags(tags)
|
||||
|
||||
# for t in tqdm(self.files):
|
||||
# self.get_tags(t)
|
||||
|
||||
d = time.time() - s
|
||||
Log(f"Tagged {len(self.tagged_tracks)} files in {d} seconds")
|
||||
|
||||
@@ -147,7 +136,6 @@ class Populate:
|
||||
self.exist_count += 1
|
||||
return
|
||||
|
||||
self.albums.sort(key=lambda x: x.hash)
|
||||
index = find_track(self.tagged_tracks, albumhash)
|
||||
|
||||
if index is None:
|
||||
@@ -163,7 +151,6 @@ class Populate:
|
||||
|
||||
album = Album(album)
|
||||
|
||||
api.ALBUMS.append(album)
|
||||
self.albums.append(album)
|
||||
|
||||
def create_albums(self, albums: List[dict]):
|
||||
@@ -173,8 +160,7 @@ class Populate:
|
||||
for album in tqdm(albums, desc="Building albums"):
|
||||
self.create_album(album)
|
||||
|
||||
Log(f"{self.exist_count} of {len(albums)} albums were already in the database"
|
||||
)
|
||||
Log(f"{self.exist_count} of {len(albums)} albums were already in the database")
|
||||
|
||||
def create_track(self, track: dict):
|
||||
"""
|
||||
@@ -196,38 +182,25 @@ class Populate:
|
||||
pass
|
||||
|
||||
track["image"] = album.image
|
||||
|
||||
upsert_id = tracks_instance.insert_song(track)
|
||||
track["_id"] = {"$oid": str(upsert_id)}
|
||||
|
||||
api.TRACKS.append(Track(track))
|
||||
return track
|
||||
|
||||
def create_tracks(self):
|
||||
"""
|
||||
Loops through all the tagged tracks creating complete track objects using the `models.Track` model.
|
||||
"""
|
||||
with ThreadPoolExecutor() as executor:
|
||||
executor.map(self.create_track, self.tagged_tracks)
|
||||
iterable = executor.map(self.create_track, self.tagged_tracks)
|
||||
|
||||
Log(f"Added {len(self.tagged_tracks)} new tracks and {len(self.albums)} new albums"
|
||||
)
|
||||
self.tracks = [t for t in iterable if t is not None]
|
||||
|
||||
def save_albums(self):
|
||||
Log(
|
||||
f"Added {len(self.tagged_tracks)} new tracks and {len(self.albums)} new albums"
|
||||
)
|
||||
|
||||
def save_all(self):
|
||||
"""
|
||||
Saves the albums to the database.
|
||||
"""
|
||||
|
||||
with ThreadPoolExecutor() as executor:
|
||||
executor.map(album_instance.insert_album, self.albums)
|
||||
|
||||
# def create_folders(self):
|
||||
# """
|
||||
# Creates the folder objects for all the tracks.
|
||||
# """
|
||||
# for folder in tqdm(self.folders, desc="Creating folders"):
|
||||
# api.VALID_FOLDERS.add(folder)
|
||||
|
||||
# fff = folderslib.create_folder(folder)
|
||||
# api.FOLDERS.append(fff)
|
||||
|
||||
# Log(f"Created {len(self.folders)} new folders")
|
||||
album_instance.insert_many([a.__dict__ for a in self.albums])
|
||||
tracks_instance.insert_many(self.tracks)
|
||||
|
||||
+11
-12
@@ -2,31 +2,25 @@
|
||||
This library contains all the functions related to tracks.
|
||||
"""
|
||||
import os
|
||||
from pprint import pprint
|
||||
from typing import List
|
||||
|
||||
from app import api
|
||||
from app import instances
|
||||
from app import models
|
||||
from app import api, instances, models
|
||||
from app.helpers import remove_duplicates
|
||||
from tqdm import tqdm
|
||||
|
||||
|
||||
def create_all_tracks() -> List[models.Track]:
|
||||
def validate_tracks() -> None:
|
||||
"""
|
||||
Gets all songs under the ~/ directory.
|
||||
"""
|
||||
tracks: list[models.Track] = []
|
||||
entries = instances.tracks_instance.get_all_tracks()
|
||||
|
||||
for track in tqdm(api.DB_TRACKS, desc="Creating tracks"):
|
||||
for track in tqdm(entries, desc="Validating tracks"):
|
||||
try:
|
||||
os.chmod(track["filepath"], 0o755)
|
||||
except FileNotFoundError:
|
||||
instances.tracks_instance.remove_song_by_id(track["_id"]["$oid"])
|
||||
api.DB_TRACKS.remove(track)
|
||||
|
||||
tracks.append(models.Track(track))
|
||||
|
||||
return tracks
|
||||
|
||||
|
||||
def get_album_tracks(albumname, artist):
|
||||
@@ -48,7 +42,6 @@ def get_track_by_id(trackid: str) -> models.Track:
|
||||
return track
|
||||
except AttributeError:
|
||||
print("AttributeError")
|
||||
print(track)
|
||||
|
||||
|
||||
def find_track(tracks: list, hash: str) -> int or None:
|
||||
@@ -73,3 +66,9 @@ def find_track(tracks: list, hash: str) -> int or None:
|
||||
right = mid - 1
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_p_track(ptrack):
|
||||
return instances.tracks_instance.find_track_by_title_artists_album(
|
||||
ptrack["title"], ptrack["artists"], ptrack["album"]
|
||||
)
|
||||
|
||||
+7
-36
@@ -4,7 +4,7 @@ Contains all the models for objects generation and typing.
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List
|
||||
|
||||
from app import api, helpers
|
||||
from app import helpers
|
||||
from app.exceptions import TrackExistsInPlaylist
|
||||
|
||||
|
||||
@@ -117,30 +117,6 @@ class Album:
|
||||
return self.artist.lower() == "various artists"
|
||||
|
||||
|
||||
def get_p_track(ptrack):
|
||||
for track in api.TRACKS:
|
||||
if (
|
||||
track.title == ptrack["title"]
|
||||
and track.artists == ptrack["artists"]
|
||||
and ptrack["album"] == track.album
|
||||
):
|
||||
return track
|
||||
|
||||
|
||||
def create_playlist_tracks(playlist_tracks: List) -> List[Track]:
|
||||
"""
|
||||
Creates a list of model.Track objects from a list of playlist track dicts.
|
||||
"""
|
||||
tracks: List[Track] = []
|
||||
|
||||
for t in playlist_tracks:
|
||||
track = get_p_track(t)
|
||||
if track is not None:
|
||||
tracks.append(track)
|
||||
|
||||
return tracks
|
||||
|
||||
|
||||
@dataclass
|
||||
class Playlist:
|
||||
"""Creates playlist objects"""
|
||||
@@ -148,7 +124,7 @@ class Playlist:
|
||||
playlistid: str
|
||||
name: str
|
||||
tracks: List[Track]
|
||||
_pre_tracks: list = field(init=False, repr=False)
|
||||
pretracks: list = field(init=False, repr=False)
|
||||
lastUpdated: int
|
||||
image: str
|
||||
thumb: str
|
||||
@@ -162,16 +138,11 @@ class Playlist:
|
||||
self.description = data["description"]
|
||||
self.image = self.create_img_link(data["image"])
|
||||
self.thumb = self.create_img_link(data["thumb"])
|
||||
self._pre_tracks = data["pre_tracks"]
|
||||
self.pretracks = data["pre_tracks"]
|
||||
self.tracks = []
|
||||
self.lastUpdated = data["lastUpdated"]
|
||||
self.count = len(self._pre_tracks)
|
||||
self.count = len(self.pretracks)
|
||||
|
||||
def get_tracks(self) -> List[Track]:
|
||||
"""
|
||||
Generates and returns Track objects from pre_tracks
|
||||
"""
|
||||
return create_playlist_tracks(self._pre_tracks)
|
||||
|
||||
def create_img_link(self, image: str):
|
||||
if image:
|
||||
@@ -180,11 +151,11 @@ class Playlist:
|
||||
return "default.webp"
|
||||
|
||||
def update_count(self):
|
||||
self.count = len(self._pre_tracks)
|
||||
self.count = len(self.pretracks)
|
||||
|
||||
def add_track(self, track):
|
||||
if track not in self._pre_tracks:
|
||||
self._pre_tracks.append(track)
|
||||
if track not in self.pretracks:
|
||||
self.pretracks.append(track)
|
||||
self.update_count()
|
||||
self.lastUpdated = helpers.create_new_date()
|
||||
else:
|
||||
|
||||
@@ -11,7 +11,8 @@ CONFIG_FOLDER = ".alice"
|
||||
HOME_DIR = os.path.expanduser("~")
|
||||
APP_DIR = os.path.join(HOME_DIR, CONFIG_FOLDER)
|
||||
THUMBS_PATH = os.path.join(APP_DIR, "images", "thumbnails")
|
||||
TEST_DIR = "/home/cwilvx/Music/Link to Music/Chill"
|
||||
TEST_DIR = "/home/cwilvx/Music/Link to Music/Chill/Wolftyla Radio"
|
||||
HOME_DIR = TEST_DIR
|
||||
# URL
|
||||
IMG_BASE_URI = "http://127.0.0.1:8900/images/"
|
||||
IMG_ARTIST_URI = IMG_BASE_URI + "artists/"
|
||||
@@ -38,6 +39,7 @@ P_COLORS = [
|
||||
|
||||
CPU_COUNT = multiprocessing.cpu_count()
|
||||
|
||||
|
||||
class logger:
|
||||
enable = True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user