diff --git a/server/app/api.py b/server/app/api.py index 50be629..0795b00 100644 --- a/server/app/api.py +++ b/server/app/api.py @@ -1,60 +1,25 @@ -from app.models import Artists - -from app.helpers import ( - all_songs_instance, - getTags, - remove_duplicates, - save_image, - create_config_dir, - extract_thumb, - run_fast_scandir, - convert_one_to_json, - convert_to_json, - home_dir, app_dir, -) - -from app import cache - import os -import requests import urllib - -from progress.bar import Bar -from mutagen.flac import MutagenError - -from flask import Blueprint, request, send_from_directory - +from flask import Blueprint, request +from app import functions, instances, helpers, cache bp = Blueprint('api', __name__, url_prefix='') -artist_instance = Artists() -img_path = "http://127.0.0.1:8900/images/thumbnails/" - - all_the_f_music = [] +home_dir = helpers.home_dir -def getAllSongs(): - all_the_f_music.clear() - all_the_f_music.extend(all_songs_instance.get_all_songs()) +all_the_f_music = helpers.getAllSongs() +def initialize() -> None: + helpers.create_config_dir() + helpers.check_for_new_songs() -def main_whatever(): - create_config_dir() - # populate() - getAllSongs() - +initialize() @bp.route('/') def adutsfsd(): - for song in all_the_f_music: - print(os.path.join(home_dir, song['filepath'])) - os.chmod(os.path.join(home_dir, song['filepath']), 0o755) - - return "Done" - - -main_whatever() + return "^ _ ^" @bp.route('/search') @@ -67,9 +32,9 @@ def search_by_title(): albums = [] artists = [] - s = all_songs_instance.find_song_by_title(query) - al = all_songs_instance.search_songs_by_album(query) - ar = all_songs_instance.search_songs_by_artist(query) + s = instances.songs_instance.find_song_by_title(query) + al = instances.songs_instance.search_songs_by_album(query) + ar = instances.songs_instance.search_songs_by_artist(query) for song in al: album_obj = { @@ -82,7 +47,7 @@ def search_by_title(): for album in albums: # try: - # image = convert_one_to_json(all_songs_instance.get_song_by_album(album['name'], album['artists']))['image'] + # image = convert_one_to_json(instances.songs_instance.get_song_by_album(album['name'], album['artists']))['image'] # except: # image: None @@ -101,52 +66,21 @@ def search_by_title(): if artist_obj not in artists: artists.append(artist_obj) - return {'songs': remove_duplicates(s), 'albums': albums, 'artists': artists} + return {'songs': helpers.remove_duplicates(s), 'albums': albums, 'artists': artists} @bp.route('/populate') -def populate(): - ''' - Populate the database with all songs in the music directory - - checks if the song is in the database, if not, it adds it - also checks if the album art exists in the image path, if not tries to - extract it. - ''' - files = run_fast_scandir(home_dir, [".flac", ".mp3"])[1] - - bar = Bar('Processing', max=len(files)) - - for file in files: - file_in_db_obj = all_songs_instance.find_song_by_path(file) - - try: - image = file_in_db_obj['image'] - - if not os.path.exists(os.path.join(app_dir, 'images', 'thumbnails', image)): - extract_thumb(file) - except: - image = None - - if image is None: - try: - getTags(file) - except MutagenError: - pass - - bar.next() - - bar.finish() - - return {'msg': 'updated everything'} +def x(): + functions.populate() + return "🎸" @bp.route("/folder/artists") def get_folder_artists(): dir = request.args.get('dir') - songs = all_songs_instance.find_songs_by_folder(dir) - without_duplicates = remove_duplicates(songs) + songs = instances.songs_instance.find_songs_by_folder(dir) + without_duplicates = helpers.remove_duplicates(songs) artists = [] @@ -161,7 +95,7 @@ def get_folder_artists(): final_artists = [] for artist in artists[:15]: - artist_obj = artist_instance.find_artists_by_name(artist) + artist_obj = instances.artist_instance.find_artists_by_name(artist) if artist_obj != []: final_artists.append(artist_obj) @@ -171,51 +105,7 @@ def get_folder_artists(): @bp.route("/populate/images") def populate_images(): - all_songs = all_songs_instance.get_all_songs() - - artists = [] - - for song in all_songs: - this_artists = song['artists'].split(', ') - - for artist in this_artists: - if artist not in artists: - artists.append(artist) - - bar = Bar('Processing images', max=len(artists)) - for artist in artists: - file_path = app_dir + '/images/artists/' + artist + '.jpg' - - if not os.path.exists(file_path): - url = 'https://api.deezer.com/search/artist?q={}'.format(artist) - response = requests.get(url) - data = response.json() - - try: - image_path = data['data'][0]['picture_xl'] - except: - image_path = None - - if image_path is not None: - try: - save_image(image_path, file_path) - artist_obj = { - 'name': artist - } - - artist_instance.insert_artist(artist_obj) - except: - pass - else: - pass - - bar.next() - - bar.finish() - - artists_in_db = artist_instance.get_all_artists() - - return {'sample': artists_in_db[:25]} + functions.populate_images() @bp.route("/artist/") @@ -223,21 +113,21 @@ def populate_images(): def getArtistData(artist: str): print(artist) artist = urllib.parse.unquote(artist) - artist_obj = artist_instance.get_artists_by_name(artist) + artist_obj = instances.artist_instance.get_artists_by_name(artist) def getArtistSongs(): - songs = all_songs_instance.find_songs_by_artist(artist) + songs = instances.songs_instance.find_songs_by_artist(artist) return songs artist_songs = getArtistSongs() - songs = remove_duplicates(artist_songs) + songs = helpers.remove_duplicates(artist_songs) def getArtistAlbums(): artist_albums = [] albums_with_count = [] - albums = all_songs_instance.find_songs_by_album_artist(artist) + albums = instances.songs_instance.find_songs_by_album_artist(artist) for song in songs: song['artists'] = song['artists'].split(', ') @@ -282,7 +172,8 @@ def getFolderTree(folder: str = None): for entry in dir_content: if entry.is_dir() and not entry.name.startswith('.'): - files_in_dir = run_fast_scandir(entry.path, [".flac", ".mp3"])[1] + files_in_dir = helpers.run_fast_scandir( + entry.path, [".flac", ".mp3"])[1] if len(files_in_dir) != 0: dir = { @@ -295,12 +186,12 @@ def getFolderTree(folder: str = None): # if entry.is_file(): # if isValidFile(entry.name) == True: - # file = all_songs_instance.find_song_by_path(entry.path) + # file = instances.songs_instance.find_song_by_path(entry.path) # if not file: # getTags(entry.path) - # songs_array = all_songs_instance.find_songs_by_folder( + # songs_array = instances.songs_instance.find_songs_by_folder( # req_dir) songs = [] @@ -311,19 +202,16 @@ def getFolderTree(folder: str = None): for song in songs: try: - song['artists'] = song['artists'].split(', ') or None + song['artists'] = song['artists'].split(', ') except: pass - if song['image'] is not None: - print(song['image']) - song['image'] = img_path + song['image'] - return {"files": remove_duplicates(songs), "folders": sorted(folders, key=lambda i: i['name'])} + return {"files": helpers.remove_duplicates(songs), "folders": sorted(folders, key=lambda i: i['name'])} @bp.route('/qwerty') def populateArtists(): - all_songs = all_songs_instance.get_all_songs() + all_songs = instances.songs_instance.get_all_songs() artists = [] @@ -338,14 +226,14 @@ def populateArtists(): if a_obj not in artists: artists.append(a_obj) - artist_instance.insert_artist(a_obj) + instances.artist_instance.insert_artist(a_obj) return {'songs': artists} @bp.route('/albums') def getAlbums(): - s = all_songs_instance.get_all_songs() + s = instances.songs_instance.get_all_songs() albums = [] @@ -366,13 +254,13 @@ def getAlbumSongs(query: str): album = query.split('::')[0].replace('|', '/') artist = query.split('::')[1].replace('|', '/') - songs = all_songs_instance.find_songs_by_album(album, artist) + songs = instances.songs_instance.find_songs_by_album(album, artist) print(artist) for song in songs: song['artists'] = song['artists'].split(', ') - song['image'] = img_path + song['image'] + song['image'] = "http://127.0.0.1:8900/images/thumbnails/" + song['image'] album_obj = { "name": album, diff --git a/server/app/configs.py b/server/app/configs.py deleted file mode 100644 index 8e8aeda..0000000 --- a/server/app/configs.py +++ /dev/null @@ -1,6 +0,0 @@ -default_configs = { - "dirs": [ - "/home/cwilvx/Music/", - "/home/cwilvx/FreezerMusic" - ] -} diff --git a/server/app/functions.py b/server/app/functions.py new file mode 100644 index 0000000..68849c4 --- /dev/null +++ b/server/app/functions.py @@ -0,0 +1,94 @@ +""" +This module contains larger functions for the server +""" + +from progress.bar import Bar +import requests +import os +from mutagen.flac import MutagenError +from app import helpers +from app import instances + + +def populate(): + ''' + Populate the database with all songs in the music directory + + checks if the song is in the database, if not, it adds it + also checks if the album art exists in the image path, if not tries to + extract it. + ''' + files = helpers.run_fast_scandir(helpers.home_dir, [".flac", ".mp3"])[1] + + bar = Bar('Indexing files', max=len(files)) + + for file in files: + file_in_db_obj = instances.songs_instance.find_song_by_path(file) + + try: + image = file_in_db_obj['image'] + + if not os.path.exists(os.path.join(helpers.app_dir, 'images', 'thumbnails', image)): + helpers.extract_thumb(file) + except: + image = None + + if image is None: + try: + helpers.getTags(file) + except MutagenError: + pass + + bar.next() + + bar.finish() + + return {'msg': 'updated everything'} + + +def populate_images(): + all_songs = instances.songs_instance.get_all_songs() + + artists = [] + + for song in all_songs: + this_artists = song['artists'].split(', ') + + for artist in this_artists: + if artist not in artists: + artists.append(artist) + + bar = Bar('Processing images', max=len(artists)) + for artist in artists: + file_path = helpers.app_dir + '/images/artists/' + artist + '.jpg' + + if not os.path.exists(file_path): + url = 'https://api.deezer.com/search/artist?q={}'.format(artist) + response = requests.get(url) + data = response.json() + + try: + image_path = data['data'][0]['picture_xl'] + except: + image_path = None + + if image_path is not None: + try: + helpers.save_image(image_path, file_path) + artist_obj = { + 'name': artist + } + + instances.artist_instance.insert_artist(artist_obj) + except: + pass + else: + pass + + bar.next() + + bar.finish() + + artists_in_db = instances.artist_instance.get_all_artists() + + return {'sample': artists_in_db[:25]} diff --git a/server/app/helpers.py b/server/app/helpers.py index bffe70e..8006d75 100644 --- a/server/app/helpers.py +++ b/server/app/helpers.py @@ -1,32 +1,49 @@ -from genericpath import exists +""" +This module contains mimi functions for the server. +""" + import os -import json +import threading +import time import requests -import urllib from mutagen.mp3 import MP3 from mutagen.id3 import ID3 from mutagen.flac import FLAC -from bson import json_util - from io import BytesIO from PIL import Image -from app.models import AllSongs -from app.configs import default_configs - -all_songs_instance = AllSongs() -music_dir = os.environ.get("music_dir") -music_dirs = os.environ.get("music_dirs") +from app import instances +from app import functions home_dir = os.path.expanduser('~') + "/" app_dir = home_dir + '/.musicx' -PORT = os.environ.get("PORT") + +def background(f): + ''' + a threading decorator + use @background above the function you want to run in the background + ''' + def backgrnd_func(*a, **kw): + threading.Thread(target=f, args=a, kwargs=kw).start() + return backgrnd_func + +@background +def check_for_new_songs(): + flag = False + + while flag is False: + functions.populate() + time.sleep(300) -def run_fast_scandir(dir, ext): +def run_fast_scandir(dir: str, ext: str) -> list: + """ + Scans a directory for files with a specific extension. Returns a list of files and folders in the directory. + """ + subfolders = [] files = [] @@ -45,7 +62,11 @@ def run_fast_scandir(dir, ext): return subfolders, files -def extract_thumb(path): +def extract_thumb(path: str) -> str: + """ + Extracts the thumbnail from an audio file. Returns the path to the thumbnail. + """ + webp_path = path.split('/')[-1] + '.webp' img_path = app_dir + "/images/thumbnails/" + webp_path @@ -88,7 +109,11 @@ def extract_thumb(path): return webp_path -def getTags(full_path): +def getTags(full_path: str) -> dict: + """ + Returns a dictionary of tags for a given file. + """ + if full_path.endswith('.flac'): try: audio = FLAC(full_path) @@ -169,120 +194,56 @@ def getTags(full_path): } } - all_songs_instance.insert_song(tags) + instances.songs_instance.insert_song(tags) return tags -def convert_one_to_json(song): - json_song = json.dumps(song, default=json_util.default) - loaded_song = json.loads(json_song) +def remove_duplicates(array: list) -> list: + """ + Removes duplicates from a list. Returns a list without duplicates. + """ - return loaded_song - - -def convert_to_json(array): - songs = [] - - for song in array: - json_song = json.dumps(song, default=json_util.default) - loaded_song = json.loads(json_song) - - songs.append(loaded_song) - - return songs - - -def get_folders(): - folders = [] - - for dir in default_configs['dirs']: - entry = os.scandir(dir) - folders.append(entry) - - -def remove_duplicates(array): song_num = 0 - while song_num < len(array) -1: + while song_num < len(array) - 1: for index, song in enumerate(array): try: if array[song_num]["title"] == song["title"] and array[song_num]["album"] == song["album"] and array[song_num]["artists"] == song["artists"] and index != song_num: array.remove(song) except: - print('whe') + print('whe') song_num += 1 return array -def save_image(url, path): +def save_image(url: str, path: str) -> None: + """ + Saves an image from a url to a path. + """ + response = requests.get(url) img = Image.open(BytesIO(response.content)) img.save(path, 'JPEG') -def isValidFile(filename): +def isValidFile(filename: str) -> bool: + """ + Checks if a file is valid. Returns True if it is, False if it isn't. + """ + if filename.endswith('.flac') or filename.endswith('.mp3'): return True else: return False -def isValidAudioFrom(folder): - folder_content = os.scandir(folder) - files = [] +def create_config_dir() -> None: + """ + Creates the config directory if it doesn't exist. + """ - for entry in folder_content: - if isValidFile(entry.name) == True: - file = { - "path": entry.path, - "name": entry.name - } - - files.append(file) - - return files - - -def getFolderContents(filepath, folder): - - folder_name = urllib.parse.unquote(folder) - - path = filepath - name = filepath.split('/')[-1] - tags = {} - - if name.endswith('.flac'): - image_path = folder_name + '/.thumbnails/' + \ - name.replace('.flac', '.jpg') - audio = FLAC(path) - - if name.endswith('.mp3'): - image_path = folder_name + '/.thumbnails/' + \ - name.replace('.mp3', '.jpg') - audio = MP3(path) - - abslt_path = urllib.parse.quote(path.replace(music_dir, '')) - - if os.path.exists(image_path): - img_url = 'http://localhost:{}/{}'.format( - PORT, - urllib.parse.quote(image_path.replace(music_dir, '')) - ) - - try: - audio_url = 'http://localhost:{}/{}'.format( - PORT, abslt_path - ) - tags = getTags(audio_url, audio, img_url, folder_name) - except: - pass - - return tags - - -def create_config_dir(): home_dir = os.path.expanduser('~') config_folder = home_dir + app_dir @@ -291,3 +252,24 @@ def create_config_dir(): for dir in dirs: if not os.path.exists(config_folder + dir): os.makedirs(config_folder + dir) + + +def getAllSongs() -> None: + """ + Gets all songs under the ~/ directory. + """ + + tracks = [] + tracks.extend(instances.songs_instance.get_all_songs()) + + for track in tracks: + try: + os.chmod(os.path.join(home_dir, track['filepath']), 0o755) + except FileNotFoundError: + instances.songs_instance.remove_song_by_filepath( + os.path.join(home_dir, track['filepath'])) + if track['image'] is not None: + track['image'] = "http://127.0.0.1:8900/images/thumbnails/" + \ + track['image'] + + return tracks diff --git a/server/app/instances.py b/server/app/instances.py new file mode 100644 index 0000000..325819e --- /dev/null +++ b/server/app/instances.py @@ -0,0 +1,5 @@ +from app.models import AllSongs +from app.models import Artists + +songs_instance = AllSongs() +artist_instance = Artists() \ No newline at end of file diff --git a/server/start.sh b/server/start.sh index ef3b7be..da9ab28 100755 --- a/server/start.sh +++ b/server/start.sh @@ -1,14 +1,3 @@ -export PORT=8000 -export music_dir="/home/cwilvx/Music/" - -# export FLASK_APP=app -# export FLASK_DEBUG=1 -# export FLASK_RUN_PORT=8008 - -# export music_dirs="['/home/cwilvx/Music/', '/home/cwilvx/FreezerMusic']" - -# flask run - python manage.py # gunicorn -b 0.0.0.0:9876 --workers=4 "wsgi:create_app()" --log-level=debug \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index e64d427..c3c3682 100644 --- a/src/App.vue +++ b/src/App.vue @@ -19,8 +19,6 @@ @collapseSearch="collapseSearch" /> - -
diff --git a/src/components/FolderView/Header.vue b/src/components/FolderView/Header.vue index d01f064..c95e58d 100644 --- a/src/components/FolderView/Header.vue +++ b/src/components/FolderView/Header.vue @@ -1,7 +1,7 @@