diff --git a/app/config.py b/app/config.py index 8f49559..c8ea731 100644 --- a/app/config.py +++ b/app/config.py @@ -3,6 +3,9 @@ import os class Config(object): + AUDIO_SEGMENT_CHUNK_SIZE = 10 + DBFS_FADE = -12 + DBFS_SILENCE = -50 DISPLAY_SQL = False ERRORS_TO = ['kae@midnighthax.com'] LOG_LEVEL_STDERR = logging.INFO @@ -12,9 +15,10 @@ class Config(object): MAIL_SERVER = os.environ.get('MAIL_SERVER') or "woodlands.midnighthax.com" MAIL_USERNAME = os.environ.get('MAIL_USERNAME') MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS') is not None + MILLISECOND_SIGFIGS = 1 MYSQL_CONNECT = "mysql+mysqldb://songdb:songdb@localhost/songdb" ROOT = "/home/kae/music" - TIMER_MS = 500 + TIMER_MS = 1000 config = Config diff --git a/app/musicmuster.py b/app/musicmuster.py index 327a33d..8ad67b9 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -10,7 +10,6 @@ from log import DEBUG, ERROR from PyQt5.QtCore import Qt, QTimer from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog, QListWidgetItem -from threading import Timer from ui.main_window_ui import Ui_MainWindow from ui.dlg_search_database_ui import Ui_Dialog @@ -19,34 +18,9 @@ from config import Config from model import Settings, Tracks -class RepeatedTimer: - def __init__(self, interval, function, *args, **kwargs): - self._timer = None - self.interval = interval - self.function = function - self.args = args - self.kwargs = kwargs - self.is_running = False - self.start() - - def _run(self): - self.is_running = False - self.start() - self.function(*self.args, **self.kwargs) - - def start(self): - if not self.is_running: - self._timer = Timer(self.interval, self._run) - self._timer.start() - self.is_running = True - - def stop(self): - self._timer.cancel() - self.is_running = False - - class Music: def __init__(self): + self.current_track = { "player": None, "meta": None @@ -106,6 +80,12 @@ class Music: self.current_track['player'].play() + def playing(self): + if self.current_track['player']: + return self.current_track['player'].is_playing() + else: + return False + def resume_last(self): pass @@ -220,9 +200,17 @@ class Window(QMainWindow, Ui_MainWindow): ERROR("Can't set next track") def tick(self): - self.current_time.setText( - datetime.strftime(datetime.now(), "%H:%M:%S") - ) + now = datetime.now() + self.current_time.setText(now.strftime("%H:%M:%S")) + if self.music.playing(): + playtime = self.music.get_current_playtime() + self.label_elapsed_timer.setText(ms_to_mmss(playtime)) + self.label_fade_timer.setText( + ms_to_mmss(self.music.get_current_fade_at() - playtime)) + self.label_silent_timer.setText( + ms_to_mmss(self.music.get_current_silence_at() - playtime)) + self.label_end_timer.setText( + ms_to_mmss(self.music.get_current_duration() - playtime)) def add_to_playlist(self, track): """ @@ -292,13 +280,15 @@ class DbDialog(QDialog): self.parent().add_to_playlist(track) -def ms_to_mmss(ms, decimals=0): +def ms_to_mmss(ms, decimals=0, negative=False): if not ms: return "-" + sign = "" if ms < 0: - sign = "-" - else: - sign = "" + if negative: + sign = "-" + else: + ms = 0 minutes, remainder = divmod(ms, 60 * 1000) seconds = remainder / 1000 diff --git a/app/songdb.py b/app/songdb.py index 6524d91..f79bae6 100755 --- a/app/songdb.py +++ b/app/songdb.py @@ -5,20 +5,13 @@ import hashlib import logging import os -import pytiger.logging.config - -from tinytag import TinyTag -from pydub import AudioSegment - +from config import Config +from log import INFO from model import Tracks, session +from pydub import AudioSegment +from tinytag import TinyTag - -# "Constants" -ROOT = "/home/kae/music" -# Instantiate logging -pytiger.logging.config.basic_config(stderr=False, level=logging.INFO) -log = logging.getLogger(__name__) -log.info("Starting") +INFO("Starting") def main(): @@ -49,8 +42,8 @@ def get_audio_segment(path): return None -def leading_silence(audio_segment, silence_threshold=-50.0, - chunk_size=10): +def leading_silence(audio_segment, silence_threshold=Config.DBFS_SILENCE, + chunk_size=Config.AUDIO_SEGMENT_CHUNK_SIZE): """ Returns the millisecond/index that the leading silence ends. audio_segment - the segment to find silence in @@ -71,7 +64,7 @@ def leading_silence(audio_segment, silence_threshold=-50.0, return min(trim_ms, len(audio_segment)) -def fade_point(audio_segment, fade_threshold=-20.0, chunk_size=10): +def fade_point(audio_segment, fade_threshold=Config.DBFS_FADE, chunk_size=Config.AUDIO_SEGMENT_CHUNK_SIZE): """ Returns the millisecond/index of the point where the fade is down to fade_threshold and doesn't get louder again. @@ -95,7 +88,7 @@ def fade_point(audio_segment, fade_threshold=-20.0, chunk_size=10): def trailing_silence(audio_segment, silence_threshold=-50.0, - chunk_size=10): + chunk_size=Config.AUDIO_SEGMENT_CHUNK_SIZE): return fade_point(audio_segment, silence_threshold, chunk_size) @@ -107,22 +100,25 @@ def update_db(): """ count = 0 - for root, dirs, files in os.walk(ROOT): + for root, dirs, files in os.walk(Config.ROOT): for f in files: count += 1 path = os.path.join(root, f) ext = os.path.splitext(f)[1] if ext in [".flac", ".mp3"]: - track = Tracks.get_or_create(os.path.relpath(path, ROOT)) + track = Tracks.get_or_create(path) tag = TinyTag.get(path) audio = get_audio_segment(path) track.title = tag.title track.artist = tag.artist - track.duration = int(tag.duration * 1000) + track.duration = int(round( + tag.duration, Config.MILLISECOND_SIGFIGS) * 1000) track.start_gap = leading_silence(audio) - track.fade_at = fade_point(audio) - track.silence_at = trailing_silence(audio) + track.fade_at = round( + fade_point(audio), Config.MILLISECOND_SIGFIGS) * 1000 + track.silence_at = round( + trailing_silence(audio), Config.MILLISECOND_SIGFIGS) * 1000 track.mtime = os.path.getmtime(path) session.commit() @@ -132,16 +128,5 @@ def update_db(): print(f"{count} files processed") -def md5(path): - "https://stackoverflow.nl9om/questions/3431825/" - "generating-an-md5-checksum-of-a-file" - - hash_md5 = hashlib.md5() - with open(path, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): - hash_md5.update(chunk) - return hash_md5.hexdigest() - - if __name__ == '__main__': main()