Compare commits

..

No commits in common. "ad717aeb2cc7f930b3aa4d2340ffa8ceb662b220" and "326dc3655e0f84321eebcd17f62c257152c068b0" have entirely different histories.

4 changed files with 298 additions and 336 deletions

View File

@ -31,8 +31,9 @@ engine = sqlalchemy.create_engine(f"{Config.MYSQL_CONNECT}?charset=utf8",
Base = declarative_base()
Base.metadata.create_all(engine)
# Create a Session factory
# Create a Session
Session = sessionmaker(bind=engine)
session = Session()
# Database classes
@ -51,7 +52,7 @@ class Notes(Base):
)
@staticmethod
def add_note(session, playlist_id, row, text):
def add_note(playlist_id, row, text):
DEBUG(f"add_note(playlist_id={playlist_id}, row={row}, text={text})")
note = Notes()
note.playlist_id = playlist_id
@ -62,19 +63,18 @@ class Notes(Base):
return note
@staticmethod
def delete_note(session, id):
def delete_note(id):
DEBUG(f"delete_note(id={id}")
session.query(Notes).filter(Notes.id == id).delete()
session.commit()
# Not currently used 1 June 2021
# @staticmethod
# def get_note(session, id):
# return session.query(Notes).filter(Notes.id == id).one()
@staticmethod
def get_note(id):
return session.query(Notes).filter(Notes.id == id).one()
@classmethod
def update_note(cls, session, id, row, text=None):
def update_note(cls, id, row, text=None):
"""
Update note details. If text=None, don't change text.
"""
@ -97,7 +97,7 @@ class Playdates(Base):
tracks = relationship("Tracks", back_populates="playdates")
@staticmethod
def add_playdate(session, track):
def add_playdate(track):
DEBUG(f"add_playdate(track={track})")
pd = Playdates()
pd.lastplayed = datetime.now()
@ -167,7 +167,7 @@ class Playlists(Base):
return (f"<Playlist(id={self.id}, name={self.name}>")
@staticmethod
def new(session, name):
def new(name):
DEBUG(f"Playlists.new(name={name})")
playlist = Playlists()
playlist.name = name
@ -176,7 +176,7 @@ class Playlists(Base):
return playlist
@staticmethod
def open(session, plid):
def open(plid):
"Record playlist as loaded and used now"
p = session.query(Playlists).filter(Playlists.id == plid).one()
@ -186,15 +186,14 @@ class Playlists(Base):
return p
def close(self, session):
def close(self):
"Record playlist as no longer loaded"
self.loaded = False
session.add(self)
session.commit()
@staticmethod
def get_last_used(session):
def get_last_used():
"""
Return a list of playlists marked "loaded", ordered by loaded date.
"""
@ -206,13 +205,13 @@ class Playlists(Base):
).all()
@staticmethod
def get_all_playlists(session):
def get_all_playlists():
"Returns a list of all playlists"
return session.query(Playlists).all()
@staticmethod
def get_all_closed_playlists(session):
def get_all_closed_playlists():
"Returns a list of all playlists not currently open"
return (
@ -224,26 +223,25 @@ class Playlists(Base):
.order_by(Playlists.last_used.desc())
).all()
# Not currently used 1 June 2021
# @staticmethod
# def get_name(session, plid):
# """
# Return name of playlist with id 'plid'
# """
@staticmethod
def get_name(plid):
"""
Return name of playlist with id 'plid'
"""
# return (
# session.query(Playlists.name)
# .filter(Playlists.id == plid)
# ).one()[0]
return (
session.query(Playlists.name)
.filter(Playlists.id == plid)
).one()[0]
def add_track(self, session, track, row=None):
def add_track(self, track, row=None):
"""
Add track to playlist at given row.
If row=None, add to end of playlist
"""
if not row:
row = PlaylistTracks.new_row(session, self.id)
row = PlaylistTracks.new_row(self.id)
glue = PlaylistTracks(row=row)
glue.track_id = track.id
@ -267,14 +265,14 @@ class PlaylistTracks(Base):
playlists = relationship("Playlists", back_populates="tracks")
@staticmethod
def new_row(session, playlist_id):
def new_row(playlist_id):
"Return row number > largest existing row number"
last_row = session.query(func.max(PlaylistTracks.row)).one()[0]
return last_row + 1
@staticmethod
def add_track(session, playlist_id, track_id, row):
def add_track(playlist_id, track_id, row):
DEBUG(
f"PlaylistTracks.add_track(playlist_id={playlist_id}, "
f"track_id={track_id}, row={row})"
@ -287,7 +285,7 @@ class PlaylistTracks(Base):
session.commit()
@staticmethod
def move_track(session, from_playlist_id, row, to_playlist_id):
def move_track(from_playlist_id, row, to_playlist_id):
DEBUG(
"PlaylistTracks.move_tracks(from_playlist_id="
f"{from_playlist_id}, row={row}, "
@ -306,7 +304,7 @@ class PlaylistTracks(Base):
session.commit()
@staticmethod
def remove_track(session, playlist_id, row):
def remove_track(playlist_id, row):
DEBUG(
f"PlaylistTracks.remove_track(playlist_id={playlist_id}, "
f"row={row})"
@ -318,7 +316,7 @@ class PlaylistTracks(Base):
session.commit()
@staticmethod
def update_row_track(session, playlist_id, row, track_id):
def update_row_track(playlist_id, row, track_id):
DEBUG(
f"PlaylistTracks.update_track_row(playlist_id={playlist_id}, "
f"row={row}, track_id={track_id})"
@ -336,13 +334,6 @@ class PlaylistTracks(Base):
f"PlaylistTracks.row == {row}"
)
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
session.commit()
@ -358,7 +349,7 @@ class Settings(Base):
f_string = Column(String(128), default=None, nullable=True)
@classmethod
def get_int(cls, session, name):
def get_int(cls, name):
try:
int_setting = session.query(cls).filter(
cls.name == name).one()
@ -370,7 +361,7 @@ class Settings(Base):
session.commit()
return int_setting
def update(self, session, data):
def update(self, data):
for key, value in data.items():
assert hasattr(self, key)
setattr(self, key, value)
@ -400,7 +391,7 @@ class Tracks(Base):
)
@classmethod
def get_or_create(cls, session, path):
def get_or_create(cls, path):
DEBUG(f"Tracks.get_or_create(path={path})")
try:
track = session.query(cls).filter(cls.path == path).one()
@ -411,7 +402,7 @@ class Tracks(Base):
return track
@staticmethod
def get_duration(session, id):
def get_duration(id):
try:
return session.query(
Tracks.duration).filter(Tracks.id == id).one()[0]
@ -420,20 +411,19 @@ class Tracks(Base):
return None
@staticmethod
def get_all_paths(session):
def get_all_paths():
"Return a list of paths of all tracks"
return [a[0] for a in session.query(Tracks.path).all()]
# Not used as of 1 June 2021
# @classmethod
# def get_all_tracks(cls):
# "Return a list of all tracks"
@classmethod
def get_all_tracks(cls):
"Return a list of all tracks"
# return session.query(cls).all()
return session.query(cls).all()
@staticmethod
def get_path(session, id):
def get_path(id):
try:
return session.query(Tracks.path).filter(Tracks.id == id).one()[0]
except NoResultFound:
@ -441,7 +431,7 @@ class Tracks(Base):
return None
@staticmethod
def get_track(session, id):
def get_track(id):
try:
DEBUG(f"Tracks.get_track(track_id={id})")
track = session.query(Tracks).filter(Tracks.id == id).one()
@ -451,7 +441,7 @@ class Tracks(Base):
return None
@staticmethod
def search_titles(session, text):
def search_titles(text):
return (
session.query(Tracks)
.filter(Tracks.title.ilike(f"%{text}%"))
@ -459,7 +449,7 @@ class Tracks(Base):
).all()
@staticmethod
def track_from_id(session, id):
def track_from_id(id):
return session.query(Tracks).filter(
Tracks.id == id).one()

View File

@ -25,8 +25,7 @@ import helpers
import music
from config import Config
from model import (Notes, Playdates, Playlists, PlaylistTracks,
Session, Settings, Tracks)
from model import Notes, Playdates, Playlists, PlaylistTracks, Settings, Tracks
from playlists import Playlist
from songdb import add_path_to_db
from ui.dlg_search_database_ui import Ui_Dialog
@ -71,23 +70,21 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.setNameFilter("Music files (*.flac *.mp3)")
if dlg.exec_():
with Session() as session:
for fname in dlg.selectedFiles():
track = add_path_to_db(session, fname)
self.visible_playlist()._add_to_playlist(track)
for fname in dlg.selectedFiles():
track = add_path_to_db(fname)
self.visible_playlist()._add_to_playlist(track)
def set_main_window_size(self):
with Session() as session:
record = Settings.get_int(session, "mainwindow_x")
x = record.f_int or 1
record = Settings.get_int(session, "mainwindow_y")
y = record.f_int or 1
record = Settings.get_int(session, "mainwindow_width")
width = record.f_int or 1599
record = Settings.get_int(session, "mainwindow_height")
height = record.f_int or 981
self.setGeometry(x, y, width, height)
record = Settings.get_int("mainwindow_x")
x = record.f_int or 1
record = Settings.get_int("mainwindow_y")
y = record.f_int or 1
record = Settings.get_int("mainwindow_width")
width = record.f_int or 1599
record = Settings.get_int("mainwindow_height")
height = record.f_int or 981
self.setGeometry(x, y, width, height)
def clear_selection(self):
if self.visible_playlist():
@ -103,22 +100,21 @@ class Window(QMainWindow, Ui_MainWindow):
else:
DEBUG("closeEvent() accepted")
with Session() as session:
record = Settings.get_int(session, "mainwindow_height")
if record.f_int != self.height():
record.update(session, {'f_int': self.height()})
record = Settings.get_int("mainwindow_height")
if record.f_int != self.height():
record.update({'f_int': self.height()})
record = Settings.get_int(session, "mainwindow_width")
if record.f_int != self.width():
record.update(session, {'f_int': self.width()})
record = Settings.get_int("mainwindow_width")
if record.f_int != self.width():
record.update({'f_int': self.width()})
record = Settings.get_int(session, "mainwindow_x")
if record.f_int != self.x():
record.update(session, {'f_int': self.x()})
record = Settings.get_int("mainwindow_x")
if record.f_int != self.x():
record.update({'f_int': self.x()})
record = Settings.get_int(session, "mainwindow_y")
if record.f_int != self.y():
record.update(session, {'f_int': self.y()})
record = Settings.get_int("mainwindow_y")
if record.f_int != self.y():
record.update({'f_int': self.y()})
event.accept()
@ -163,9 +159,8 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.resize(500, 100)
ok = dlg.exec()
if ok:
with Session() as session:
playlist = Playlists.new(session, dlg.textValue())
self.load_playlist(session, playlist)
playlist = Playlists.new(dlg.textValue())
self.load_playlist(playlist)
def change_volume(self, volume):
"Change player maximum volume"
@ -175,12 +170,11 @@ class Window(QMainWindow, Ui_MainWindow):
self.music.set_volume(volume)
def close_playlist(self):
with Session() as session:
self.visible_playlist().db.close(session)
index = self.tabPlaylist.currentIndex()
self.tabPlaylist.removeTab(index)
self.visible_playlist().db.close()
index = self.tabPlaylist.currentIndex()
self.tabPlaylist.removeTab(index)
def create_note(self, session, text):
def create_note(self, text):
"""
Create note
@ -195,10 +189,7 @@ class Window(QMainWindow, Ui_MainWindow):
row = self.visible_playlist().rowCount()
DEBUG(f"musicmuster.create_note(text={text}): row={row}")
note = Notes.add_note(
session, self.visible_playlist().db.id, row, text)
return note
return Notes.add_note(self.visible_playlist().db.id, row, text)
def disable_play_next_controls(self):
DEBUG("disable_play_next_controls()")
@ -259,21 +250,19 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.resize(500, 100)
ok = dlg.exec()
if ok:
with Session() as session:
note = self.create_note(session, dlg.textValue())
self.visible_playlist().add_note(note)
note = self.create_note(dlg.textValue())
self.visible_playlist().add_note(note)
def load_last_playlists(self):
"Load the playlists that we loaded at end of last session"
with Session() as session:
for playlist in Playlists.get_last_used(session):
DEBUG(
f"load_last_playlists(), playlist.name={playlist.name}, "
f"playlist.id={playlist.id}")
self.load_playlist(session, playlist)
for playlist in Playlists.get_last_used():
DEBUG(
f"load_last_playlists(), playlist.name={playlist.name}, "
f"playlist.id={playlist.id}")
self.load_playlist(playlist)
def load_playlist(self, session, playlist_db):
def load_playlist(self, playlist_db):
"""
Take the passed database object, create a playlist display, attach
the database object, get it populated and then add tab.
@ -281,43 +270,40 @@ class Window(QMainWindow, Ui_MainWindow):
playlist_table = Playlist(self)
playlist_table.db = playlist_db
playlist_table.populate(session)
playlist_table.populate()
idx = self.tabPlaylist.addTab(playlist_table, playlist_db.name)
self.tabPlaylist.setCurrentIndex(idx)
def move_selected(self):
"Move selected rows to another playlist"
with Session() as session:
playlists = list(
set(Playlists.get_all_playlists(session)) -
{self.visible_playlist().db}
)
dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec()
if not dlg.plid:
return
playlists = list(
set(Playlists.get_all_playlists()) - {self.visible_playlist().db}
)
dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec()
if not dlg.plid:
return
# If destination playlist is visible, we need to add the moved
# tracks to it. If not, they will be automatically loaded when
# the playlistis opened.
destination_playlist = None
for tab in range(self.tabPlaylist.count()):
if self.tabPlaylist.widget(tab).db.id == dlg.plid:
destination_playlist = self.tabPlaylist.widget(tab)
break
# If destination playlist is visible, we need to add the moved
# tracks to it. If not, they will be automatically loaded when
# the playlistis opened.
destination_playlist = None
for tab in range(self.tabPlaylist.count()):
if self.tabPlaylist.widget(tab).db.id == dlg.plid:
destination_playlist = self.tabPlaylist.widget(tab)
break
rows = []
for (row, track_id) in (
self.visible_playlist().get_selected_rows_and_tracks()):
rows.append(row)
# Update database
PlaylistTracks.move_track(
session, self.visible_playlist().db.id, row, dlg.plid)
# Update destination playlist if visible
if destination_playlist:
destination_playlist.add_track(
session, Tracks.track_from_id(session, track_id))
rows = []
for (row, track_id) in (
self.visible_playlist().get_selected_rows_and_tracks()):
rows.append(row)
# Update database
PlaylistTracks.move_track(
self.visible_playlist().db.id, row, dlg.plid)
# Update destination playlist if visible
if destination_playlist:
destination_playlist.add_track(Tracks.track_from_id(track_id))
# Update source playlist
self.visible_playlist().remove_rows(rows)
@ -348,47 +334,46 @@ class Window(QMainWindow, Ui_MainWindow):
f"{self.current_track.title if self.current_track else None}"
)
with Session() as session:
# Stop current track, if any
self.stop_playing()
# Stop current track, if any
self.stop_playing()
# Play next track
self.current_track = self.next_track
self.current_track_playlist = self.next_track_playlist
self.next_track = None
self.next_track_playlist = None
self.music.play(self.current_track.path)
# Play next track
self.current_track = self.next_track
self.current_track_playlist = self.next_track_playlist
self.next_track = None
self.next_track_playlist = None
self.music.play(self.current_track.path)
# Update metadata
next_track_id = self.current_track_playlist.play_started()
# Update metadata
next_track_id = self.current_track_playlist.play_started()
if next_track_id is not None:
self.next_track = Tracks.get_track(session, next_track_id)
self.next_track_playlist = self.current_track_playlist
# Check we can read it
if not os.access(self.next_track.path, os.R_OK):
self.show_warning(
"Can't read next track",
self.next_track.path)
else:
self.next_track = self.next_track_playlist = None
if next_track_id is not None:
self.next_track = Tracks.get_track(next_track_id)
self.next_track_playlist = self.current_track_playlist
# Check we can read it
if not os.access(self.next_track.path, os.R_OK):
self.show_warning(
"Can't read next track",
self.next_track.path)
else:
self.next_track = self.next_track_playlist = None
# Tell database to record it as played
self.current_track.update_lastplayed()
Playdates.add_playdate(session, self.current_track)
# Tell database to record it as played
self.current_track.update_lastplayed()
Playdates.add_playdate(self.current_track)
self.disable_play_next_controls()
self.update_headers()
self.disable_play_next_controls()
self.update_headers()
# Set time clocks
now = datetime.now()
self.label_start_tod.setText(now.strftime("%H:%M:%S"))
silence_at = self.current_track.silence_at
silence_time = now + timedelta(milliseconds=silence_at)
self.label_silent_tod.setText(silence_time.strftime("%H:%M:%S"))
self.label_fade_length.setText(helpers.ms_to_mmss(
silence_at - self.current_track.fade_at
))
# Set time clocks
now = datetime.now()
self.label_start_tod.setText(now.strftime("%H:%M:%S"))
silence_at = self.current_track.silence_at
silence_time = now + timedelta(milliseconds=silence_at)
self.label_silent_tod.setText(silence_time.strftime("%H:%M:%S"))
self.label_fade_length.setText(helpers.ms_to_mmss(
silence_at - self.current_track.fade_at
))
def play_previous(self):
"Resume playing last track"
@ -401,13 +386,12 @@ class Window(QMainWindow, Ui_MainWindow):
dlg.exec()
def open_playlist(self):
with Session() as session:
playlists = Playlists.get_all_closed_playlists(session)
dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec()
if dlg.plid:
playlist = Playlists.open(session, dlg.plid)
self.load_playlist(session, playlist)
playlists = Playlists.get_all_closed_playlists()
dlg = SelectPlaylistDialog(self, playlists=playlists)
dlg.exec()
if dlg.plid:
playlist = Playlists.open(dlg.plid)
self.load_playlist(playlist)
def select_next_track(self):
"Select next or first track in playlist"
@ -422,16 +406,15 @@ class Window(QMainWindow, Ui_MainWindow):
def set_next_track(self, next_track_id=None):
"Set selected track as next"
with Session() as session:
if not next_track_id:
next_track_id = self.visible_playlist().set_selected_as_next()
if next_track_id:
if self.next_track_playlist != self.visible_playlist():
if self.next_track_playlist:
self.next_track_playlist.clear_next()
self.next_track_playlist = self.visible_playlist()
self.next_track = Tracks.get_track(session, next_track_id)
self.update_headers()
if not next_track_id:
next_track_id = self.visible_playlist().set_selected_as_next()
if next_track_id:
if self.next_track_playlist != self.visible_playlist():
if self.next_track_playlist:
self.next_track_playlist.clear_next()
self.next_track_playlist = self.visible_playlist()
self.next_track = Tracks.get_track(next_track_id)
self.update_headers()
def show_warning(self, title, msg):
"Display a warning to user"
@ -622,7 +605,6 @@ class Window(QMainWindow, Ui_MainWindow):
class DbDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.session = Session()
self.ui = Ui_Dialog()
self.ui.setupUi(self)
self.ui.searchString.textEdited.connect(self.chars_typed)
@ -632,20 +614,20 @@ class DbDialog(QDialog):
self.ui.btnClose.clicked.connect(self.close)
self.ui.matchList.itemSelectionChanged.connect(self.selection_changed)
record = Settings.get_int(self.session, "dbdialog_width")
record = Settings.get_int("dbdialog_width")
width = record.f_int or 800
record = Settings.get_int(self.session, "dbdialog_height")
record = Settings.get_int("dbdialog_height")
height = record.f_int or 600
self.resize(width, height)
def __del__(self):
record = Settings.get_int(self.session, "dbdialog_height")
record = Settings.get_int("dbdialog_height")
if record.f_int != self.height():
record.update(self.session, {'f_int': self.height()})
record.update({'f_int': self.height()})
record = Settings.get_int(self.session, "dbdialog_width")
record = Settings.get_int("dbdialog_width")
if record.f_int != self.width():
record.update(self.session, {'f_int': self.width()})
record.update({'f_int': self.width()})
def add_selected(self):
if not self.ui.matchList.selectedItems():
@ -661,7 +643,7 @@ class DbDialog(QDialog):
def chars_typed(self, s):
if len(s) >= 3:
matches = Tracks.search_titles(self.session, s)
matches = Tracks.search_titles(s)
self.ui.matchList.clear()
if matches:
for track in matches:
@ -680,7 +662,7 @@ class DbDialog(QDialog):
self.select_searchtext()
def add_track(self, track_id):
track = Tracks.track_from_id(self.session, track_id)
track = Tracks.track_from_id(track_id)
self.parent().visible_playlist()._add_to_playlist(track)
# Select search text to make it easier for next search
self.select_searchtext()
@ -695,7 +677,7 @@ class DbDialog(QDialog):
item = self.ui.matchList.currentItem()
track_id = item.data(Qt.UserRole)
self.ui.dbPath.setText(Tracks.get_path(self.session, track_id))
self.ui.dbPath.setText(Tracks.get_path(track_id))
class SelectPlaylistDialog(QDialog):
@ -711,12 +693,11 @@ class SelectPlaylistDialog(QDialog):
self.ui.buttonBox.rejected.connect(self.close)
self.plid = None
with Session() as session:
record = Settings.get_int(session, "select_playlist_dialog_width")
width = record.f_int or 800
record = Settings.get_int(session, "select_playlist_dialog_height")
height = record.f_int or 600
self.resize(width, height)
record = Settings.get_int("select_playlist_dialog_width")
width = record.f_int or 800
record = Settings.get_int("select_playlist_dialog_height")
height = record.f_int or 600
self.resize(width, height)
for (plid, plname) in [(a.id, a.name) for a in playlists]:
p = QListWidgetItem()
@ -725,14 +706,13 @@ class SelectPlaylistDialog(QDialog):
self.ui.lstPlaylists.addItem(p)
def __del__(self):
with Session() as session:
record = Settings.get_int(session, "select_playlist_dialog_height")
if record.f_int != self.height():
record.update(session, {'f_int': self.height()})
record = Settings.get_int("select_playlist_dialog_height")
if record.f_int != self.height():
record.update({'f_int': self.height()})
record = Settings.get_int(session, "select_playlist_dialog_width")
if record.f_int != self.width():
record.update(session, {'f_int': self.width()})
record = Settings.get_int("select_playlist_dialog_width")
if record.f_int != self.width():
record.update({'f_int': self.width()})
def list_doubleclick(self, entry):
self.plid = entry.data(Qt.UserRole)

View File

@ -17,7 +17,7 @@ import os
from config import Config
from datetime import datetime, timedelta
from log import DEBUG, ERROR
from model import Notes, PlaylistTracks, Session, Settings, Tracks
from model import Notes, PlaylistTracks, Settings, Tracks
class Playlist(QTableWidget):
@ -94,10 +94,9 @@ class Playlist(QTableWidget):
for column in range(self.columnCount()):
width = self.columnWidth(column)
name = f"playlist_col_{str(column)}_width"
with Session() as session:
record = Settings.get_int(session, name)
if record.f_int != self.columnWidth(column):
record.update(session, {'f_int': width})
record = Settings.get_int(name)
if record.f_int != self.columnWidth(column):
record.update({'f_int': width})
event.accept()
@ -276,7 +275,7 @@ class Playlist(QTableWidget):
if not self.selectionModel().hasSelection():
return None
else:
return self.selectionModel().selectedRows()[0].row()
return self.selectionModel().selectedRows()[0]
def get_selected_rows_and_tracks(self):
"Return a list of selected (rows, track_id) tuples"
@ -337,7 +336,7 @@ class Playlist(QTableWidget):
self.current_track_start_time = None
self._repaint(save_playlist=False)
def populate(self, session):
def populate(self):
# add them in row order. We don't mandate that an item will be
# on its specified row, only that it will be above
# larger-numbered row items, and below lower-numbered ones.
@ -455,7 +454,7 @@ class Playlist(QTableWidget):
elif isinstance(data, Notes):
self.add_note(data, repaint=repaint)
def _calculate_next_start_time(self, session, row, start):
def _calculate_next_start_time(self, row, start):
"Return this row's end time given its start time"
if start is None:
@ -464,7 +463,7 @@ class Playlist(QTableWidget):
DEBUG("_calculate_next_start_time() called with row=None")
return None
duration = Tracks.get_duration(session, self._get_row_id(row))
duration = Tracks.get_duration(self._get_row_id(row))
return start + timedelta(milliseconds=duration)
def _can_read_track(self, track):
@ -500,13 +499,12 @@ class Playlist(QTableWidget):
msg.setDefaultButton(QMessageBox.Cancel)
msg.setWindowTitle("Delete row")
if msg.exec() == QMessageBox.Yes:
with Session() as session:
id = self._get_row_id(row)
if row in self._meta_get_notes():
Notes.delete_note(session, id)
else:
PlaylistTracks.remove_track(session, self.db.id, row)
self.removeRow(row)
id = self._get_row_id(row)
if row in self._meta_get_notes():
Notes.delete_note(id)
else:
PlaylistTracks.remove_track(self.db.id, row)
self.removeRow(row)
self._repaint()
@ -725,88 +723,86 @@ class Playlist(QTableWidget):
f"save_playlist={save_playlist})"
)
with Session() as session:
if clear_selection:
self.clearSelection()
if save_playlist:
self._save_playlist(session)
if clear_selection:
self.clearSelection()
if save_playlist:
self._save_playlist()
current = self._meta_get_current()
next = self._meta_get_next()
notes = self._meta_get_notes()
current = self._meta_get_current()
next = self._meta_get_next()
notes = self._meta_get_notes()
# Set colours and start times
next_start_time = None
# Set colours and start times
next_start_time = None
# Cycle through all rows
for row in range(self.rowCount()):
# 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
# current track.
# Cycle through all rows
for row in range(self.rowCount()):
# 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
# current track.
if row in notes:
row_time = self._get_row_time(row)
if row_time:
next_start_time = row_time
# Set colour
self._set_row_colour(
row, QColor(Config.COLOUR_NOTES_PLAYLIST)
)
self._set_row_bold(row)
if row in notes:
row_time = self._get_row_time(row)
if row_time:
next_start_time = row_time
# Set colour
self._set_row_colour(
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(
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)
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:
# 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)
# 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)
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)
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)
def _save_playlist(self, session):
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)
def _save_playlist(self):
"""
Save playlist to database. We do this by correcting differences
between the on-screen (definitive) playlist and that in the
@ -850,7 +846,7 @@ class Playlist(QTableWidget):
f"_save_playlist(): Delete note note_id={note_id} "
f"from playlist {self} in database"
)
Notes.delete_note(session, note_id)
Notes.delete_note(note_id)
# Note rows to update in playlist database
for note_id in set(playlist_notes.keys()) & set(database_notes.keys()):
@ -860,7 +856,7 @@ class Playlist(QTableWidget):
f"from row={database_notes[note_id]} to "
f"row={playlist_notes[note_id]}"
)
Notes.update_note(session, note_id, playlist_notes[note_id])
Notes.update_note(note_id, playlist_notes[note_id])
# Now check tracks
# Create dictionaries indexed by row
@ -882,8 +878,7 @@ class Playlist(QTableWidget):
set(set(playlist_tracks.keys()) - set(database_tracks.keys()))
):
DEBUG(f"_save_playlist(): row {row} missing from database")
PlaylistTracks.add_track(session, self.db.id,
playlist_tracks[row], row)
PlaylistTracks.add_track(self.db.id, playlist_tracks[row], row)
# Track rows to remove from database
for row in (
@ -894,7 +889,7 @@ class Playlist(QTableWidget):
f"_save_playlist(): row {row} in database not playlist "
f"(track={track})"
)
PlaylistTracks.remove_track(session, self.db.id, row)
PlaylistTracks.remove_track(self.db.id, row)
# Track rows to update in database
for row in (
@ -907,21 +902,20 @@ class Playlist(QTableWidget):
f"to track={playlist_tracks[row]}"
)
PlaylistTracks.update_row_track(
session, self.db.id, row, playlist_tracks[row])
self.db.id, row, playlist_tracks[row])
def _set_column_widths(self):
# Column widths from settings
with Session() as session:
for column in range(self.columnCount()):
# Only show column 0 in test mode
if (column == 0 and not Config.TESTMODE):
self.setColumnWidth(0, 0)
else:
name = f"playlist_col_{str(column)}_width"
record = Settings.get_int(session, name)
if record.f_int is not None:
self.setColumnWidth(column, record.f_int)
for column in range(self.columnCount()):
# Only show column 0 in test mode
if (column == 0 and not Config.TESTMODE):
self.setColumnWidth(0, 0)
else:
name = f"playlist_col_{str(column)}_width"
record = Settings.get_int(name)
if record.f_int is not None:
self.setColumnWidth(column, record.f_int)
def _set_row_bold(self, row, bold=True):
boldfont = QFont()

View File

@ -7,7 +7,7 @@ import tempfile
from config import Config
from log import DEBUG, INFO
from model import Tracks, Session
from model import Tracks, session
from mutagen.flac import FLAC
from pydub import AudioSegment, effects
from tinytag import TinyTag
@ -28,16 +28,15 @@ def main():
# Run as required
if args.update:
INFO("Updating database")
with Session() as session:
update_db(session)
update_db()
INFO("Finished")
def add_path_to_db(session, path):
def add_path_to_db(path):
"Add passed path to database along with metadata"
track = Tracks.get_or_create(session, path)
track = Tracks.get_or_create(path)
tag = TinyTag.get(path)
audio = get_audio_segment(path)
@ -144,20 +143,19 @@ def fade_point(audio_segment, fade_threshold=Config.DBFS_FADE,
return int(trim_ms)
# Current unused (1 June 2021)
# def rescan_database(session):
#
# tracks = Tracks.get_all_tracks(session)
# total_tracks = len(tracks)
# track_count = 0
# for track in tracks:
# track_count += 1
# print(f"Track {track_count} of {total_tracks}")
# audio = get_audio_segment(track.path)
# track.start_gap = leading_silence(audio)
# track.fade_at = fade_point(audio)
# track.silence_at = trailing_silence(audio)
# session.commit()
def rescan_database():
tracks = Tracks.get_all_tracks()
total_tracks = len(tracks)
track_count = 0
for track in tracks:
track_count += 1
print(f"Track {track_count} of {total_tracks}")
audio = get_audio_segment(track.path)
track.start_gap = leading_silence(audio)
track.fade_at = fade_point(audio)
track.silence_at = trailing_silence(audio)
session.commit()
def trailing_silence(audio_segment, silence_threshold=-50.0,
@ -165,14 +163,14 @@ def trailing_silence(audio_segment, silence_threshold=-50.0,
return fade_point(audio_segment, silence_threshold, chunk_size)
def update_db(session):
def update_db():
"""
Repopulate database
"""
# Search for tracks in only one of directory and database
db_paths = set(Tracks.get_all_paths(session))
db_paths = set(Tracks.get_all_paths())
os_paths_list = []
for root, dirs, files in os.walk(Config.ROOT):
@ -190,7 +188,7 @@ def update_db(session):
for path in list(os_paths - db_paths):
# TODO
INFO(f"Adding to dataabase: {path}")
add_path_to_db(session, path)
add_path_to_db(path)
if __name__ == '__main__' and '__file__' in globals():