process albums seperate from tracks

- break populate function into 2
This commit is contained in:
geoffrey45
2022-06-19 14:45:25 +03:00
parent 06ed41d869
commit 3cf44759b5
11 changed files with 100 additions and 220 deletions
-1
View File
@@ -40,7 +40,6 @@ def get_albums():
def get_album():
"""Returns all the tracks in the given album."""
data = request.get_json()
print(data)
album, artist = data["album"], data["artist"]
albumhash = helpers.create_album_hash(album, artist)
+2 -2
View File
@@ -2,7 +2,6 @@
This file contains the Album class for interacting with
album documents in MongoDB.
"""
from app import db
from app.db.mongodb import convert_many
from app.db.mongodb import convert_one
from app.db.mongodb import MongoAlbums
@@ -26,7 +25,8 @@ class Albums(MongoAlbums):
upsert=True,
).upserted_id
def insert_many(self, albums: list):
def insert_many(self, albums: Album):
albums = [a.__dict__ for a in albums]
"""
Inserts multiple albums into the database.
"""
+3 -9
View File
@@ -6,16 +6,14 @@ import time
from io import BytesIO
import requests
from app import api
from app import helpers
from app import settings
from app.lib import watchdoge
from app.lib.populate import Populate
from app.lib.populate import Populate, CreateAlbums
from PIL import Image
from concurrent.futures import ThreadPoolExecutor
from app.lib import trackslib
from app import instances, models
@helpers.background
@@ -27,7 +25,8 @@ def reindex_tracks():
while True:
trackslib.validate_tracks()
populate()
Populate()
CreateAlbums()
CheckArtistImages()()
time.sleep(60)
@@ -41,11 +40,6 @@ def start_watchdog():
watchdoge.watch.run()
def populate():
pop = Populate()
pop.run()
class getArtistImage:
"""
Returns an artist image url.
+1 -12
View File
@@ -73,17 +73,6 @@ def remove_duplicates(tracklist: List[models.Track]) -> List[models.Track]:
return tracklist
# def save_image(url: str, path: str) -> None:
# """
# Saves an image from an url to a path.
# """
# response = requests.get(url)
# img = Image.open(BytesIO(response.content))
# img.save(path, "JPEG")
def is_valid_file(filename: str) -> bool:
"""
Checks if a file is valid. Returns True if it is, False if it isn't.
@@ -120,7 +109,7 @@ def create_album_hash(title: str, artist: str) -> str:
Creates a simple hash for an album
"""
lower = (title + artist).replace(" ", "").lower()
hash = lower.join([i for i in lower if i not in '/\\:*?"<>|&'])
hash = "".join([i for i in lower if i not in '/\\:*?"<>|&'])
return hash
+16 -62
View File
@@ -34,32 +34,6 @@ def validate() -> None:
"""
def find_album(albums: List[models.Album], hash: str) -> int | None:
"""
Finds an album by album title and artist.
:param `albums`: List of album objects.
:param `hash`: Hash of album.
:return: Index of album in list.
"""
left = 0
right = len(albums) - 1
while left <= right:
mid = (left + right) // 2
if albums[mid].hash == hash:
return mid
if albums[mid].hash < hash:
left = mid + 1
else:
right = mid - 1
return None
def get_album_duration(album: List[models.Track]) -> int:
"""
Gets the duration of an album.
@@ -81,31 +55,19 @@ def use_defaults() -> str:
return path
def gen_random_path() -> str:
"""
Generates a random image file path for an album image.
"""
choices = "abcdefghijklmnopqrstuvwxyz0123456789"
path = "".join(random.choice(choices) for i in range(20))
path += ".webp"
return path
def get_album_image(album: list) -> str:
def get_album_image(track: models.Track) -> str:
"""
Gets the image of an album.
"""
for track in album:
img_p = gen_random_path()
img_p = track.albumhash + ".webp"
exists = taglib.extract_thumb(track["filepath"], webp_path=img_p)
success = taglib.extract_thumb(track.filepath, webp_path=img_p)
if exists:
return img_p
if success:
return img_p
return use_defaults()
return None
class GetAlbumTracks:
@@ -122,14 +84,6 @@ class GetAlbumTracks:
def __call__(self):
tracks = helpers.UseBisection(self.tracks, "albumhash", [self.hash])()
pprint(tracks)
# while index is not None:
# track = self.tracks[index]
# tracks.append(track)
# self.tracks.remove(track)
# index = helpers.UseBisection(self.tracks, "albumhash", [self.hash])()
return tracks
@@ -137,23 +91,23 @@ def get_album_tracks(tracklist: List[models.Track], hash: str) -> List:
return GetAlbumTracks(tracklist, hash)()
def create_album(track: dict, tracklist: list[models.Track]) -> dict:
def create_album(track: models.Track) -> dict:
"""
Generates and returns an album object from a track object.
"""
album = {
"title": track["album"],
"artist": track["albumartist"],
"title": track.album,
"artist": track.albumartist,
"hash": track.albumhash,
}
albumhash = helpers.create_album_hash(album["title"], album["artist"])
album_tracks = get_album_tracks(tracklist, albumhash)
if len(album_tracks) == 0:
return None
album["date"] = track.date
album["date"] = album_tracks[0]["date"]
img_p = get_album_image(track)
album["image"] = get_album_image(album_tracks)
# album["image"] = "".join(x for x in albumhash if x not in "\/:*?<>|")
if img_p is not None:
album["image"] = img_p
return album
album["image"] = None
return album
+62 -101
View File
@@ -1,18 +1,17 @@
from dataclasses import dataclass
from pprint import pprint
import time
from concurrent.futures import ThreadPoolExecutor
from typing import List
from app import settings
from app.helpers import create_album_hash
from app.helpers import Get, UseBisection, 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.albumslib import create_album
from app.lib.albumslib import find_album
from app.lib.taglib import get_tags
from app.lib.trackslib import find_track
from app.logger import Log
from app.models import Album
from app.models import Album, Track
from tqdm import tqdm
from app import instances
@@ -28,36 +27,15 @@ class Populate:
"""
def __init__(self) -> None:
self.files = []
self.db_tracks = []
self.tagged_tracks = []
self.folders = set()
self.pre_albums = []
self.albums: List[Album] = []
self.files = run_fast_scandir(settings.HOME_DIR, full=True)[1]
self.db_tracks = tracks_instance.get_all_tracks()
self.tag_count = 0
self.exist_count = 0
self.tracks = []
def run(self):
self.check_untagged()
self.tag_untagged()
if len(self.tagged_tracks) == 0:
return
# self.tagged_tracks.sort(key=lambda x: x["albumhash"])
self.pre_albums = self.create_pre_albums(self.tagged_tracks)
self.create_albums(self.pre_albums)
self.albums.sort(key=lambda x: x.hash)
self.create_tracks()
self.save_all()
def check_untagged(self):
"""
Loops through all the tracks in db tracks removing each
@@ -88,97 +66,80 @@ class Populate:
with ThreadPoolExecutor() as executor:
executor.map(self.get_tags, self.files)
tracks_instance.insert_many(self.tagged_tracks)
if len(self.tagged_tracks) > 0:
tracks_instance.insert_many(self.tagged_tracks)
d = time.time() - s
Log(f"Tagged {len(self.tagged_tracks)} files in {d} seconds")
@dataclass
class PreAlbum:
title: str
artist: str
hash: str
class CreateAlbums:
def __init__(self) -> None:
self.db_tracks = Get.get_all_tracks()
self.db_albums = Get.get_all_albums()
prealbums = self.create_pre_albums(self.db_tracks)
prealbums = self.filter_processed(self.db_albums, prealbums)
print(f"📌 {len(prealbums)}")
albums = []
for album in tqdm(prealbums, desc="Creating albums"):
a = self.create_album(album)
albums.append(a)
if len(albums) > 0:
instances.album_instance.insert_many(albums)
@staticmethod
def create_pre_albums(tracks: List[dict]):
"""
Creates pre-albums for the all tagged tracks.
"""
def create_pre_albums(tracks: List[Track]) -> List[PreAlbum]:
prealbums = []
for track in tqdm(tracks, desc="Creating pre-albums"):
album = {"title": track["album"], "artist": track["albumartist"]}
for track in tqdm(tracks, desc="Creating prealbums"):
album = {
"title": track.album,
"artist": track.albumartist,
"hash": track.albumhash,
}
album = PreAlbum(**album)
if album not in prealbums:
prealbums.append(album)
Log(f"Created {len(prealbums)} pre-albums")
return prealbums
def create_album(self, album: dict):
albumhash = create_album_hash(album["title"], album["artist"])
album = instances.album_instance.find_album_by_hash(albumhash)
@staticmethod
def filter_processed(albums: List[Album], prealbums: List[PreAlbum]) -> List[dict]:
to_process = []
if album is not None:
self.albums.append(album)
self.exist_count += 1
return
for p in tqdm(prealbums, desc="Filtering processed albums"):
album = UseBisection(albums, "hash", [p.hash])()[0]
index = find_track(self.tagged_tracks, albumhash)
if album is None:
to_process.append(p)
track = self.tagged_tracks[index]
return to_process
album = create_album(track, self.tagged_tracks)
def create_album(self, album: PreAlbum) -> Album:
hash = album.hash
if album is None:
print("album is none")
return
album = {"image": None}
while album["image"] is None:
track = UseBisection(self.db_tracks, "albumhash", [hash])()[0]
if track is not None:
album = create_album(track)
self.db_tracks.remove(track)
else:
album["image"] = hash
album = Album(album)
self.albums.append(album)
def create_albums(self, albums: List[dict]):
"""
Uses the pre-albums to create new albums and add them to the database.
"""
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")
def create_track(self, track: dict):
"""
Creates a single track object.
"""
albumhash = track["albumhash"]
index = find_album(self.albums, albumhash)
if index is None:
return
try:
album: Album = self.albums[index]
except (TypeError):
"""
😭😭😭
"""
pass
track["image"] = album.image
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:
iterable = executor.map(self.create_track, self.tagged_tracks)
self.tracks = [t for t in iterable if t is not None]
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.
"""
album_instance.insert_many([a.__dict__ for a in self.albums])
tracks_instance.insert_many(self.tracks)
return album
+4 -4
View File
@@ -135,8 +135,8 @@ def parse_track_number(tags):
Parses the track number from an audio file.
"""
try:
track_number = tags["tracknumber"][0]
except (KeyError, IndexError):
track_number = int(tags["tracknumber"][0])
except (KeyError, IndexError, ValueError):
track_number = 1
return track_number
@@ -147,8 +147,8 @@ def parse_disk_number(tags):
Parses the disk number from an audio file.
"""
try:
disk_number = tags["disknumber"][0]
except (KeyError, IndexError):
disk_number = int(tags["disknumber"][0])
except (KeyError, IndexError, ValueError):
disk_number = 1
return disk_number
+1 -1
View File
@@ -44,7 +44,7 @@ def get_track_by_id(trackid: str) -> models.Track:
print("AttributeError")
def find_track(tracks: list, hash: str) -> int or None:
def find_track(tracks: list, hash: str) -> int | None:
"""
Finds an album by album title and artist.
"""
+9 -27
View File
@@ -6,7 +6,6 @@ import random
from typing import List
from app import helpers
from app.exceptions import TrackExistsInPlaylist
@dataclass(slots=True)
@@ -25,20 +24,14 @@ class Track:
length: int
genre: str
bitrate: int
image: str
tracknumber: int
disknumber: int
albumhash: str
date: str
image: str
def __init__(self, tags):
try:
self.trackid = tags["_id"]["$oid"]
except KeyError:
self.trackid = "".join(
random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(20)
)
print("No id")
self.trackid = tags["_id"]["$oid"]
self.title = tags["title"]
self.artists = tags["artists"].split(", ")
self.albumartist = tags["albumartist"]
@@ -50,16 +43,9 @@ class Track:
self.length = int(tags["length"])
self.disknumber = int(tags["disknumber"])
self.albumhash = tags["albumhash"]
try:
self.image = tags["image"]
except KeyError:
print(tags)
try:
self.tracknumber = int(tags["tracknumber"])
except ValueError:
self.tracknumber = 1
self.date = tags["date"]
self.image = tags["albumhash"] + ".webp"
self.tracknumber = int(tags["tracknumber"])
@dataclass(slots=True)
@@ -81,14 +67,14 @@ class Artist:
@dataclass
class Album:
"""
Album class
Creates an album object
"""
title: str
artist: str
hash: str
date: int
image: str
hash: str
count: int = 0
duration: int = 0
is_soundtrack: bool = False
@@ -100,11 +86,7 @@ class Album:
self.artist = tags["artist"]
self.date = tags["date"]
self.image = tags["image"]
try:
self.hash = tags["albumhash"]
except KeyError:
self.hash = helpers.create_album_hash(self.title, self.artist)
self.hash = tags["hash"]
@property
def is_soundtrack(self) -> bool:
+1 -1
View File
@@ -12,7 +12,7 @@ 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/Wolftyla Radio"
HOME_DIR = TEST_DIR
# HOME_DIR = TEST_DIR
# URL
IMG_BASE_URI = "http://127.0.0.1:8900/images/"
IMG_ARTIST_URI = IMG_BASE_URI + "artists/"
+1
View File
@@ -6,6 +6,7 @@ export interface Track {
album?: string;
artists: string[];
albumartist?: string;
albumhash?: string;
folder?: string;
filepath?: string;
length?: number;