add file watcher

- disable double flask instances
- remove unused files
- play song by id (instead of from nginx)
This commit is contained in:
geoffrey45 2022-02-16 15:02:36 +03:00
parent 1802e02179
commit 1caef9d3a2
9 changed files with 60 additions and 169 deletions

View File

@ -17,6 +17,7 @@ def create_app():
app.config.from_mapping(config)
cache.init_app(app)
with app.app_context():
from . import api
app.register_blueprint(api.bp, url_prefix='/')

View File

@ -1,7 +1,7 @@
import os
import urllib
from typing import List
from flask import Blueprint, request
from flask import Blueprint, request, send_file
from app import functions, instances, helpers, cache
bp = Blueprint("api", __name__, url_prefix="")
@ -17,6 +17,7 @@ def initialize() -> None:
"""
helpers.create_config_dir()
helpers.check_for_new_songs()
helpers.start_watchdog()
initialize()
@ -283,3 +284,14 @@ def get_album_bio(title, artist):
"""Returns the album bio for the given album."""
bio = functions.get_album_bio(title, artist)
return {"bio": bio}, 200
@bp.route("/file/<track_id>")
def send_track_file(track_id):
"""
Returns an audio file that matches the passed id to the client.
"""
filepath = instances.songs_instance.get_song_by_id(track_id)['filepath']
return send_file(filepath, mimetype="audio/mp3")

View File

@ -32,6 +32,7 @@ def populate():
"""
start = time.time()
print("\nchecking for new tracks")
files = helpers.run_fast_scandir(helpers.home_dir, [".flac", ".mp3"])[1]
for file in files:
@ -41,6 +42,7 @@ def populate():
instances.songs_instance.insert_song(tags)
api.all_the_f_music = helpers.get_all_songs()
print("\n check done")
end = time.time()
@ -251,18 +253,18 @@ def parse_disk_number(audio):
return disk_number
def get_tags(full_path: str) -> dict:
def get_tags(fullpath: str) -> dict:
"""
Returns a dictionary of tags for a given file.
"""
try:
audio = mutagen.File(full_path, easy=True)
audio = mutagen.File(fullpath, easy=True)
except MutagenError:
return None
tags = {
"artists": parse_artist_tag(audio),
"title": parse_title_tag(audio, full_path),
"title": parse_title_tag(audio, fullpath),
"albumartist": parse_album_artist_tag(audio),
"album": parse_album_tag(audio),
"genre": parse_genre_tag(audio),
@ -271,9 +273,9 @@ def get_tags(full_path: str) -> dict:
"discnumber": parse_disk_number(audio),
"length": round(audio.info.length),
"bitrate": round(int(audio.info.bitrate) / 1000),
"filepath": full_path.replace(helpers.home_dir, ""),
"image": extract_thumb(full_path),
"folder": os.path.dirname(full_path).replace(helpers.home_dir, ""),
"filepath": fullpath,
"image": extract_thumb(fullpath),
"folder": os.path.dirname(fullpath).replace(helpers.home_dir, ""),
}
return tags
@ -314,7 +316,6 @@ def create_track_class(tags):
tags["artists"],
tags["albumartist"],
tags["album"],
tags["filepath"],
tags["folder"],
tags["length"],
tags["date"],

View File

@ -9,24 +9,26 @@ from typing import List
import requests
from io import BytesIO
from PIL import Image
from app import instances
from app import functions
from app import watchdoge
home_dir = os.path.expanduser('~') + '/'
app_dir = os.path.join(home_dir, '.musicx')
LAST_FM_API_KEY = "762db7a44a9e6fb5585661f5f2bdf23a"
def background(f):
def background(func):
"""
a threading decorator
use @background above the function you want to run in the background
"""
def background_func(*a, **kw):
threading.Thread(target=f, args=a, kwargs=kw).start()
threading.Thread(target=func, args=a, kwargs=kw).start()
return background_func
@ -44,6 +46,11 @@ def check_for_new_songs():
time.sleep(300)
@background
def start_watchdog():
watchdoge.watch.run()
def run_fast_scandir(_dir: str, ext: list):
"""
Scans a directory for files with a specific extension. Returns a list of files and folders in the directory.
@ -89,7 +96,7 @@ def remove_duplicates(array: list) -> list:
def save_image(url: str, path: str) -> None:
"""
Saves an image from a url to a path.
Saves an image from an url to a path.
"""
response = requests.get(url)
@ -138,14 +145,11 @@ def get_all_songs() -> List:
tracks = []
for track in instances.songs_instance.get_all_songs():
track = functions.create_track_class(track)
try:
os.chmod(os.path.join(home_dir, track.filepath), 0o755)
os.chmod(os.path.join(track["filepath"]), 0o755)
except FileNotFoundError:
print("")
instances.songs_instance.remove_song_by_filepath(track.filepath)
instances.songs_instance.remove_song_by_filepath(track['filepath'])
tracks.append(track)
tracks.append(functions.create_track_class(track))
return tracks

View File

@ -202,7 +202,6 @@ class Track:
artists: str
albumartist: str
album: str
filepath: str
folder: str
length: int
date: int

View File

@ -4,9 +4,8 @@ import os
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
class OnMyWatch:
watchDirectory = "/home/cwilvx/Music"
directory = "/home/cwilvx/Music"
def __init__(self):
self.observer = Observer()
@ -14,8 +13,10 @@ class OnMyWatch:
def run(self):
event_handler = Handler()
self.observer.schedule(
event_handler, self.watchDirectory, recursive=True)
event_handler, self.directory, recursive=True
)
self.observer.start()
try:
while True:
time.sleep(5)
@ -38,21 +39,26 @@ def create_thumb_dir(filepath):
class Handler(PatternMatchingEventHandler):
def __init__(self):
print("💠 started watchxx")
PatternMatchingEventHandler.__init__(
self, patterns=['*.flac', '*.mp3'], ignore_directories=True, case_sensitive=False)
def on_created(self, event):
print("🔵 created +++")
print(event.src_path)
create_thumb_dir(event.src_path)
def on_deleted(self, event):
print("🔴 deleted ---")
print(event.src_path)
def on_moved(self, event):
print("🔘 moved -->")
print(event.src_path)
print(event.dest_path)
if __name__ == '__main__':
watch = OnMyWatch()
watch.run()
# if __name__ == '__main__':
watch = OnMyWatch()
# watch.run()

View File

@ -1,6 +1,5 @@
from app import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True, threaded=True, host="0.0.0.0", port=9876)
app.run(debug=True, threaded=True, host="0.0.0.0", port=9876, use_reloader=False)

View File

@ -1,5 +1,6 @@
# /home/cwilvx/.local/share/virtualenvs/server-PQNgo_Nv/bin/
/home/cwilvx/.cache/pypoetry/virtualenvs/musicx_server-jsG71GtA-py3.8/bin/python manage.py
python manage.py
#python manage.py
# gunicorn -b 0.0.0.0:9876 --workers=4 "wsgi:create_app()" --log-level=debug

View File

@ -1,132 +0,0 @@
import os, sys, logging, time
from io import BytesIO
from pathlib import Path
import PIL
from watchdog import observers
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler, FileSystemEventHandler
from mutagen.mp3 import MP3, MutagenError
from mutagen.id3 import ID3
from mutagen.flac import FLAC
from PIL import Image
music_dir = "/home/cwilvx/Music/"
folders = os.listdir(music_dir)
def updateThumbnails():
start_time = time.time()
print("Updating thumbnails ...")
for folder in folders:
print(folder)
try:
dir = music_dir + folder
thumbnail_folder = dir + "/"+ ".thumbnails"
if not os.path.exists(thumbnail_folder):
os.makedirs(thumbnail_folder)
def thumbnail_extractor(type, song):
if type == "mp3":
tags = ID3(song)
image_path = "{}/.thumbnails/{}".format(dir, song.name.replace(".mp3", ".jpg"))
album_art = tags.getall('APIC')[0].data
elif type == "flac":
tags = FLAC(song)
image_path = "{}/.thumbnails/{}".format(dir, song.name.replace(".flac", ".jpg"))
album_art = tags.pictures[0].data
else:
print("Unsupported file type")
return
image = Image.open(BytesIO(album_art))
if not os.path.exists(image_path):
try:
image.save(image_path, 'JPEG')
except OSError:
image.convert('RGB').save(image_path, 'JPEG')
for song in Path(dir).rglob('*.mp3'):
try:
thumbnail_extractor("mp3", song)
except (MutagenError, IndexError):
pass
for song in Path(dir).rglob('*.flac'):
try:
thumbnail_extractor("flac", song)
except (MutagenError, IndexError):
pass
except NotADirectoryError:
pass
print("done")
print("Done in: %s seconds" % round((time.time() - start_time), 1))
class watchMusicDirs(FileSystemEventHandler):
def __init__(self, logger=None):
super().__init__()
self.logger = logger or logging.root
# def on_moved(self, event):
# super().on_moved(event)
# what = 'directory' if event.is_directory else 'file'
# self.logger.info("Moved %s: from %s to %s", what, event.src_path,
# event.dest_path)
# def on_created(self, event):
# super().on_created(event)
# what = 'directory' if event.is_directory else 'file'
# self.logger.info("Created %s: %s", what, event.src_path)
# def on_deleted(self, event):
# super().on_deleted(event)
# what = 'directory' if event.is_directory else 'file'
# self.logger.info("Deleted %s: %s", what, event.src_path)
def on_modified(self, event):
super().on_modified(event)
what = 'directory' if event.is_directory else 'file'
# self.logger.info("Modified %s: %s", what, event.src_path)
print("Modified %s: %s" % (what, event.src_path))
updateThumbnails()
paths = [music_dir, '/home/cwilvx/watched']
if __name__ == "__main__":
observer = Observer()
event_handler = watchMusicDirs()
observers = []
for path in paths:
observer.schedule(event_handler, path, recursive=True)
observers.append(observer)
observer.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
for observer in observers:
observer.unschedule_all()
observer.stop()
for observer in observers:
observer.join()