mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-12 22:17:35 +00:00
Restyle Implement Fuzzy search using rapidfuzz (#60)
This commit is contained in:
parent
1a3a196d7a
commit
2b33fb87a2
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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]:
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -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()
|
||||||
]
|
]
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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()"
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-right: $small;
|
padding-right: $small;
|
||||||
|
|
||||||
@include phone-only {
|
@include phone-only {
|
||||||
grid-template-columns: 1fr 9.2rem;
|
grid-template-columns: 1fr 9.2rem;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
<div
|
<div
|
||||||
class="h"
|
class="h"
|
||||||
v-if="props.album.artist.toLowerCase() == 'various artists'"
|
v-if="props.album.artist.toLowerCase() == 'various artists'"
|
||||||
|
|
||||||
>
|
>
|
||||||
Compilation
|
Compilation
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
class="nav-button"
|
class="nav-button"
|
||||||
id="home-button"
|
id="home-button"
|
||||||
v-motion-slide-from-left-100
|
v-motion-slide-from-left-100
|
||||||
|
|
||||||
>
|
>
|
||||||
<div class="in">
|
<div class="in">
|
||||||
<div class="nav-icon image" :id="`${menu.name}-icon`"></div>
|
<div class="nav-icon image" :id="`${menu.name}-icon`"></div>
|
||||||
|
@ -43,7 +43,7 @@ function updateQueue(track: Track) {
|
|||||||
padding: $small;
|
padding: $small;
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.list-enter-active,
|
.list-enter-active,
|
||||||
.list-leave-active {
|
.list-leave-active {
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
|
@ -64,7 +64,7 @@ export default function (queue: any) {
|
|||||||
if (!key_down_fired) {
|
if (!key_down_fired) {
|
||||||
if (!ctrlKey) return;
|
if (!ctrlKey) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
|
||||||
key_down_fired = true;
|
key_down_fired = true;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
14
src/main.js
14
src/main.js
@ -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());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user