Restyle Implement Fuzzy search using rapidfuzz (#60)

This commit is contained in:
restyled-io[bot] 2022-05-26 19:12:04 +03:00 committed by GitHub
parent 1a3a196d7a
commit 2b33fb87a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 90 additions and 74 deletions

View File

@ -1,11 +1,10 @@
""" """
Contains all the search routes. Contains all the search routes.
""" """
from flask import Blueprint, request
from app.lib import searchlib
from app import helpers from app import helpers
from app.lib import searchlib
from flask import Blueprint
from flask import request
search_bp = Blueprint("search", __name__, url_prefix="/") search_bp = Blueprint("search", __name__, url_prefix="/")
@ -96,9 +95,18 @@ def search():
return { return {
"data": [ "data": [
{"tracks": tracks[:5], "more": len(tracks) > 5}, {
{"albums": albums[:6], "more": len(albums) > 6}, "tracks": tracks[:5],
{"artists": artists_dicts[:6], "more": len(artists_dicts) > 6}, "more": len(tracks) > 5
},
{
"albums": albums[:6],
"more": len(albums) > 6
},
{
"artists": artists_dicts[:6],
"more": len(artists_dicts) > 6
},
] ]
} }
@ -116,18 +124,18 @@ def search_load_more():
if type == "tracks": if type == "tracks":
return { return {
"tracks": SEARCH_RESULTS["tracks"][index : index + 5], "tracks": SEARCH_RESULTS["tracks"][index:index + 5],
"more": len(SEARCH_RESULTS["tracks"]) > index + 5, "more": len(SEARCH_RESULTS["tracks"]) > index + 5,
} }
elif type == "albums": elif type == "albums":
return { return {
"albums": SEARCH_RESULTS["albums"][index : index + 6], "albums": SEARCH_RESULTS["albums"][index:index + 6],
"more": len(SEARCH_RESULTS["albums"]) > index + 6, "more": len(SEARCH_RESULTS["albums"]) > index + 6,
} }
elif type == "artists": elif type == "artists":
return { return {
"artists": SEARCH_RESULTS["artists"][index : index + 6], "artists": SEARCH_RESULTS["artists"][index:index + 6],
"more": len(SEARCH_RESULTS["artists"]) > index + 6, "more": len(SEARCH_RESULTS["artists"]) > index + 6,
} }

View File

@ -6,9 +6,11 @@ import random
import threading import threading
import time import time
from datetime import datetime from datetime import datetime
from typing import Dict, List from typing import Dict
from typing import List
from app import models, settings from app import models
from app import settings
app_dir = settings.APP_DIR app_dir = settings.APP_DIR
@ -25,7 +27,9 @@ def background(func):
return background_func return background_func
def run_fast_scandir(__dir: str, ext: list, full=False) -> Dict[List[str], List[str]]: def run_fast_scandir(__dir: str,
ext: list,
full=False) -> Dict[List[str], List[str]]:
""" """
Scans a directory for files with a specific extension. Returns a list of files and folders in the directory. Scans a directory for files with a specific extension. Returns a list of files and folders in the directory.
""" """
@ -58,12 +62,10 @@ def remove_duplicates(tracklist: List[models.Track]) -> List[models.Track]:
while song_num < len(tracklist) - 1: while song_num < len(tracklist) - 1:
for index, song in enumerate(tracklist): for index, song in enumerate(tracklist):
if ( if (tracklist[song_num].title == song.title
tracklist[song_num].title == song.title and tracklist[song_num].album == song.album
and tracklist[song_num].album == song.album and tracklist[song_num].artists == song.artists
and tracklist[song_num].artists == song.artists and index != song_num):
and index != song_num
):
tracklist.remove(song) tracklist.remove(song)
song_num += 1 song_num += 1
@ -106,7 +108,8 @@ def check_artist_image(image: str) -> str:
""" """
img_name = image.replace("/", "::") + ".webp" img_name = image.replace("/", "::") + ".webp"
if not os.path.exists(os.path.join(app_dir, "images", "artists", img_name)): if not os.path.exists(os.path.join(app_dir, "images", "artists",
img_name)):
return use_memoji() return use_memoji()
else: else:
return img_name return img_name

View File

@ -6,13 +6,13 @@ import urllib
from typing import List from typing import List
from app import api from app import api
from app import helpers
from app import instances from app import instances
from app import models from app import models
from app.lib import taglib from app.lib import taglib
from app.lib import trackslib from app.lib import trackslib
from progress.bar import Bar from progress.bar import Bar
from tqdm import tqdm from tqdm import tqdm
from app import helpers
def get_all_albums() -> List[models.Album]: def get_all_albums() -> List[models.Album]:

View File

@ -1,26 +1,26 @@
from concurrent.futures import ThreadPoolExecutor
from multiprocessing import Pool
from copy import deepcopy
import os import os
from os import path
import time import time
from concurrent.futures import ThreadPoolExecutor
from copy import deepcopy
from multiprocessing import Pool
from os import path
from typing import List from typing import List
from tqdm import tqdm
from app import api from app import api
from app import settings from app import settings
from app.helpers import create_album_hash, run_fast_scandir from app.helpers import create_album_hash
from app.helpers import run_fast_scandir
from app.instances import album_instance from app.instances import album_instance
from app.instances import tracks_instance from app.instances import tracks_instance
from app.lib import folderslib from app.lib import folderslib
from app.lib.albumslib import create_album from app.lib.albumslib import create_album
from app.lib.albumslib import find_album from app.lib.albumslib import find_album
from app.lib.taglib import get_tags from app.lib.taglib import get_tags
from app.logger import Log
from app.models import Album, Track
from app.lib.trackslib import find_track from app.lib.trackslib import find_track
from app.logger import Log
from app.models import Album
from app.models import Track
from tqdm import tqdm
class Populate: class Populate:
@ -95,7 +95,8 @@ class Populate:
folder = tags["folder"] folder = tags["folder"]
self.folders.add(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) self.tagged_tracks.append(tags)
api.DB_TRACKS.append(tags) api.DB_TRACKS.append(tags)
@ -168,9 +169,8 @@ class Populate:
for album in tqdm(self.pre_albums, desc="Building albums"): for album in tqdm(self.pre_albums, desc="Building albums"):
self.create_album(album) self.create_album(album)
Log( Log(f"{self.exist_count} of {len(self.pre_albums)} albums were already in the database"
f"{self.exist_count} of {len(self.pre_albums)} albums were already in the database" )
)
def create_track(self, track: dict): def create_track(self, track: dict):
""" """
@ -205,9 +205,8 @@ class Populate:
with ThreadPoolExecutor() as executor: with ThreadPoolExecutor() as executor:
executor.map(self.create_track, self.tagged_tracks) executor.map(self.create_track, self.tagged_tracks)
Log( Log(f"Added {len(self.tagged_tracks)} new tracks and {len(self.albums)} new albums"
f"Added {len(self.tagged_tracks)} new tracks and {len(self.albums)} new albums" )
)
def save_albums(self): def save_albums(self):
""" """

View File

@ -1,13 +1,14 @@
""" """
This library contains all the functions related to the search functionality. This library contains all the functions related to the search functionality.
""" """
from typing import List from typing import List
from app import api, helpers, models from app import api
from app import helpers
from app import models
from app.lib import albumslib from app.lib import albumslib
from rapidfuzz import fuzz, process from rapidfuzz import fuzz
from rapidfuzz import process
ratio = fuzz.ratio ratio = fuzz.ratio
wratio = fuzz.WRatio wratio = fuzz.WRatio
@ -34,6 +35,7 @@ class Limit:
class SearchTracks: class SearchTracks:
def __init__(self, query) -> None: def __init__(self, query) -> None:
self.query = query self.query = query
@ -55,6 +57,7 @@ class SearchTracks:
class SearchArtists: class SearchArtists:
def __init__(self, query) -> None: def __init__(self, query) -> None:
self.query = query self.query = query
@ -100,6 +103,7 @@ class SearchArtists:
class SearchAlbums: class SearchAlbums:
def __init__(self, query) -> None: def __init__(self, query) -> None:
self.query = query self.query = query
@ -137,6 +141,7 @@ class SearchAlbums:
class GetTopArtistTracks: class GetTopArtistTracks:
def __init__(self, artist: str) -> None: def __init__(self, artist: str) -> None:
self.artist = artist self.artist = artist
@ -160,5 +165,6 @@ def get_artists(artist: str) -> List[models.Track]:
Gets all songs with a given artist. Gets all songs with a given artist.
""" """
return [ return [
track for track in api.TRACKS if artist.lower() in str(track.artists).lower() track for track in api.TRACKS
if artist.lower() in str(track.artists).lower()
] ]

View File

@ -7,14 +7,14 @@ import time
from app import api from app import api
from app import instances from app import instances
from app import models from app import models
from app.helpers import create_album_hash
from app.lib import folderslib from app.lib import folderslib
from app.lib.albumslib import create_album, find_album from app.lib.albumslib import create_album
from app.lib.albumslib import find_album
from app.lib.taglib import get_tags from app.lib.taglib import get_tags
from watchdog.events import PatternMatchingEventHandler from watchdog.events import PatternMatchingEventHandler
from watchdog.observers import Observer from watchdog.observers import Observer
from app.helpers import create_album_hash
class OnMyWatch: class OnMyWatch:
""" """
@ -87,7 +87,8 @@ def remove_track(filepath: str) -> None:
fpath = filepath.replace(fname, "") fpath = filepath.replace(fname, "")
try: try:
trackid = instances.tracks_instance.get_song_by_path(filepath)["_id"]["$oid"] trackid = instances.tracks_instance.get_song_by_path(
filepath)["_id"]["$oid"]
except TypeError: except TypeError:
print(f"💙 Watchdog Error: Error removing track {filepath} TypeError") print(f"💙 Watchdog Error: Error removing track {filepath} TypeError")
return return

View File

@ -6,8 +6,8 @@ from dataclasses import field
from typing import List from typing import List
from app import api from app import api
from app.exceptions import TrackExistsInPlaylist
from app import helpers from app import helpers
from app.exceptions import TrackExistsInPlaylist
@dataclass(slots=True) @dataclass(slots=True)
@ -97,11 +97,9 @@ class Album:
def get_p_track(ptrack): def get_p_track(ptrack):
for track in api.TRACKS: for track in api.TRACKS:
if ( if (track.title == ptrack["title"]
track.title == ptrack["title"] and track.artists == ptrack["artists"]
and track.artists == ptrack["artists"] and ptrack["album"] == track.album):
and ptrack["album"] == track.album
):
return track return track

View File

@ -8,17 +8,15 @@ gpath=$(poetry run which gunicorn)
while getopts ':s' opt; do while getopts ':s' opt; do
case $opt in case $opt in
s) s)
echo "🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴" echo "🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴🔴"
cd "./app" cd "./app"
"$gpath" -b 0.0.0.0:9877 -w 4 --threads=2 "imgserver:app" & "$gpath" -b 0.0.0.0:9877 -w 4 --threads=2 "imgserver:app" &
cd ../ cd ../
;; ;;
\?) \?)
echo "Invalid option: -$OPTARG" >&2 echo "Invalid option: -$OPTARG" >&2
;; ;;
esac esac
done done
"$gpath" -b 0.0.0.0:9876 -w 1 --threads=4 "manage:create_app()" "$gpath" -b 0.0.0.0:9876 -w 1 --threads=4 "manage:create_app()"

View File

@ -3,7 +3,8 @@ import { getElem } from "./perks";
export default (mouseX, mouseY) => { export default (mouseX, mouseY) => {
const scope = getElem("app", "id"); const scope = getElem("app", "id");
const contextMenu = getElem("context-menu", "class"); const contextMenu = getElem("context-menu", "class");
// ? compute what is the mouse position relative to the container element (scope) // ? compute what is the mouse position relative to the container element
// (scope)
let { left: scopeOffsetX, top: scopeOffsetY } = scope.getBoundingClientRect(); let { left: scopeOffsetX, top: scopeOffsetY } = scope.getBoundingClientRect();
scopeOffsetX = scopeOffsetX < 0 ? 0 : scopeOffsetX; scopeOffsetX = scopeOffsetX < 0 ? 0 : scopeOffsetX;

View File

@ -1,12 +1,14 @@
import { createApp } from "vue";
import App from "./App.vue";
import "./registerServiceWorker"; import "./registerServiceWorker";
import router from "./router";
import { createPinia } from "pinia";
import { MotionPlugin } from "@vueuse/motion";
import useCustomTransitions from "./transitions";
import "../src/assets/css/global.scss"; import "../src/assets/css/global.scss";
import { MotionPlugin } from "@vueuse/motion";
import { createPinia } from "pinia";
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import useCustomTransitions from "./transitions";
const app = createApp(App); const app = createApp(App);
app.use(createPinia()); app.use(createPinia());