Compare commits
3 Commits
326dc3655e
...
ad717aeb2c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad717aeb2c | ||
|
|
4984ddec98 | ||
|
|
caa13b6693 |
98
app/model.py
98
app/model.py
@ -31,9 +31,8 @@ engine = sqlalchemy.create_engine(f"{Config.MYSQL_CONNECT}?charset=utf8",
|
|||||||
Base = declarative_base()
|
Base = declarative_base()
|
||||||
Base.metadata.create_all(engine)
|
Base.metadata.create_all(engine)
|
||||||
|
|
||||||
# Create a Session
|
# Create a Session factory
|
||||||
Session = sessionmaker(bind=engine)
|
Session = sessionmaker(bind=engine)
|
||||||
session = Session()
|
|
||||||
|
|
||||||
|
|
||||||
# Database classes
|
# Database classes
|
||||||
@ -52,7 +51,7 @@ class Notes(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_note(playlist_id, row, text):
|
def add_note(session, playlist_id, row, text):
|
||||||
DEBUG(f"add_note(playlist_id={playlist_id}, row={row}, text={text})")
|
DEBUG(f"add_note(playlist_id={playlist_id}, row={row}, text={text})")
|
||||||
note = Notes()
|
note = Notes()
|
||||||
note.playlist_id = playlist_id
|
note.playlist_id = playlist_id
|
||||||
@ -63,18 +62,19 @@ class Notes(Base):
|
|||||||
return note
|
return note
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_note(id):
|
def delete_note(session, id):
|
||||||
DEBUG(f"delete_note(id={id}")
|
DEBUG(f"delete_note(id={id}")
|
||||||
|
|
||||||
session.query(Notes).filter(Notes.id == id).delete()
|
session.query(Notes).filter(Notes.id == id).delete()
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
# Not currently used 1 June 2021
|
||||||
def get_note(id):
|
# @staticmethod
|
||||||
return session.query(Notes).filter(Notes.id == id).one()
|
# def get_note(session, id):
|
||||||
|
# return session.query(Notes).filter(Notes.id == id).one()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_note(cls, id, row, text=None):
|
def update_note(cls, session, id, row, text=None):
|
||||||
"""
|
"""
|
||||||
Update note details. If text=None, don't change text.
|
Update note details. If text=None, don't change text.
|
||||||
"""
|
"""
|
||||||
@ -97,7 +97,7 @@ class Playdates(Base):
|
|||||||
tracks = relationship("Tracks", back_populates="playdates")
|
tracks = relationship("Tracks", back_populates="playdates")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_playdate(track):
|
def add_playdate(session, track):
|
||||||
DEBUG(f"add_playdate(track={track})")
|
DEBUG(f"add_playdate(track={track})")
|
||||||
pd = Playdates()
|
pd = Playdates()
|
||||||
pd.lastplayed = datetime.now()
|
pd.lastplayed = datetime.now()
|
||||||
@ -167,7 +167,7 @@ class Playlists(Base):
|
|||||||
return (f"<Playlist(id={self.id}, name={self.name}>")
|
return (f"<Playlist(id={self.id}, name={self.name}>")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def new(name):
|
def new(session, name):
|
||||||
DEBUG(f"Playlists.new(name={name})")
|
DEBUG(f"Playlists.new(name={name})")
|
||||||
playlist = Playlists()
|
playlist = Playlists()
|
||||||
playlist.name = name
|
playlist.name = name
|
||||||
@ -176,7 +176,7 @@ class Playlists(Base):
|
|||||||
return playlist
|
return playlist
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def open(plid):
|
def open(session, plid):
|
||||||
"Record playlist as loaded and used now"
|
"Record playlist as loaded and used now"
|
||||||
|
|
||||||
p = session.query(Playlists).filter(Playlists.id == plid).one()
|
p = session.query(Playlists).filter(Playlists.id == plid).one()
|
||||||
@ -186,14 +186,15 @@ class Playlists(Base):
|
|||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def close(self):
|
def close(self, session):
|
||||||
"Record playlist as no longer loaded"
|
"Record playlist as no longer loaded"
|
||||||
|
|
||||||
self.loaded = False
|
self.loaded = False
|
||||||
|
session.add(self)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_last_used():
|
def get_last_used(session):
|
||||||
"""
|
"""
|
||||||
Return a list of playlists marked "loaded", ordered by loaded date.
|
Return a list of playlists marked "loaded", ordered by loaded date.
|
||||||
"""
|
"""
|
||||||
@ -205,13 +206,13 @@ class Playlists(Base):
|
|||||||
).all()
|
).all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_playlists():
|
def get_all_playlists(session):
|
||||||
"Returns a list of all playlists"
|
"Returns a list of all playlists"
|
||||||
|
|
||||||
return session.query(Playlists).all()
|
return session.query(Playlists).all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_closed_playlists():
|
def get_all_closed_playlists(session):
|
||||||
"Returns a list of all playlists not currently open"
|
"Returns a list of all playlists not currently open"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -223,25 +224,26 @@ class Playlists(Base):
|
|||||||
.order_by(Playlists.last_used.desc())
|
.order_by(Playlists.last_used.desc())
|
||||||
).all()
|
).all()
|
||||||
|
|
||||||
@staticmethod
|
# Not currently used 1 June 2021
|
||||||
def get_name(plid):
|
# @staticmethod
|
||||||
"""
|
# def get_name(session, plid):
|
||||||
Return name of playlist with id 'plid'
|
# """
|
||||||
"""
|
# Return name of playlist with id 'plid'
|
||||||
|
# """
|
||||||
|
|
||||||
return (
|
# return (
|
||||||
session.query(Playlists.name)
|
# session.query(Playlists.name)
|
||||||
.filter(Playlists.id == plid)
|
# .filter(Playlists.id == plid)
|
||||||
).one()[0]
|
# ).one()[0]
|
||||||
|
|
||||||
def add_track(self, track, row=None):
|
def add_track(self, session, track, row=None):
|
||||||
"""
|
"""
|
||||||
Add track to playlist at given row.
|
Add track to playlist at given row.
|
||||||
If row=None, add to end of playlist
|
If row=None, add to end of playlist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not row:
|
if not row:
|
||||||
row = PlaylistTracks.new_row(self.id)
|
row = PlaylistTracks.new_row(session, self.id)
|
||||||
|
|
||||||
glue = PlaylistTracks(row=row)
|
glue = PlaylistTracks(row=row)
|
||||||
glue.track_id = track.id
|
glue.track_id = track.id
|
||||||
@ -265,14 +267,14 @@ class PlaylistTracks(Base):
|
|||||||
playlists = relationship("Playlists", back_populates="tracks")
|
playlists = relationship("Playlists", back_populates="tracks")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def new_row(playlist_id):
|
def new_row(session, playlist_id):
|
||||||
"Return row number > largest existing row number"
|
"Return row number > largest existing row number"
|
||||||
|
|
||||||
last_row = session.query(func.max(PlaylistTracks.row)).one()[0]
|
last_row = session.query(func.max(PlaylistTracks.row)).one()[0]
|
||||||
return last_row + 1
|
return last_row + 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_track(playlist_id, track_id, row):
|
def add_track(session, playlist_id, track_id, row):
|
||||||
DEBUG(
|
DEBUG(
|
||||||
f"PlaylistTracks.add_track(playlist_id={playlist_id}, "
|
f"PlaylistTracks.add_track(playlist_id={playlist_id}, "
|
||||||
f"track_id={track_id}, row={row})"
|
f"track_id={track_id}, row={row})"
|
||||||
@ -285,7 +287,7 @@ class PlaylistTracks(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def move_track(from_playlist_id, row, to_playlist_id):
|
def move_track(session, from_playlist_id, row, to_playlist_id):
|
||||||
DEBUG(
|
DEBUG(
|
||||||
"PlaylistTracks.move_tracks(from_playlist_id="
|
"PlaylistTracks.move_tracks(from_playlist_id="
|
||||||
f"{from_playlist_id}, row={row}, "
|
f"{from_playlist_id}, row={row}, "
|
||||||
@ -304,7 +306,7 @@ class PlaylistTracks(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_track(playlist_id, row):
|
def remove_track(session, playlist_id, row):
|
||||||
DEBUG(
|
DEBUG(
|
||||||
f"PlaylistTracks.remove_track(playlist_id={playlist_id}, "
|
f"PlaylistTracks.remove_track(playlist_id={playlist_id}, "
|
||||||
f"row={row})"
|
f"row={row})"
|
||||||
@ -316,7 +318,7 @@ class PlaylistTracks(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_row_track(playlist_id, row, track_id):
|
def update_row_track(session, playlist_id, row, track_id):
|
||||||
DEBUG(
|
DEBUG(
|
||||||
f"PlaylistTracks.update_track_row(playlist_id={playlist_id}, "
|
f"PlaylistTracks.update_track_row(playlist_id={playlist_id}, "
|
||||||
f"row={row}, track_id={track_id})"
|
f"row={row}, track_id={track_id})"
|
||||||
@ -334,6 +336,13 @@ class PlaylistTracks(Base):
|
|||||||
f"PlaylistTracks.row == {row}"
|
f"PlaylistTracks.row == {row}"
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
except NoResultFound:
|
||||||
|
ERROR(
|
||||||
|
f"No rows matched in query: "
|
||||||
|
f"PlaylistTracks.playlist_id == {playlist_id}, "
|
||||||
|
f"PlaylistTracks.row == {row}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
plt.track_id = track_id
|
plt.track_id = track_id
|
||||||
session.commit()
|
session.commit()
|
||||||
@ -349,7 +358,7 @@ class Settings(Base):
|
|||||||
f_string = Column(String(128), default=None, nullable=True)
|
f_string = Column(String(128), default=None, nullable=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_int(cls, name):
|
def get_int(cls, session, name):
|
||||||
try:
|
try:
|
||||||
int_setting = session.query(cls).filter(
|
int_setting = session.query(cls).filter(
|
||||||
cls.name == name).one()
|
cls.name == name).one()
|
||||||
@ -361,7 +370,7 @@ class Settings(Base):
|
|||||||
session.commit()
|
session.commit()
|
||||||
return int_setting
|
return int_setting
|
||||||
|
|
||||||
def update(self, data):
|
def update(self, session, data):
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
assert hasattr(self, key)
|
assert hasattr(self, key)
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
@ -391,7 +400,7 @@ class Tracks(Base):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_or_create(cls, path):
|
def get_or_create(cls, session, path):
|
||||||
DEBUG(f"Tracks.get_or_create(path={path})")
|
DEBUG(f"Tracks.get_or_create(path={path})")
|
||||||
try:
|
try:
|
||||||
track = session.query(cls).filter(cls.path == path).one()
|
track = session.query(cls).filter(cls.path == path).one()
|
||||||
@ -402,7 +411,7 @@ class Tracks(Base):
|
|||||||
return track
|
return track
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_duration(id):
|
def get_duration(session, id):
|
||||||
try:
|
try:
|
||||||
return session.query(
|
return session.query(
|
||||||
Tracks.duration).filter(Tracks.id == id).one()[0]
|
Tracks.duration).filter(Tracks.id == id).one()[0]
|
||||||
@ -411,19 +420,20 @@ class Tracks(Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_all_paths():
|
def get_all_paths(session):
|
||||||
"Return a list of paths of all tracks"
|
"Return a list of paths of all tracks"
|
||||||
|
|
||||||
return [a[0] for a in session.query(Tracks.path).all()]
|
return [a[0] for a in session.query(Tracks.path).all()]
|
||||||
|
|
||||||
@classmethod
|
# Not used as of 1 June 2021
|
||||||
def get_all_tracks(cls):
|
# @classmethod
|
||||||
"Return a list of all tracks"
|
# def get_all_tracks(cls):
|
||||||
|
# "Return a list of all tracks"
|
||||||
|
|
||||||
return session.query(cls).all()
|
# return session.query(cls).all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_path(id):
|
def get_path(session, id):
|
||||||
try:
|
try:
|
||||||
return session.query(Tracks.path).filter(Tracks.id == id).one()[0]
|
return session.query(Tracks.path).filter(Tracks.id == id).one()[0]
|
||||||
except NoResultFound:
|
except NoResultFound:
|
||||||
@ -431,7 +441,7 @@ class Tracks(Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_track(id):
|
def get_track(session, id):
|
||||||
try:
|
try:
|
||||||
DEBUG(f"Tracks.get_track(track_id={id})")
|
DEBUG(f"Tracks.get_track(track_id={id})")
|
||||||
track = session.query(Tracks).filter(Tracks.id == id).one()
|
track = session.query(Tracks).filter(Tracks.id == id).one()
|
||||||
@ -441,7 +451,7 @@ class Tracks(Base):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def search_titles(text):
|
def search_titles(session, text):
|
||||||
return (
|
return (
|
||||||
session.query(Tracks)
|
session.query(Tracks)
|
||||||
.filter(Tracks.title.ilike(f"%{text}%"))
|
.filter(Tracks.title.ilike(f"%{text}%"))
|
||||||
@ -449,7 +459,7 @@ class Tracks(Base):
|
|||||||
).all()
|
).all()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def track_from_id(id):
|
def track_from_id(session, id):
|
||||||
return session.query(Tracks).filter(
|
return session.query(Tracks).filter(
|
||||||
Tracks.id == id).one()
|
Tracks.id == id).one()
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,8 @@ import helpers
|
|||||||
import music
|
import music
|
||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from model import Notes, Playdates, Playlists, PlaylistTracks, Settings, Tracks
|
from model import (Notes, Playdates, Playlists, PlaylistTracks,
|
||||||
|
Session, Settings, Tracks)
|
||||||
from playlists import Playlist
|
from playlists import Playlist
|
||||||
from songdb import add_path_to_db
|
from songdb import add_path_to_db
|
||||||
from ui.dlg_search_database_ui import Ui_Dialog
|
from ui.dlg_search_database_ui import Ui_Dialog
|
||||||
@ -70,21 +71,23 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg.setNameFilter("Music files (*.flac *.mp3)")
|
dlg.setNameFilter("Music files (*.flac *.mp3)")
|
||||||
|
|
||||||
if dlg.exec_():
|
if dlg.exec_():
|
||||||
for fname in dlg.selectedFiles():
|
with Session() as session:
|
||||||
track = add_path_to_db(fname)
|
for fname in dlg.selectedFiles():
|
||||||
self.visible_playlist()._add_to_playlist(track)
|
track = add_path_to_db(session, fname)
|
||||||
|
self.visible_playlist()._add_to_playlist(track)
|
||||||
|
|
||||||
def set_main_window_size(self):
|
def set_main_window_size(self):
|
||||||
|
|
||||||
record = Settings.get_int("mainwindow_x")
|
with Session() as session:
|
||||||
x = record.f_int or 1
|
record = Settings.get_int(session, "mainwindow_x")
|
||||||
record = Settings.get_int("mainwindow_y")
|
x = record.f_int or 1
|
||||||
y = record.f_int or 1
|
record = Settings.get_int(session, "mainwindow_y")
|
||||||
record = Settings.get_int("mainwindow_width")
|
y = record.f_int or 1
|
||||||
width = record.f_int or 1599
|
record = Settings.get_int(session, "mainwindow_width")
|
||||||
record = Settings.get_int("mainwindow_height")
|
width = record.f_int or 1599
|
||||||
height = record.f_int or 981
|
record = Settings.get_int(session, "mainwindow_height")
|
||||||
self.setGeometry(x, y, width, height)
|
height = record.f_int or 981
|
||||||
|
self.setGeometry(x, y, width, height)
|
||||||
|
|
||||||
def clear_selection(self):
|
def clear_selection(self):
|
||||||
if self.visible_playlist():
|
if self.visible_playlist():
|
||||||
@ -100,21 +103,22 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
else:
|
else:
|
||||||
DEBUG("closeEvent() accepted")
|
DEBUG("closeEvent() accepted")
|
||||||
|
|
||||||
record = Settings.get_int("mainwindow_height")
|
with Session() as session:
|
||||||
if record.f_int != self.height():
|
record = Settings.get_int(session, "mainwindow_height")
|
||||||
record.update({'f_int': self.height()})
|
if record.f_int != self.height():
|
||||||
|
record.update(session, {'f_int': self.height()})
|
||||||
|
|
||||||
record = Settings.get_int("mainwindow_width")
|
record = Settings.get_int(session, "mainwindow_width")
|
||||||
if record.f_int != self.width():
|
if record.f_int != self.width():
|
||||||
record.update({'f_int': self.width()})
|
record.update(session, {'f_int': self.width()})
|
||||||
|
|
||||||
record = Settings.get_int("mainwindow_x")
|
record = Settings.get_int(session, "mainwindow_x")
|
||||||
if record.f_int != self.x():
|
if record.f_int != self.x():
|
||||||
record.update({'f_int': self.x()})
|
record.update(session, {'f_int': self.x()})
|
||||||
|
|
||||||
record = Settings.get_int("mainwindow_y")
|
record = Settings.get_int(session, "mainwindow_y")
|
||||||
if record.f_int != self.y():
|
if record.f_int != self.y():
|
||||||
record.update({'f_int': self.y()})
|
record.update(session, {'f_int': self.y()})
|
||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
@ -159,8 +163,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg.resize(500, 100)
|
dlg.resize(500, 100)
|
||||||
ok = dlg.exec()
|
ok = dlg.exec()
|
||||||
if ok:
|
if ok:
|
||||||
playlist = Playlists.new(dlg.textValue())
|
with Session() as session:
|
||||||
self.load_playlist(playlist)
|
playlist = Playlists.new(session, dlg.textValue())
|
||||||
|
self.load_playlist(session, playlist)
|
||||||
|
|
||||||
def change_volume(self, volume):
|
def change_volume(self, volume):
|
||||||
"Change player maximum volume"
|
"Change player maximum volume"
|
||||||
@ -170,11 +175,12 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.music.set_volume(volume)
|
self.music.set_volume(volume)
|
||||||
|
|
||||||
def close_playlist(self):
|
def close_playlist(self):
|
||||||
self.visible_playlist().db.close()
|
with Session() as session:
|
||||||
index = self.tabPlaylist.currentIndex()
|
self.visible_playlist().db.close(session)
|
||||||
self.tabPlaylist.removeTab(index)
|
index = self.tabPlaylist.currentIndex()
|
||||||
|
self.tabPlaylist.removeTab(index)
|
||||||
|
|
||||||
def create_note(self, text):
|
def create_note(self, session, text):
|
||||||
"""
|
"""
|
||||||
Create note
|
Create note
|
||||||
|
|
||||||
@ -189,7 +195,10 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
row = self.visible_playlist().rowCount()
|
row = self.visible_playlist().rowCount()
|
||||||
DEBUG(f"musicmuster.create_note(text={text}): row={row}")
|
DEBUG(f"musicmuster.create_note(text={text}): row={row}")
|
||||||
|
|
||||||
return Notes.add_note(self.visible_playlist().db.id, row, text)
|
note = Notes.add_note(
|
||||||
|
session, self.visible_playlist().db.id, row, text)
|
||||||
|
|
||||||
|
return note
|
||||||
|
|
||||||
def disable_play_next_controls(self):
|
def disable_play_next_controls(self):
|
||||||
DEBUG("disable_play_next_controls()")
|
DEBUG("disable_play_next_controls()")
|
||||||
@ -250,19 +259,21 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg.resize(500, 100)
|
dlg.resize(500, 100)
|
||||||
ok = dlg.exec()
|
ok = dlg.exec()
|
||||||
if ok:
|
if ok:
|
||||||
note = self.create_note(dlg.textValue())
|
with Session() as session:
|
||||||
self.visible_playlist().add_note(note)
|
note = self.create_note(session, dlg.textValue())
|
||||||
|
self.visible_playlist().add_note(note)
|
||||||
|
|
||||||
def load_last_playlists(self):
|
def load_last_playlists(self):
|
||||||
"Load the playlists that we loaded at end of last session"
|
"Load the playlists that we loaded at end of last session"
|
||||||
|
|
||||||
for playlist in Playlists.get_last_used():
|
with Session() as session:
|
||||||
DEBUG(
|
for playlist in Playlists.get_last_used(session):
|
||||||
f"load_last_playlists(), playlist.name={playlist.name}, "
|
DEBUG(
|
||||||
f"playlist.id={playlist.id}")
|
f"load_last_playlists(), playlist.name={playlist.name}, "
|
||||||
self.load_playlist(playlist)
|
f"playlist.id={playlist.id}")
|
||||||
|
self.load_playlist(session, playlist)
|
||||||
|
|
||||||
def load_playlist(self, playlist_db):
|
def load_playlist(self, session, playlist_db):
|
||||||
"""
|
"""
|
||||||
Take the passed database object, create a playlist display, attach
|
Take the passed database object, create a playlist display, attach
|
||||||
the database object, get it populated and then add tab.
|
the database object, get it populated and then add tab.
|
||||||
@ -270,40 +281,43 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
|
|
||||||
playlist_table = Playlist(self)
|
playlist_table = Playlist(self)
|
||||||
playlist_table.db = playlist_db
|
playlist_table.db = playlist_db
|
||||||
playlist_table.populate()
|
playlist_table.populate(session)
|
||||||
idx = self.tabPlaylist.addTab(playlist_table, playlist_db.name)
|
idx = self.tabPlaylist.addTab(playlist_table, playlist_db.name)
|
||||||
self.tabPlaylist.setCurrentIndex(idx)
|
self.tabPlaylist.setCurrentIndex(idx)
|
||||||
|
|
||||||
def move_selected(self):
|
def move_selected(self):
|
||||||
"Move selected rows to another playlist"
|
"Move selected rows to another playlist"
|
||||||
|
|
||||||
playlists = list(
|
with Session() as session:
|
||||||
set(Playlists.get_all_playlists()) - {self.visible_playlist().db}
|
playlists = list(
|
||||||
)
|
set(Playlists.get_all_playlists(session)) -
|
||||||
dlg = SelectPlaylistDialog(self, playlists=playlists)
|
{self.visible_playlist().db}
|
||||||
dlg.exec()
|
)
|
||||||
if not dlg.plid:
|
dlg = SelectPlaylistDialog(self, playlists=playlists)
|
||||||
return
|
dlg.exec()
|
||||||
|
if not dlg.plid:
|
||||||
|
return
|
||||||
|
|
||||||
# If destination playlist is visible, we need to add the moved
|
# If destination playlist is visible, we need to add the moved
|
||||||
# tracks to it. If not, they will be automatically loaded when
|
# tracks to it. If not, they will be automatically loaded when
|
||||||
# the playlistis opened.
|
# the playlistis opened.
|
||||||
destination_playlist = None
|
destination_playlist = None
|
||||||
for tab in range(self.tabPlaylist.count()):
|
for tab in range(self.tabPlaylist.count()):
|
||||||
if self.tabPlaylist.widget(tab).db.id == dlg.plid:
|
if self.tabPlaylist.widget(tab).db.id == dlg.plid:
|
||||||
destination_playlist = self.tabPlaylist.widget(tab)
|
destination_playlist = self.tabPlaylist.widget(tab)
|
||||||
break
|
break
|
||||||
|
|
||||||
rows = []
|
rows = []
|
||||||
for (row, track_id) in (
|
for (row, track_id) in (
|
||||||
self.visible_playlist().get_selected_rows_and_tracks()):
|
self.visible_playlist().get_selected_rows_and_tracks()):
|
||||||
rows.append(row)
|
rows.append(row)
|
||||||
# Update database
|
# Update database
|
||||||
PlaylistTracks.move_track(
|
PlaylistTracks.move_track(
|
||||||
self.visible_playlist().db.id, row, dlg.plid)
|
session, self.visible_playlist().db.id, row, dlg.plid)
|
||||||
# Update destination playlist if visible
|
# Update destination playlist if visible
|
||||||
if destination_playlist:
|
if destination_playlist:
|
||||||
destination_playlist.add_track(Tracks.track_from_id(track_id))
|
destination_playlist.add_track(
|
||||||
|
session, Tracks.track_from_id(session, track_id))
|
||||||
|
|
||||||
# Update source playlist
|
# Update source playlist
|
||||||
self.visible_playlist().remove_rows(rows)
|
self.visible_playlist().remove_rows(rows)
|
||||||
@ -334,46 +348,47 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
f"{self.current_track.title if self.current_track else None}"
|
f"{self.current_track.title if self.current_track else None}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Stop current track, if any
|
with Session() as session:
|
||||||
self.stop_playing()
|
# Stop current track, if any
|
||||||
|
self.stop_playing()
|
||||||
|
|
||||||
# Play next track
|
# Play next track
|
||||||
self.current_track = self.next_track
|
self.current_track = self.next_track
|
||||||
self.current_track_playlist = self.next_track_playlist
|
self.current_track_playlist = self.next_track_playlist
|
||||||
self.next_track = None
|
self.next_track = None
|
||||||
self.next_track_playlist = None
|
self.next_track_playlist = None
|
||||||
self.music.play(self.current_track.path)
|
self.music.play(self.current_track.path)
|
||||||
|
|
||||||
# Update metadata
|
# Update metadata
|
||||||
next_track_id = self.current_track_playlist.play_started()
|
next_track_id = self.current_track_playlist.play_started()
|
||||||
|
|
||||||
if next_track_id is not None:
|
if next_track_id is not None:
|
||||||
self.next_track = Tracks.get_track(next_track_id)
|
self.next_track = Tracks.get_track(session, next_track_id)
|
||||||
self.next_track_playlist = self.current_track_playlist
|
self.next_track_playlist = self.current_track_playlist
|
||||||
# Check we can read it
|
# Check we can read it
|
||||||
if not os.access(self.next_track.path, os.R_OK):
|
if not os.access(self.next_track.path, os.R_OK):
|
||||||
self.show_warning(
|
self.show_warning(
|
||||||
"Can't read next track",
|
"Can't read next track",
|
||||||
self.next_track.path)
|
self.next_track.path)
|
||||||
else:
|
else:
|
||||||
self.next_track = self.next_track_playlist = None
|
self.next_track = self.next_track_playlist = None
|
||||||
|
|
||||||
# Tell database to record it as played
|
# Tell database to record it as played
|
||||||
self.current_track.update_lastplayed()
|
self.current_track.update_lastplayed()
|
||||||
Playdates.add_playdate(self.current_track)
|
Playdates.add_playdate(session, self.current_track)
|
||||||
|
|
||||||
self.disable_play_next_controls()
|
self.disable_play_next_controls()
|
||||||
self.update_headers()
|
self.update_headers()
|
||||||
|
|
||||||
# Set time clocks
|
# Set time clocks
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
self.label_start_tod.setText(now.strftime("%H:%M:%S"))
|
self.label_start_tod.setText(now.strftime("%H:%M:%S"))
|
||||||
silence_at = self.current_track.silence_at
|
silence_at = self.current_track.silence_at
|
||||||
silence_time = now + timedelta(milliseconds=silence_at)
|
silence_time = now + timedelta(milliseconds=silence_at)
|
||||||
self.label_silent_tod.setText(silence_time.strftime("%H:%M:%S"))
|
self.label_silent_tod.setText(silence_time.strftime("%H:%M:%S"))
|
||||||
self.label_fade_length.setText(helpers.ms_to_mmss(
|
self.label_fade_length.setText(helpers.ms_to_mmss(
|
||||||
silence_at - self.current_track.fade_at
|
silence_at - self.current_track.fade_at
|
||||||
))
|
))
|
||||||
|
|
||||||
def play_previous(self):
|
def play_previous(self):
|
||||||
"Resume playing last track"
|
"Resume playing last track"
|
||||||
@ -386,12 +401,13 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg.exec()
|
dlg.exec()
|
||||||
|
|
||||||
def open_playlist(self):
|
def open_playlist(self):
|
||||||
playlists = Playlists.get_all_closed_playlists()
|
with Session() as session:
|
||||||
dlg = SelectPlaylistDialog(self, playlists=playlists)
|
playlists = Playlists.get_all_closed_playlists(session)
|
||||||
dlg.exec()
|
dlg = SelectPlaylistDialog(self, playlists=playlists)
|
||||||
if dlg.plid:
|
dlg.exec()
|
||||||
playlist = Playlists.open(dlg.plid)
|
if dlg.plid:
|
||||||
self.load_playlist(playlist)
|
playlist = Playlists.open(session, dlg.plid)
|
||||||
|
self.load_playlist(session, playlist)
|
||||||
|
|
||||||
def select_next_track(self):
|
def select_next_track(self):
|
||||||
"Select next or first track in playlist"
|
"Select next or first track in playlist"
|
||||||
@ -406,15 +422,16 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
def set_next_track(self, next_track_id=None):
|
def set_next_track(self, next_track_id=None):
|
||||||
"Set selected track as next"
|
"Set selected track as next"
|
||||||
|
|
||||||
if not next_track_id:
|
with Session() as session:
|
||||||
next_track_id = self.visible_playlist().set_selected_as_next()
|
if not next_track_id:
|
||||||
if next_track_id:
|
next_track_id = self.visible_playlist().set_selected_as_next()
|
||||||
if self.next_track_playlist != self.visible_playlist():
|
if next_track_id:
|
||||||
if self.next_track_playlist:
|
if self.next_track_playlist != self.visible_playlist():
|
||||||
self.next_track_playlist.clear_next()
|
if self.next_track_playlist:
|
||||||
self.next_track_playlist = self.visible_playlist()
|
self.next_track_playlist.clear_next()
|
||||||
self.next_track = Tracks.get_track(next_track_id)
|
self.next_track_playlist = self.visible_playlist()
|
||||||
self.update_headers()
|
self.next_track = Tracks.get_track(session, next_track_id)
|
||||||
|
self.update_headers()
|
||||||
|
|
||||||
def show_warning(self, title, msg):
|
def show_warning(self, title, msg):
|
||||||
"Display a warning to user"
|
"Display a warning to user"
|
||||||
@ -605,6 +622,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
class DbDialog(QDialog):
|
class DbDialog(QDialog):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self.session = Session()
|
||||||
self.ui = Ui_Dialog()
|
self.ui = Ui_Dialog()
|
||||||
self.ui.setupUi(self)
|
self.ui.setupUi(self)
|
||||||
self.ui.searchString.textEdited.connect(self.chars_typed)
|
self.ui.searchString.textEdited.connect(self.chars_typed)
|
||||||
@ -614,20 +632,20 @@ class DbDialog(QDialog):
|
|||||||
self.ui.btnClose.clicked.connect(self.close)
|
self.ui.btnClose.clicked.connect(self.close)
|
||||||
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
|
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
|
||||||
|
|
||||||
record = Settings.get_int("dbdialog_width")
|
record = Settings.get_int(self.session, "dbdialog_width")
|
||||||
width = record.f_int or 800
|
width = record.f_int or 800
|
||||||
record = Settings.get_int("dbdialog_height")
|
record = Settings.get_int(self.session, "dbdialog_height")
|
||||||
height = record.f_int or 600
|
height = record.f_int or 600
|
||||||
self.resize(width, height)
|
self.resize(width, height)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
record = Settings.get_int("dbdialog_height")
|
record = Settings.get_int(self.session, "dbdialog_height")
|
||||||
if record.f_int != self.height():
|
if record.f_int != self.height():
|
||||||
record.update({'f_int': self.height()})
|
record.update(self.session, {'f_int': self.height()})
|
||||||
|
|
||||||
record = Settings.get_int("dbdialog_width")
|
record = Settings.get_int(self.session, "dbdialog_width")
|
||||||
if record.f_int != self.width():
|
if record.f_int != self.width():
|
||||||
record.update({'f_int': self.width()})
|
record.update(self.session, {'f_int': self.width()})
|
||||||
|
|
||||||
def add_selected(self):
|
def add_selected(self):
|
||||||
if not self.ui.matchList.selectedItems():
|
if not self.ui.matchList.selectedItems():
|
||||||
@ -643,7 +661,7 @@ class DbDialog(QDialog):
|
|||||||
|
|
||||||
def chars_typed(self, s):
|
def chars_typed(self, s):
|
||||||
if len(s) >= 3:
|
if len(s) >= 3:
|
||||||
matches = Tracks.search_titles(s)
|
matches = Tracks.search_titles(self.session, s)
|
||||||
self.ui.matchList.clear()
|
self.ui.matchList.clear()
|
||||||
if matches:
|
if matches:
|
||||||
for track in matches:
|
for track in matches:
|
||||||
@ -662,7 +680,7 @@ class DbDialog(QDialog):
|
|||||||
self.select_searchtext()
|
self.select_searchtext()
|
||||||
|
|
||||||
def add_track(self, track_id):
|
def add_track(self, track_id):
|
||||||
track = Tracks.track_from_id(track_id)
|
track = Tracks.track_from_id(self.session, track_id)
|
||||||
self.parent().visible_playlist()._add_to_playlist(track)
|
self.parent().visible_playlist()._add_to_playlist(track)
|
||||||
# Select search text to make it easier for next search
|
# Select search text to make it easier for next search
|
||||||
self.select_searchtext()
|
self.select_searchtext()
|
||||||
@ -677,7 +695,7 @@ class DbDialog(QDialog):
|
|||||||
|
|
||||||
item = self.ui.matchList.currentItem()
|
item = self.ui.matchList.currentItem()
|
||||||
track_id = item.data(Qt.UserRole)
|
track_id = item.data(Qt.UserRole)
|
||||||
self.ui.dbPath.setText(Tracks.get_path(track_id))
|
self.ui.dbPath.setText(Tracks.get_path(self.session, track_id))
|
||||||
|
|
||||||
|
|
||||||
class SelectPlaylistDialog(QDialog):
|
class SelectPlaylistDialog(QDialog):
|
||||||
@ -693,11 +711,12 @@ class SelectPlaylistDialog(QDialog):
|
|||||||
self.ui.buttonBox.rejected.connect(self.close)
|
self.ui.buttonBox.rejected.connect(self.close)
|
||||||
self.plid = None
|
self.plid = None
|
||||||
|
|
||||||
record = Settings.get_int("select_playlist_dialog_width")
|
with Session() as session:
|
||||||
width = record.f_int or 800
|
record = Settings.get_int(session, "select_playlist_dialog_width")
|
||||||
record = Settings.get_int("select_playlist_dialog_height")
|
width = record.f_int or 800
|
||||||
height = record.f_int or 600
|
record = Settings.get_int(session, "select_playlist_dialog_height")
|
||||||
self.resize(width, height)
|
height = record.f_int or 600
|
||||||
|
self.resize(width, height)
|
||||||
|
|
||||||
for (plid, plname) in [(a.id, a.name) for a in playlists]:
|
for (plid, plname) in [(a.id, a.name) for a in playlists]:
|
||||||
p = QListWidgetItem()
|
p = QListWidgetItem()
|
||||||
@ -706,13 +725,14 @@ class SelectPlaylistDialog(QDialog):
|
|||||||
self.ui.lstPlaylists.addItem(p)
|
self.ui.lstPlaylists.addItem(p)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
record = Settings.get_int("select_playlist_dialog_height")
|
with Session() as session:
|
||||||
if record.f_int != self.height():
|
record = Settings.get_int(session, "select_playlist_dialog_height")
|
||||||
record.update({'f_int': self.height()})
|
if record.f_int != self.height():
|
||||||
|
record.update(session, {'f_int': self.height()})
|
||||||
|
|
||||||
record = Settings.get_int("select_playlist_dialog_width")
|
record = Settings.get_int(session, "select_playlist_dialog_width")
|
||||||
if record.f_int != self.width():
|
if record.f_int != self.width():
|
||||||
record.update({'f_int': self.width()})
|
record.update(session, {'f_int': self.width()})
|
||||||
|
|
||||||
def list_doubleclick(self, entry):
|
def list_doubleclick(self, entry):
|
||||||
self.plid = entry.data(Qt.UserRole)
|
self.plid = entry.data(Qt.UserRole)
|
||||||
|
|||||||
210
app/playlists.py
210
app/playlists.py
@ -17,7 +17,7 @@ import os
|
|||||||
from config import Config
|
from config import Config
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from log import DEBUG, ERROR
|
from log import DEBUG, ERROR
|
||||||
from model import Notes, PlaylistTracks, Settings, Tracks
|
from model import Notes, PlaylistTracks, Session, Settings, Tracks
|
||||||
|
|
||||||
|
|
||||||
class Playlist(QTableWidget):
|
class Playlist(QTableWidget):
|
||||||
@ -94,9 +94,10 @@ class Playlist(QTableWidget):
|
|||||||
for column in range(self.columnCount()):
|
for column in range(self.columnCount()):
|
||||||
width = self.columnWidth(column)
|
width = self.columnWidth(column)
|
||||||
name = f"playlist_col_{str(column)}_width"
|
name = f"playlist_col_{str(column)}_width"
|
||||||
record = Settings.get_int(name)
|
with Session() as session:
|
||||||
if record.f_int != self.columnWidth(column):
|
record = Settings.get_int(session, name)
|
||||||
record.update({'f_int': width})
|
if record.f_int != self.columnWidth(column):
|
||||||
|
record.update(session, {'f_int': width})
|
||||||
|
|
||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
@ -275,7 +276,7 @@ class Playlist(QTableWidget):
|
|||||||
if not self.selectionModel().hasSelection():
|
if not self.selectionModel().hasSelection():
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return self.selectionModel().selectedRows()[0]
|
return self.selectionModel().selectedRows()[0].row()
|
||||||
|
|
||||||
def get_selected_rows_and_tracks(self):
|
def get_selected_rows_and_tracks(self):
|
||||||
"Return a list of selected (rows, track_id) tuples"
|
"Return a list of selected (rows, track_id) tuples"
|
||||||
@ -336,7 +337,7 @@ class Playlist(QTableWidget):
|
|||||||
self.current_track_start_time = None
|
self.current_track_start_time = None
|
||||||
self._repaint(save_playlist=False)
|
self._repaint(save_playlist=False)
|
||||||
|
|
||||||
def populate(self):
|
def populate(self, session):
|
||||||
# add them in row order. We don't mandate that an item will be
|
# add them in row order. We don't mandate that an item will be
|
||||||
# on its specified row, only that it will be above
|
# on its specified row, only that it will be above
|
||||||
# larger-numbered row items, and below lower-numbered ones.
|
# larger-numbered row items, and below lower-numbered ones.
|
||||||
@ -454,7 +455,7 @@ class Playlist(QTableWidget):
|
|||||||
elif isinstance(data, Notes):
|
elif isinstance(data, Notes):
|
||||||
self.add_note(data, repaint=repaint)
|
self.add_note(data, repaint=repaint)
|
||||||
|
|
||||||
def _calculate_next_start_time(self, row, start):
|
def _calculate_next_start_time(self, session, row, start):
|
||||||
"Return this row's end time given its start time"
|
"Return this row's end time given its start time"
|
||||||
|
|
||||||
if start is None:
|
if start is None:
|
||||||
@ -463,7 +464,7 @@ class Playlist(QTableWidget):
|
|||||||
DEBUG("_calculate_next_start_time() called with row=None")
|
DEBUG("_calculate_next_start_time() called with row=None")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
duration = Tracks.get_duration(self._get_row_id(row))
|
duration = Tracks.get_duration(session, self._get_row_id(row))
|
||||||
return start + timedelta(milliseconds=duration)
|
return start + timedelta(milliseconds=duration)
|
||||||
|
|
||||||
def _can_read_track(self, track):
|
def _can_read_track(self, track):
|
||||||
@ -499,12 +500,13 @@ class Playlist(QTableWidget):
|
|||||||
msg.setDefaultButton(QMessageBox.Cancel)
|
msg.setDefaultButton(QMessageBox.Cancel)
|
||||||
msg.setWindowTitle("Delete row")
|
msg.setWindowTitle("Delete row")
|
||||||
if msg.exec() == QMessageBox.Yes:
|
if msg.exec() == QMessageBox.Yes:
|
||||||
id = self._get_row_id(row)
|
with Session() as session:
|
||||||
if row in self._meta_get_notes():
|
id = self._get_row_id(row)
|
||||||
Notes.delete_note(id)
|
if row in self._meta_get_notes():
|
||||||
else:
|
Notes.delete_note(session, id)
|
||||||
PlaylistTracks.remove_track(self.db.id, row)
|
else:
|
||||||
self.removeRow(row)
|
PlaylistTracks.remove_track(session, self.db.id, row)
|
||||||
|
self.removeRow(row)
|
||||||
|
|
||||||
self._repaint()
|
self._repaint()
|
||||||
|
|
||||||
@ -723,86 +725,88 @@ class Playlist(QTableWidget):
|
|||||||
f"save_playlist={save_playlist})"
|
f"save_playlist={save_playlist})"
|
||||||
)
|
)
|
||||||
|
|
||||||
if clear_selection:
|
with Session() as session:
|
||||||
self.clearSelection()
|
if clear_selection:
|
||||||
if save_playlist:
|
self.clearSelection()
|
||||||
self._save_playlist()
|
if save_playlist:
|
||||||
|
self._save_playlist(session)
|
||||||
|
|
||||||
current = self._meta_get_current()
|
current = self._meta_get_current()
|
||||||
next = self._meta_get_next()
|
next = self._meta_get_next()
|
||||||
notes = self._meta_get_notes()
|
notes = self._meta_get_notes()
|
||||||
|
|
||||||
# Set colours and start times
|
# Set colours and start times
|
||||||
next_start_time = None
|
next_start_time = None
|
||||||
|
|
||||||
# Cycle through all rows
|
# Cycle through all rows
|
||||||
for row in range(self.rowCount()):
|
for row in range(self.rowCount()):
|
||||||
# We can't calculate start times until next_start_time is
|
# We can't calculate start times until next_start_time is
|
||||||
# set. That can be set by either a note with a time, or the
|
# set. That can be set by either a note with a time, or the
|
||||||
# current track.
|
# current track.
|
||||||
|
|
||||||
if row in notes:
|
if row in notes:
|
||||||
row_time = self._get_row_time(row)
|
row_time = self._get_row_time(row)
|
||||||
if row_time:
|
if row_time:
|
||||||
next_start_time = row_time
|
next_start_time = row_time
|
||||||
# Set colour
|
# Set colour
|
||||||
self._set_row_colour(
|
self._set_row_colour(
|
||||||
row, QColor(Config.COLOUR_NOTES_PLAYLIST)
|
row, QColor(Config.COLOUR_NOTES_PLAYLIST)
|
||||||
)
|
)
|
||||||
self._set_row_bold(row)
|
|
||||||
|
|
||||||
elif row == current:
|
|
||||||
# Set start time
|
|
||||||
self._set_row_time(row, self.current_track_start_time)
|
|
||||||
# Calculate next_start_time
|
|
||||||
next_start_time = self._calculate_next_start_time(
|
|
||||||
row, self.current_track_start_time)
|
|
||||||
# Set colour
|
|
||||||
self._set_row_colour(row, QColor(
|
|
||||||
Config.COLOUR_CURRENT_PLAYLIST))
|
|
||||||
# Make bold
|
|
||||||
self._set_row_bold(row)
|
|
||||||
|
|
||||||
elif row == next:
|
|
||||||
# if there's a current track playing, set start time from that
|
|
||||||
if self.current_track_start_time:
|
|
||||||
start_time = self._calculate_next_start_time(
|
|
||||||
current, self.current_track_start_time)
|
|
||||||
else:
|
|
||||||
# No current track to base from, but don't change
|
|
||||||
# time if it's already set
|
|
||||||
start_time = self._get_row_time(row)
|
|
||||||
if not start_time:
|
|
||||||
start_time = next_start_time
|
|
||||||
# Now set it
|
|
||||||
self._set_row_time(row, start_time)
|
|
||||||
next_start_time = self._calculate_next_start_time(
|
|
||||||
row, start_time)
|
|
||||||
# Set colour
|
|
||||||
self._set_row_colour(row, QColor(Config.COLOUR_NEXT_PLAYLIST))
|
|
||||||
# Make bold
|
|
||||||
self._set_row_bold(row)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Stripe remaining rows
|
|
||||||
if row % 2:
|
|
||||||
colour = QColor(Config.COLOUR_ODD_PLAYLIST)
|
|
||||||
else:
|
|
||||||
colour = QColor(Config.COLOUR_EVEN_PLAYLIST)
|
|
||||||
self._set_row_colour(row, colour)
|
|
||||||
|
|
||||||
if self._get_row_id(row) in self.played_tracks:
|
|
||||||
self._set_row_not_bold(row)
|
|
||||||
else:
|
|
||||||
# Set time only if we haven't played it yet
|
|
||||||
if next_start_time:
|
|
||||||
self._set_row_time(row, next_start_time)
|
|
||||||
next_start_time = self._calculate_next_start_time(
|
|
||||||
row, next_start_time)
|
|
||||||
# Don't dim unplayed tracks
|
|
||||||
self._set_row_bold(row)
|
self._set_row_bold(row)
|
||||||
|
|
||||||
def _save_playlist(self):
|
elif row == current:
|
||||||
|
# Set start time
|
||||||
|
self._set_row_time(row, self.current_track_start_time)
|
||||||
|
# Calculate next_start_time
|
||||||
|
next_start_time = self._calculate_next_start_time(
|
||||||
|
session, row, self.current_track_start_time)
|
||||||
|
# Set colour
|
||||||
|
self._set_row_colour(row, QColor(
|
||||||
|
Config.COLOUR_CURRENT_PLAYLIST))
|
||||||
|
# Make bold
|
||||||
|
self._set_row_bold(row)
|
||||||
|
|
||||||
|
elif row == next:
|
||||||
|
# if there's a track playing, set start time from that
|
||||||
|
if self.current_track_start_time:
|
||||||
|
start_time = self._calculate_next_start_time(
|
||||||
|
session, current, self.current_track_start_time)
|
||||||
|
else:
|
||||||
|
# No current track to base from, but don't change
|
||||||
|
# time if it's already set
|
||||||
|
start_time = self._get_row_time(row)
|
||||||
|
if not start_time:
|
||||||
|
start_time = next_start_time
|
||||||
|
# Now set it
|
||||||
|
self._set_row_time(row, start_time)
|
||||||
|
next_start_time = self._calculate_next_start_time(
|
||||||
|
session, row, start_time)
|
||||||
|
# Set colour
|
||||||
|
self._set_row_colour(
|
||||||
|
row, QColor(Config.COLOUR_NEXT_PLAYLIST))
|
||||||
|
# Make bold
|
||||||
|
self._set_row_bold(row)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Stripe remaining rows
|
||||||
|
if row % 2:
|
||||||
|
colour = QColor(Config.COLOUR_ODD_PLAYLIST)
|
||||||
|
else:
|
||||||
|
colour = QColor(Config.COLOUR_EVEN_PLAYLIST)
|
||||||
|
self._set_row_colour(row, colour)
|
||||||
|
|
||||||
|
if self._get_row_id(row) in self.played_tracks:
|
||||||
|
self._set_row_not_bold(row)
|
||||||
|
else:
|
||||||
|
# Set time only if we haven't played it yet
|
||||||
|
if next_start_time:
|
||||||
|
self._set_row_time(row, next_start_time)
|
||||||
|
next_start_time = self._calculate_next_start_time(
|
||||||
|
session, row, next_start_time)
|
||||||
|
# Don't dim unplayed tracks
|
||||||
|
self._set_row_bold(row)
|
||||||
|
|
||||||
|
def _save_playlist(self, session):
|
||||||
"""
|
"""
|
||||||
Save playlist to database. We do this by correcting differences
|
Save playlist to database. We do this by correcting differences
|
||||||
between the on-screen (definitive) playlist and that in the
|
between the on-screen (definitive) playlist and that in the
|
||||||
@ -846,7 +850,7 @@ class Playlist(QTableWidget):
|
|||||||
f"_save_playlist(): Delete note note_id={note_id} "
|
f"_save_playlist(): Delete note note_id={note_id} "
|
||||||
f"from playlist {self} in database"
|
f"from playlist {self} in database"
|
||||||
)
|
)
|
||||||
Notes.delete_note(note_id)
|
Notes.delete_note(session, note_id)
|
||||||
|
|
||||||
# Note rows to update in playlist database
|
# Note rows to update in playlist database
|
||||||
for note_id in set(playlist_notes.keys()) & set(database_notes.keys()):
|
for note_id in set(playlist_notes.keys()) & set(database_notes.keys()):
|
||||||
@ -856,7 +860,7 @@ class Playlist(QTableWidget):
|
|||||||
f"from row={database_notes[note_id]} to "
|
f"from row={database_notes[note_id]} to "
|
||||||
f"row={playlist_notes[note_id]}"
|
f"row={playlist_notes[note_id]}"
|
||||||
)
|
)
|
||||||
Notes.update_note(note_id, playlist_notes[note_id])
|
Notes.update_note(session, note_id, playlist_notes[note_id])
|
||||||
|
|
||||||
# Now check tracks
|
# Now check tracks
|
||||||
# Create dictionaries indexed by row
|
# Create dictionaries indexed by row
|
||||||
@ -878,7 +882,8 @@ class Playlist(QTableWidget):
|
|||||||
set(set(playlist_tracks.keys()) - set(database_tracks.keys()))
|
set(set(playlist_tracks.keys()) - set(database_tracks.keys()))
|
||||||
):
|
):
|
||||||
DEBUG(f"_save_playlist(): row {row} missing from database")
|
DEBUG(f"_save_playlist(): row {row} missing from database")
|
||||||
PlaylistTracks.add_track(self.db.id, playlist_tracks[row], row)
|
PlaylistTracks.add_track(session, self.db.id,
|
||||||
|
playlist_tracks[row], row)
|
||||||
|
|
||||||
# Track rows to remove from database
|
# Track rows to remove from database
|
||||||
for row in (
|
for row in (
|
||||||
@ -889,7 +894,7 @@ class Playlist(QTableWidget):
|
|||||||
f"_save_playlist(): row {row} in database not playlist "
|
f"_save_playlist(): row {row} in database not playlist "
|
||||||
f"(track={track})"
|
f"(track={track})"
|
||||||
)
|
)
|
||||||
PlaylistTracks.remove_track(self.db.id, row)
|
PlaylistTracks.remove_track(session, self.db.id, row)
|
||||||
|
|
||||||
# Track rows to update in database
|
# Track rows to update in database
|
||||||
for row in (
|
for row in (
|
||||||
@ -902,20 +907,21 @@ class Playlist(QTableWidget):
|
|||||||
f"to track={playlist_tracks[row]}"
|
f"to track={playlist_tracks[row]}"
|
||||||
)
|
)
|
||||||
PlaylistTracks.update_row_track(
|
PlaylistTracks.update_row_track(
|
||||||
self.db.id, row, playlist_tracks[row])
|
session, self.db.id, row, playlist_tracks[row])
|
||||||
|
|
||||||
def _set_column_widths(self):
|
def _set_column_widths(self):
|
||||||
|
|
||||||
# Column widths from settings
|
# Column widths from settings
|
||||||
for column in range(self.columnCount()):
|
with Session() as session:
|
||||||
# Only show column 0 in test mode
|
for column in range(self.columnCount()):
|
||||||
if (column == 0 and not Config.TESTMODE):
|
# Only show column 0 in test mode
|
||||||
self.setColumnWidth(0, 0)
|
if (column == 0 and not Config.TESTMODE):
|
||||||
else:
|
self.setColumnWidth(0, 0)
|
||||||
name = f"playlist_col_{str(column)}_width"
|
else:
|
||||||
record = Settings.get_int(name)
|
name = f"playlist_col_{str(column)}_width"
|
||||||
if record.f_int is not None:
|
record = Settings.get_int(session, name)
|
||||||
self.setColumnWidth(column, record.f_int)
|
if record.f_int is not None:
|
||||||
|
self.setColumnWidth(column, record.f_int)
|
||||||
|
|
||||||
def _set_row_bold(self, row, bold=True):
|
def _set_row_bold(self, row, bold=True):
|
||||||
boldfont = QFont()
|
boldfont = QFont()
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import tempfile
|
|||||||
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from log import DEBUG, INFO
|
from log import DEBUG, INFO
|
||||||
from model import Tracks, session
|
from model import Tracks, Session
|
||||||
from mutagen.flac import FLAC
|
from mutagen.flac import FLAC
|
||||||
from pydub import AudioSegment, effects
|
from pydub import AudioSegment, effects
|
||||||
from tinytag import TinyTag
|
from tinytag import TinyTag
|
||||||
@ -28,15 +28,16 @@ def main():
|
|||||||
# Run as required
|
# Run as required
|
||||||
if args.update:
|
if args.update:
|
||||||
INFO("Updating database")
|
INFO("Updating database")
|
||||||
update_db()
|
with Session() as session:
|
||||||
|
update_db(session)
|
||||||
|
|
||||||
INFO("Finished")
|
INFO("Finished")
|
||||||
|
|
||||||
|
|
||||||
def add_path_to_db(path):
|
def add_path_to_db(session, path):
|
||||||
"Add passed path to database along with metadata"
|
"Add passed path to database along with metadata"
|
||||||
|
|
||||||
track = Tracks.get_or_create(path)
|
track = Tracks.get_or_create(session, path)
|
||||||
tag = TinyTag.get(path)
|
tag = TinyTag.get(path)
|
||||||
audio = get_audio_segment(path)
|
audio = get_audio_segment(path)
|
||||||
|
|
||||||
@ -143,19 +144,20 @@ def fade_point(audio_segment, fade_threshold=Config.DBFS_FADE,
|
|||||||
return int(trim_ms)
|
return int(trim_ms)
|
||||||
|
|
||||||
|
|
||||||
def rescan_database():
|
# Current unused (1 June 2021)
|
||||||
|
# def rescan_database(session):
|
||||||
tracks = Tracks.get_all_tracks()
|
#
|
||||||
total_tracks = len(tracks)
|
# tracks = Tracks.get_all_tracks(session)
|
||||||
track_count = 0
|
# total_tracks = len(tracks)
|
||||||
for track in tracks:
|
# track_count = 0
|
||||||
track_count += 1
|
# for track in tracks:
|
||||||
print(f"Track {track_count} of {total_tracks}")
|
# track_count += 1
|
||||||
audio = get_audio_segment(track.path)
|
# print(f"Track {track_count} of {total_tracks}")
|
||||||
track.start_gap = leading_silence(audio)
|
# audio = get_audio_segment(track.path)
|
||||||
track.fade_at = fade_point(audio)
|
# track.start_gap = leading_silence(audio)
|
||||||
track.silence_at = trailing_silence(audio)
|
# track.fade_at = fade_point(audio)
|
||||||
session.commit()
|
# track.silence_at = trailing_silence(audio)
|
||||||
|
# session.commit()
|
||||||
|
|
||||||
|
|
||||||
def trailing_silence(audio_segment, silence_threshold=-50.0,
|
def trailing_silence(audio_segment, silence_threshold=-50.0,
|
||||||
@ -163,14 +165,14 @@ def trailing_silence(audio_segment, silence_threshold=-50.0,
|
|||||||
return fade_point(audio_segment, silence_threshold, chunk_size)
|
return fade_point(audio_segment, silence_threshold, chunk_size)
|
||||||
|
|
||||||
|
|
||||||
def update_db():
|
def update_db(session):
|
||||||
"""
|
"""
|
||||||
Repopulate database
|
Repopulate database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Search for tracks in only one of directory and database
|
# Search for tracks in only one of directory and database
|
||||||
|
|
||||||
db_paths = set(Tracks.get_all_paths())
|
db_paths = set(Tracks.get_all_paths(session))
|
||||||
|
|
||||||
os_paths_list = []
|
os_paths_list = []
|
||||||
for root, dirs, files in os.walk(Config.ROOT):
|
for root, dirs, files in os.walk(Config.ROOT):
|
||||||
@ -188,7 +190,7 @@ def update_db():
|
|||||||
for path in list(os_paths - db_paths):
|
for path in list(os_paths - db_paths):
|
||||||
# TODO
|
# TODO
|
||||||
INFO(f"Adding to dataabase: {path}")
|
INFO(f"Adding to dataabase: {path}")
|
||||||
add_path_to_db(path)
|
add_path_to_db(session, path)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__' and '__file__' in globals():
|
if __name__ == '__main__' and '__file__' in globals():
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user