Separate out model
This commit is contained in:
parent
dab05c079a
commit
a6b089a5ae
99
app.py
99
app.py
@ -1,17 +1,12 @@
|
|||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import vlc
|
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from pydub import AudioSegment
|
from pydub import AudioSegment
|
||||||
from PyQt5.QtCore import QEvent, Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow, QMessageBox
|
from PyQt5.QtWidgets import QApplication, QDialog, QMainWindow
|
||||||
from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog, QListWidgetItem
|
from PyQt5.QtWidgets import QTableWidgetItem, QFileDialog, QListWidgetItem
|
||||||
from PyQt5.uic import loadUi
|
|
||||||
from threading import Timer
|
from threading import Timer
|
||||||
from time import sleep
|
|
||||||
from timeloop import Timeloop
|
|
||||||
from tinytag import TinyTag
|
from tinytag import TinyTag
|
||||||
|
|
||||||
from main_window_ui import Ui_MainWindow
|
from main_window_ui import Ui_MainWindow
|
||||||
@ -19,11 +14,6 @@ from dlg_search_database_ui import Ui_Dialog
|
|||||||
|
|
||||||
from songdb import Tracks
|
from songdb import Tracks
|
||||||
|
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
|
|
||||||
|
|
||||||
class RepeatedTimer:
|
class RepeatedTimer:
|
||||||
def __init__(self, interval, function, *args, **kwargs):
|
def __init__(self, interval, function, *args, **kwargs):
|
||||||
@ -81,7 +71,7 @@ class Track:
|
|||||||
return AudioSegment.from_mp3(path)
|
return AudioSegment.from_mp3(path)
|
||||||
elif path.endswith('.flac'):
|
elif path.endswith('.flac'):
|
||||||
return AudioSegment.from_file(path, "flac")
|
return AudioSegment.from_file(path, "flac")
|
||||||
except:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def leading_silence(self, audio_segment, silence_threshold=-50.0,
|
def leading_silence(self, audio_segment, silence_threshold=-50.0,
|
||||||
@ -232,92 +222,11 @@ class DbDialog(QDialog):
|
|||||||
self.ui.listWidget.addItem(t)
|
self.ui.listWidget.addItem(t)
|
||||||
|
|
||||||
def listdclick(self, entry):
|
def listdclick(self, entry):
|
||||||
import ipdb; ipdb.set_trace()
|
|
||||||
print(f"clicked entry id={entry.data(Qt.UserRole)}")
|
print(f"clicked entry id={entry.data(Qt.UserRole)}")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
win = Window()
|
win = Window()
|
||||||
win.show()
|
win.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|
||||||
|
|
||||||
# tl = Timeloop()
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # @tl.job(interval=timedelta(seconds=1))
|
|
||||||
# def update_progress(player, talk_at, silent_at):
|
|
||||||
# elapsed_time = player.get_time()
|
|
||||||
# total_time = player.get_length()
|
|
||||||
# remaining_time = total_time - elapsed_time
|
|
||||||
# talk_time = remaining_time - (total_time - talk_at)
|
|
||||||
# silent_time = remaining_time - (total_time - silent_at)
|
|
||||||
# end_time = (datetime.now() + timedelta(
|
|
||||||
# milliseconds=remaining_time)).strftime("%H:%M:%S")
|
|
||||||
# print(
|
|
||||||
# f"\t{ms_to_mmss(elapsed_time)}/"
|
|
||||||
# f"{ms_to_mmss(total_time)}\t\t"
|
|
||||||
# f"Talk in: {ms_to_mmss(talk_time)} "
|
|
||||||
# f"Silent in: {ms_to_mmss(silent_time)} "
|
|
||||||
# f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]"
|
|
||||||
# , end="\r")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # Print name of current song, print name of next song. Play current when
|
|
||||||
# # return pressed, Pri--current-song-output-lengthnt remaining time every
|
|
||||||
# # second. When it ends, print name of new current and next song.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# def test():
|
|
||||||
# track = "wibg.mp3"
|
|
||||||
# segment = AudioSegment.from_mp3(trm.ack)
|
|
||||||
# print(f"Track: {track}")
|
|
||||||
# print(f"Leading silence: {ms_to_mmss(leading_silence(segment),
|
|
||||||
# decimals=1)}")
|
|
||||||
# talk_at = significant_fade(segment)
|
|
||||||
# silent_at = trailing_silence(segment)
|
|
||||||
# print(f"Talkover fade: {ms_to_mmss(talk_at)}")
|
|
||||||
# print(f"Track silent from: {ms_to_mmss(silent_at)}")
|
|
||||||
# p = vlc.MediaPlayer("wibg.mp3")
|
|
||||||
# _ = input("")
|
|
||||||
# p.play()
|
|
||||||
# print()
|
|
||||||
# rt = RepeatedTimer(0.5, update_progress, p, talk_at, silent_at)
|
|
||||||
# sleep(1)
|
|
||||||
# while p.is_playing():
|
|
||||||
# sleep(1)
|
|
||||||
# rt.stop() # better in a try/finally block to make sure the program ends!
|
|
||||||
# print("End")
|
|
||||||
|
|
||||||
#def kae2(self, index):
|
|
||||||
# print(f"table header click, index={index}")
|
|
||||||
|
|
||||||
#def kae(self, a, b, c):
|
|
||||||
# self.data.append(f"a={a}, b={b}, c={c}")
|
|
||||||
|
|
||||||
#def mousePressEvent(self, QMouseEvent):
|
|
||||||
# print("mouse press")
|
|
||||||
|
|
||||||
#def mouseReleaseEvent(self, QMouseEvent):
|
|
||||||
# print("mouse release")
|
|
||||||
# # QMessageBox.about(
|
|
||||||
# # self,
|
|
||||||
# # "About Sample Editor",
|
|
||||||
# # "\n".join(self.data)
|
|
||||||
# # )
|
|
||||||
#def eventFilter(self, obj, event):
|
|
||||||
# # you could be doing different groups of actions
|
|
||||||
# # for different types of widgets and either filtering
|
|
||||||
# # the event or not.
|
|
||||||
# # Here we just check if its one of the layout widgets
|
|
||||||
# # if self.layout.indexOf(obj) != -1:
|
|
||||||
# # print(f"event received: {event.type()}")
|
|
||||||
# if event.type() == QEvent.MouseButtonPress:
|
|
||||||
# print("Widget click")
|
|
||||||
# # if I returned True right here, the event
|
|
||||||
# # would be filtered and not reach the obj,
|
|
||||||
# # meaning that I decided to handle it myself
|
|
||||||
|
|
||||||
# # regardless, just do the default
|
|
||||||
# return super().eventFilter(obj, event)
|
|
||||||
|
|||||||
70
model.py
Normal file
70
model.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import sqlalchemy
|
||||||
|
|
||||||
|
import pytiger.logging.config
|
||||||
|
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from sqlalchemy import Column, Float, DateTime, Integer, String
|
||||||
|
from sqlalchemy.orm.exc import NoResultFound
|
||||||
|
from sqlalchemy.orm import sessionmaker
|
||||||
|
|
||||||
|
# "Constants"
|
||||||
|
DISPLAY_SQL = False
|
||||||
|
MYSQL_CONNECT = "mysql+mysqldb://songdb:songdb@localhost/songdb"
|
||||||
|
|
||||||
|
# Instantiate logging
|
||||||
|
pytiger.logging.config.basic_config(stderr=False, level=logging.INFO)
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
log.info("Starting")
|
||||||
|
|
||||||
|
# Create session at the global level as per
|
||||||
|
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html
|
||||||
|
|
||||||
|
# Set up database connection
|
||||||
|
log.info("Connect to database")
|
||||||
|
engine = sqlalchemy.create_engine(f"{MYSQL_CONNECT}?charset=utf8",
|
||||||
|
encoding='utf-8',
|
||||||
|
echo=DISPLAY_SQL)
|
||||||
|
Base = declarative_base()
|
||||||
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
|
# Create a Session
|
||||||
|
Session = sessionmaker(bind=engine)
|
||||||
|
session = Session()
|
||||||
|
|
||||||
|
|
||||||
|
# Database classes
|
||||||
|
class Tracks(Base):
|
||||||
|
__tablename__ = 'tracks'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
||||||
|
title = Column(String(256), index=True)
|
||||||
|
artist = Column(String(256), index=True)
|
||||||
|
length = Column(Integer, index=True)
|
||||||
|
path = Column(String(2048), index=False, nullable=False)
|
||||||
|
mtime = Column(Float, index=True)
|
||||||
|
lastplayed = Column(DateTime, index=True, default=None)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"<Track(id={self.id}, title={self.title}, "
|
||||||
|
f"artist={self.artist}, path={self.path}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_or_create(cls, path):
|
||||||
|
try:
|
||||||
|
track = session.query(cls).filter(cls.path == path).one()
|
||||||
|
except NoResultFound:
|
||||||
|
track = Tracks()
|
||||||
|
track.path = path
|
||||||
|
session.add(track)
|
||||||
|
return track
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def search_titles(text):
|
||||||
|
return session.query(Tracks).filter(
|
||||||
|
Tracks.title.ilike(f"%{text}%")
|
||||||
|
).all()
|
||||||
64
songdb.py
64
songdb.py
@ -4,76 +4,21 @@ import argparse
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import sqlalchemy
|
|
||||||
|
|
||||||
import pytiger.logging.config
|
import pytiger.logging.config
|
||||||
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
|
||||||
from sqlalchemy import Column, Float, DateTime, Integer, String
|
|
||||||
from sqlalchemy.orm.exc import NoResultFound
|
|
||||||
from sqlalchemy.orm import sessionmaker
|
|
||||||
from tinytag import TinyTag
|
from tinytag import TinyTag
|
||||||
|
|
||||||
|
from model import Tracks, session
|
||||||
|
|
||||||
|
|
||||||
# "Constants"
|
# "Constants"
|
||||||
ROOT = "/home/kae/music"
|
ROOT = "/home/kae/music"
|
||||||
DISPLAY_SQL = False
|
|
||||||
MYSQL_CONNECT = "mysql+mysqldb://songdb:songdb@localhost/songdb"
|
|
||||||
|
|
||||||
# Instantiate logging
|
# Instantiate logging
|
||||||
pytiger.logging.config.basic_config(stderr=False, level=logging.INFO)
|
pytiger.logging.config.basic_config(stderr=False, level=logging.INFO)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
log.info("Starting")
|
log.info("Starting")
|
||||||
|
|
||||||
# Create session at the global level as per
|
|
||||||
# https://docs.sqlalchemy.org/en/13/orm/session_basics.html
|
|
||||||
|
|
||||||
# Set up database connection
|
|
||||||
log.info("Connect to database")
|
|
||||||
engine = sqlalchemy.create_engine(f"{MYSQL_CONNECT}?charset=utf8",
|
|
||||||
encoding='utf-8',
|
|
||||||
echo=DISPLAY_SQL)
|
|
||||||
Base = declarative_base()
|
|
||||||
Base.metadata.create_all(engine)
|
|
||||||
|
|
||||||
# Create a Session
|
|
||||||
Session = sessionmaker(bind=engine)
|
|
||||||
session = Session()
|
|
||||||
|
|
||||||
|
|
||||||
# Database classes
|
|
||||||
class Tracks(Base):
|
|
||||||
__tablename__ = 'tracks'
|
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
||||||
title = Column(String(256), index=True)
|
|
||||||
artist = Column(String(256), index=True)
|
|
||||||
length = Column(Integer, index=True)
|
|
||||||
path = Column(String(2048), index=False, nullable=False)
|
|
||||||
mtime = Column(Float, index=True)
|
|
||||||
lastplayed = Column(DateTime, index=True, default=None)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return (
|
|
||||||
f"<Track(id={self.id}, title={self.title}, "
|
|
||||||
f"artist={self.artist}, path={self.path}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_or_create(cls, path):
|
|
||||||
try:
|
|
||||||
track = session.query(cls).filter(cls.path == path).one()
|
|
||||||
except NoResultFound:
|
|
||||||
track = Tracks()
|
|
||||||
track.path = path
|
|
||||||
session.add(track)
|
|
||||||
return track
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def search_titles(text):
|
|
||||||
return session.query(Tracks).filter(
|
|
||||||
Tracks.title.ilike(f"%{text}%")
|
|
||||||
).all()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"Main loop"
|
"Main loop"
|
||||||
@ -117,11 +62,12 @@ def update_db():
|
|||||||
print(f"Unrecognised file type: {path}")
|
print(f"Unrecognised file type: {path}")
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
print(f"{count} files processed")
|
print(f"{count} files processed")
|
||||||
|
|
||||||
|
|
||||||
def md5(path):
|
def md5(path):
|
||||||
"https://stackoverflow.com/questions/3431825/"
|
"https://stackoverflow.nl9om/questions/3431825/"
|
||||||
"generating-an-md5-checksum-of-a-file"
|
"generating-an-md5-checksum-of-a-file"
|
||||||
|
|
||||||
hash_md5 = hashlib.md5()
|
hash_md5 = hashlib.md5()
|
||||||
|
|||||||
81
spike.py
Normal file
81
spike.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# tl = Timeloop()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # @tl.job(interval=timedelta(seconds=1))
|
||||||
|
# def update_progress(player, talk_at, silent_at):
|
||||||
|
# elapsed_time = player.get_time()
|
||||||
|
# total_time = player.get_length()
|
||||||
|
# remaining_time = total_time - elapsed_time
|
||||||
|
# talk_time = remaining_time - (total_time - talk_at)
|
||||||
|
# silent_time = remaining_time - (total_time - silent_at)
|
||||||
|
# end_time = (datetime.now() + timedelta(
|
||||||
|
# milliseconds=remaining_time)).strftime("%H:%M:%S")
|
||||||
|
# print(
|
||||||
|
# f"\t{ms_to_mmss(elapsed_time)}/"
|
||||||
|
# f"{ms_to_mmss(total_time)}\t\t"
|
||||||
|
# f"Talk in: {ms_to_mmss(talk_time)} "
|
||||||
|
# f"Silent in: {ms_to_mmss(silent_time)} "
|
||||||
|
# f"Ends at: {end_time} [{ms_to_mmss(remaining_time)}]"
|
||||||
|
# , end="\r")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # Print name of current song, print name of next song. Play current when
|
||||||
|
# # return pressed, Pri--current-song-output-lengthnt remaining time every
|
||||||
|
# # second. When it ends, print name of new current and next song.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def test():
|
||||||
|
# track = "wibg.mp3"
|
||||||
|
# segment = AudioSegment.from_mp3(trm.ack)
|
||||||
|
# print(f"Track: {track}")
|
||||||
|
# print(f"Leading silence: {ms_to_mmss(leading_silence(segment),
|
||||||
|
# decimals=1)}")
|
||||||
|
# talk_at = significant_fade(segment)
|
||||||
|
# silent_at = trailing_silence(segment)
|
||||||
|
# print(f"Talkover fade: {ms_to_mmss(talk_at)}")
|
||||||
|
# print(f"Track silent from: {ms_to_mmss(silent_at)}")
|
||||||
|
# p = vlc.MediaPlayer("wibg.mp3")
|
||||||
|
# _ = input("")
|
||||||
|
# p.play()
|
||||||
|
# print()
|
||||||
|
# rt = RepeatedTimer(0.5, update_progress, p, talk_at, silent_at)
|
||||||
|
# sleep(1)
|
||||||
|
# while p.is_playing():
|
||||||
|
# sleep(1)
|
||||||
|
# rt.stop() # better in a try/finally block to make sure the program ends!
|
||||||
|
# print("End")
|
||||||
|
|
||||||
|
#def kae2(self, index):
|
||||||
|
# print(f"table header click, index={index}")
|
||||||
|
|
||||||
|
#def kae(self, a, b, c):
|
||||||
|
# self.data.append(f"a={a}, b={b}, c={c}")
|
||||||
|
|
||||||
|
#def mousePressEvent(self, QMouseEvent):
|
||||||
|
# print("mouse press")
|
||||||
|
|
||||||
|
#def mouseReleaseEvent(self, QMouseEvent):
|
||||||
|
# print("mouse release")
|
||||||
|
# # QMessageBox.about(
|
||||||
|
# # self,
|
||||||
|
# # "About Sample Editor",
|
||||||
|
# # "\n".join(self.data)
|
||||||
|
# # )
|
||||||
|
#def eventFilter(self, obj, event):
|
||||||
|
# # you could be doing different groups of actions
|
||||||
|
# # for different types of widgets and either filtering
|
||||||
|
# # the event or not.
|
||||||
|
# # Here we just check if its one of the layout widgets
|
||||||
|
# # if self.layout.indexOf(obj) != -1:
|
||||||
|
# # print(f"event received: {event.type()}")
|
||||||
|
# if event.type() == QEvent.MouseButtonPress:
|
||||||
|
# print("Widget click")
|
||||||
|
# # if I returned True right here, the event
|
||||||
|
# # would be filtered and not reach the obj,
|
||||||
|
# # meaning that I decided to handle it myself
|
||||||
|
|
||||||
|
# # regardless, just do the default
|
||||||
|
# return super().eventFilter(obj, event)
|
||||||
Loading…
Reference in New Issue
Block a user