Compare commits
No commits in common. "fd0d3e6e1fa0b251b950d4c42b8bb96fe55003b3" and "0fe26e8a75eea269d2077a6e8d39b5bedbe38e8a" have entirely different histories.
fd0d3e6e1f
...
0fe26e8a75
@ -46,7 +46,6 @@ elif MM_ENV == 'DEVELOPMENT':
|
|||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown MusicMuster environment: {MM_ENV=}")
|
raise ValueError(f"Unknown MusicMuster environment: {MM_ENV=}")
|
||||||
|
|
||||||
DEBUG(f"Using {dbname} database", True)
|
|
||||||
MYSQL_CONNECT = f"mysql+mysqldb://{dbuser}:{dbpw}@{dbhost}/{dbname}"
|
MYSQL_CONNECT = f"mysql+mysqldb://{dbuser}:{dbpw}@{dbhost}/{dbname}"
|
||||||
|
|
||||||
engine = sqlalchemy.create_engine(
|
engine = sqlalchemy.create_engine(
|
||||||
|
|||||||
@ -59,7 +59,7 @@ def log_uncaught_exceptions(ex_cls, ex, tb):
|
|||||||
sys.excepthook = log_uncaught_exceptions
|
sys.excepthook = log_uncaught_exceptions
|
||||||
|
|
||||||
|
|
||||||
def DEBUG(msg, force_stderr=True):
|
def DEBUG(msg, force_stderr=False):
|
||||||
"""
|
"""
|
||||||
Outupt a log message at level DEBUG. If force_stderr is True,
|
Outupt a log message at level DEBUG. If force_stderr is True,
|
||||||
output this message to stderr regardless of default stderr level
|
output this message to stderr regardless of default stderr level
|
||||||
|
|||||||
@ -129,8 +129,8 @@ class Notes(Base):
|
|||||||
row: int = Column(Integer, nullable=False)
|
row: int = Column(Integer, nullable=False)
|
||||||
note: str = Column(String(256), index=False)
|
note: str = Column(String(256), index=False)
|
||||||
|
|
||||||
def __init__(self, session: Session, playlist_id: int,
|
def __init__(
|
||||||
row: int, text: str) -> None:
|
self, session: Session, playlist_id: int, row: int, text: str) -> None:
|
||||||
"""Create note"""
|
"""Create note"""
|
||||||
|
|
||||||
DEBUG(f"Notes.__init__({playlist_id=}, {row=}, {text=})")
|
DEBUG(f"Notes.__init__({playlist_id=}, {row=}, {text=})")
|
||||||
@ -257,6 +257,11 @@ class Playlists(Base):
|
|||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<Playlists(id={self.id}, name={self.name}>"
|
return f"<Playlists(id={self.id}, name={self.name}>"
|
||||||
|
|
||||||
|
def add_note(self, session: Session, row: int, text: str) -> Notes:
|
||||||
|
"""Add note to playlist at passed row"""
|
||||||
|
|
||||||
|
return Notes(session, self.id, row, text)
|
||||||
|
|
||||||
def add_track(
|
def add_track(
|
||||||
self, session: Session, track_id: int,
|
self, session: Session, track_id: int,
|
||||||
row: Optional[int] = None) -> None:
|
row: Optional[int] = None) -> None:
|
||||||
@ -490,8 +495,8 @@ class Tracks(Base):
|
|||||||
path: str,
|
path: str,
|
||||||
title: Optional[str] = None,
|
title: Optional[str] = None,
|
||||||
artist: Optional[str] = None,
|
artist: Optional[str] = None,
|
||||||
duration: int = 0,
|
duration: Optional[int] = None,
|
||||||
start_gap: int = 0,
|
start_gap: Optional[int] = None,
|
||||||
fade_at: Optional[int] = None,
|
fade_at: Optional[int] = None,
|
||||||
silence_at: Optional[int] = None,
|
silence_at: Optional[int] = None,
|
||||||
mtime: Optional[float] = None,
|
mtime: Optional[float] = None,
|
||||||
@ -647,6 +652,5 @@ class Tracks(Base):
|
|||||||
session.add(self)
|
session.add(self)
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
def update_path(self, session, newpath: str) -> None:
|
def update_path(self, newpath: str) -> None:
|
||||||
self.path = newpath
|
self.path = newpath
|
||||||
session.commit()
|
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os.path
|
import os.path
|
||||||
import psutil
|
import psutil
|
||||||
import sys
|
import sys
|
||||||
@ -21,7 +20,6 @@ from PyQt5.QtWidgets import (
|
|||||||
QFileDialog,
|
QFileDialog,
|
||||||
QInputDialog,
|
QInputDialog,
|
||||||
QLabel,
|
QLabel,
|
||||||
QLineEdit,
|
|
||||||
QListWidgetItem,
|
QListWidgetItem,
|
||||||
QMainWindow,
|
QMainWindow,
|
||||||
)
|
)
|
||||||
@ -40,7 +38,7 @@ from ui.dlg_search_database_ui import Ui_Dialog
|
|||||||
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
|
from ui.dlg_SelectPlaylist_ui import Ui_dlgSelectPlaylist
|
||||||
from ui.downloadcsv_ui import Ui_DateSelect
|
from ui.downloadcsv_ui import Ui_DateSelect
|
||||||
from ui.main_window_ui import Ui_MainWindow
|
from ui.main_window_ui import Ui_MainWindow
|
||||||
from utilities import create_track_from_file, update_db
|
from utilities import create_track_from_file
|
||||||
|
|
||||||
|
|
||||||
class TrackData:
|
class TrackData:
|
||||||
@ -65,6 +63,7 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.timer: QTimer = QTimer()
|
self.timer: QTimer = QTimer()
|
||||||
self.even_tick: bool = True
|
self.even_tick: bool = True
|
||||||
self.playing: bool = False
|
self.playing: bool = False
|
||||||
|
self.connect_signals_slots()
|
||||||
self.disable_play_next_controls()
|
self.disable_play_next_controls()
|
||||||
|
|
||||||
self.music: music.Music = music.Music()
|
self.music: music.Music = music.Music()
|
||||||
@ -80,9 +79,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.set_main_window_size()
|
self.set_main_window_size()
|
||||||
self.lblSumPlaytime: QLabel = QLabel("")
|
self.lblSumPlaytime: QLabel = QLabel("")
|
||||||
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
|
self.statusbar.addPermanentWidget(self.lblSumPlaytime)
|
||||||
self.txtSearch = QLineEdit()
|
|
||||||
self.statusbar.addWidget(self.txtSearch)
|
|
||||||
self.txtSearch.setHidden(True)
|
|
||||||
|
|
||||||
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
|
self.visible_playlist_tab: Callable[[], PlaylistTab] = \
|
||||||
self.tabPlaylist.currentWidget
|
self.tabPlaylist.currentWidget
|
||||||
@ -91,7 +87,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.enable_play_next_controls()
|
self.enable_play_next_controls()
|
||||||
self.check_audacity()
|
self.check_audacity()
|
||||||
self.timer.start(Config.TIMER_MS)
|
self.timer.start(Config.TIMER_MS)
|
||||||
self.connect_signals_slots()
|
|
||||||
|
|
||||||
def set_main_window_size(self) -> None:
|
def set_main_window_size(self) -> None:
|
||||||
"""Set size of window from database"""
|
"""Set size of window from database"""
|
||||||
@ -187,7 +182,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.actionNewPlaylist.triggered.connect(self.create_playlist)
|
self.actionNewPlaylist.triggered.connect(self.create_playlist)
|
||||||
self.actionOpenPlaylist.triggered.connect(self.open_playlist)
|
self.actionOpenPlaylist.triggered.connect(self.open_playlist)
|
||||||
self.actionPlay_next.triggered.connect(self.play_next)
|
self.actionPlay_next.triggered.connect(self.play_next)
|
||||||
self.actionSearch.triggered.connect(self.search_playlist)
|
|
||||||
self.actionSearch_database.triggered.connect(self.search_database)
|
self.actionSearch_database.triggered.connect(self.search_database)
|
||||||
self.actionSelect_next_track.triggered.connect(self.select_next_row)
|
self.actionSelect_next_track.triggered.connect(self.select_next_row)
|
||||||
self.actionSelect_played_tracks.triggered.connect(self.select_played)
|
self.actionSelect_played_tracks.triggered.connect(self.select_played)
|
||||||
@ -209,8 +203,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
self.btnStop.clicked.connect(self.stop)
|
self.btnStop.clicked.connect(self.stop)
|
||||||
self.spnVolume.valueChanged.connect(self.change_volume)
|
self.spnVolume.valueChanged.connect(self.change_volume)
|
||||||
self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
|
self.tabPlaylist.tabCloseRequested.connect(self.close_tab)
|
||||||
self.txtSearch.returnPressed.connect(self.search_playlist_return)
|
|
||||||
self.txtSearch.textChanged.connect(self.search_playlist_update)
|
|
||||||
|
|
||||||
self.timer.timeout.connect(self.tick)
|
self.timer.timeout.connect(self.tick)
|
||||||
|
|
||||||
@ -629,27 +621,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
|||||||
dlg = DbDialog(self, session)
|
dlg = DbDialog(self, session)
|
||||||
dlg.exec()
|
dlg.exec()
|
||||||
|
|
||||||
def search_playlist(self):
|
def open_playlist(self) -> None:
|
||||||
"""Show text box to search playlist"""
|
"""Select and activate existing playlist"""
|
||||||
|
|
||||||
self.disable_play_next_controls()
|
|
||||||
self.txtSearch.setHidden(False)
|
|
||||||
self.txtSearch.setFocus()
|
|
||||||
|
|
||||||
def search_playlist_return(self):
|
|
||||||
"""Close off search box when return pressed"""
|
|
||||||
|
|
||||||
self.txtSearch.setText("")
|
|
||||||
self.txtSearch.setHidden(True)
|
|
||||||
self.enable_play_next_controls()
|
|
||||||
self.visible_playlist_tab().set_filter("")
|
|
||||||
|
|
||||||
def search_playlist_update(self):
|
|
||||||
"""Update search when search string changes"""
|
|
||||||
|
|
||||||
self.visible_playlist_tab().set_filter(self.txtSearch.text())
|
|
||||||
|
|
||||||
def open_playlist(self):
|
|
||||||
with Session() as session:
|
with Session() as session:
|
||||||
playlists = Playlists.get_closed(session)
|
playlists = Playlists.get_closed(session)
|
||||||
dlg = SelectPlaylistDialog(self, playlists=playlists,
|
dlg = SelectPlaylistDialog(self, playlists=playlists,
|
||||||
@ -1075,34 +1049,11 @@ class SelectPlaylistDialog(QDialog):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
p = argparse.ArgumentParser()
|
try:
|
||||||
# Only allow at most one option to be specified
|
Base.metadata.create_all(dbconfig.engine)
|
||||||
group = p.add_mutually_exclusive_group()
|
app = QApplication(sys.argv)
|
||||||
group.add_argument('-u', '--update',
|
win = Window()
|
||||||
action="store_true", dest="update",
|
win.show()
|
||||||
default=False, help="Update database")
|
sys.exit(app.exec())
|
||||||
# group.add_argument('-f', '--full-update',
|
except Exception:
|
||||||
# action="store_true", dest="full_update",
|
EXCEPTION("Unhandled Exception caught by musicmuster.main()")
|
||||||
# default=False, help="Update database")
|
|
||||||
# group.add_argument('-i', '--import', dest="fname", help="Input file")
|
|
||||||
args = p.parse_args()
|
|
||||||
|
|
||||||
# Run as required
|
|
||||||
if args.update:
|
|
||||||
DEBUG("Updating database")
|
|
||||||
with Session() as session:
|
|
||||||
update_db(session)
|
|
||||||
# elif args.full_update:
|
|
||||||
# DEBUG("Full update of database")
|
|
||||||
# with Session() as session:
|
|
||||||
# full_update_db(session)
|
|
||||||
else:
|
|
||||||
# Normal run
|
|
||||||
try:
|
|
||||||
Base.metadata.create_all(dbconfig.engine)
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
win = Window()
|
|
||||||
win.show()
|
|
||||||
sys.exit(app.exec())
|
|
||||||
except Exception:
|
|
||||||
EXCEPTION("Unhandled Exception caught by musicmuster.main()")
|
|
||||||
|
|||||||
@ -67,7 +67,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Qt.UserRoles
|
# Qt.UserRoles
|
||||||
ROW_METADATA = Qt.UserRole
|
ROW_METADATA = Qt.UserRole
|
||||||
CONTENT_OBJECT = Qt.UserRole + 1
|
CONTENT_OBJECT = Qt.UserRole + 1
|
||||||
ROW_DURATION = Qt.UserRole + 2
|
|
||||||
|
|
||||||
def __init__(self, musicmuster: QMainWindow, session: Session,
|
def __init__(self, musicmuster: QMainWindow, session: Session,
|
||||||
playlist_id: int, *args, **kwargs):
|
playlist_id: int, *args, **kwargs):
|
||||||
@ -136,7 +135,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
self.itemSelectionChanged.connect(self._select_event)
|
self.itemSelectionChanged.connect(self._select_event)
|
||||||
|
|
||||||
self.row_filter: Optional[str] = None
|
|
||||||
self.editing_cell: bool = False
|
self.editing_cell: bool = False
|
||||||
self.selecting_in_progress = False
|
self.selecting_in_progress = False
|
||||||
self.cellChanged.connect(self._cell_changed)
|
self.cellChanged.connect(self._cell_changed)
|
||||||
@ -360,7 +358,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
duration_item: QTableWidgetItem = QTableWidgetItem(
|
duration_item: QTableWidgetItem = QTableWidgetItem(
|
||||||
helpers.ms_to_mmss(track.duration)
|
helpers.ms_to_mmss(track.duration)
|
||||||
)
|
)
|
||||||
self._set_row_duration(row, track.duration)
|
|
||||||
self.setItem(row, self.COL_DURATION, duration_item)
|
self.setItem(row, self.COL_DURATION, duration_item)
|
||||||
|
|
||||||
last_playtime: Optional[datetime] = Playdates.last_played(
|
last_playtime: Optional[datetime] = Playdates.last_played(
|
||||||
@ -665,13 +662,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.selecting_in_progress = False
|
self.selecting_in_progress = False
|
||||||
self._select_event()
|
self._select_event()
|
||||||
|
|
||||||
def set_filter(self, text: Optional[str]) -> None:
|
|
||||||
"""Filter rows to only show those containing text"""
|
|
||||||
|
|
||||||
self.row_filter = text
|
|
||||||
with Session() as session:
|
|
||||||
self.update_display(session)
|
|
||||||
|
|
||||||
def set_selected_as_next(self) -> None:
|
def set_selected_as_next(self) -> None:
|
||||||
"""Sets the select track as next to play"""
|
"""Sets the select track as next to play"""
|
||||||
|
|
||||||
@ -723,6 +713,12 @@ class PlaylistTab(QTableWidget):
|
|||||||
# For unplayed tracks, if there's a 'current' or 'next'
|
# For unplayed tracks, if there's a 'current' or 'next'
|
||||||
# track marked, populate start times from then onwards. A note
|
# track marked, populate start times from then onwards. A note
|
||||||
# with a start time will reset the next track start time.
|
# with a start time will reset the next track start time.
|
||||||
|
if current_row and next_row:
|
||||||
|
start_times_row = min(current_row, next_row)
|
||||||
|
else:
|
||||||
|
start_times_row = current_row or next_row
|
||||||
|
if not start_times_row:
|
||||||
|
start_times_row = 0
|
||||||
|
|
||||||
# Cycle through all rows
|
# Cycle through all rows
|
||||||
for row in range(self.rowCount()):
|
for row in range(self.rowCount()):
|
||||||
@ -731,14 +727,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
if row in notes:
|
if row in notes:
|
||||||
# Extract note text from database to ignore section timings
|
# Extract note text from database to ignore section timings
|
||||||
note_text = self._get_row_notes_object(row, session).note
|
note_text = self._get_row_notes_object(row, session).note
|
||||||
if self.row_filter:
|
|
||||||
if self.row_filter not in note_text:
|
|
||||||
self.hideRow(row)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
self.showRow(row)
|
|
||||||
else:
|
|
||||||
self.showRow(row)
|
|
||||||
# Does the note have a start time?
|
# Does the note have a start time?
|
||||||
row_time = self._get_note_text_time(note_text)
|
row_time = self._get_note_text_time(note_text)
|
||||||
if row_time:
|
if row_time:
|
||||||
@ -778,20 +766,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
if section_start_row is not None:
|
if section_start_row is not None:
|
||||||
section_time += track.duration
|
section_time += track.duration
|
||||||
# Render current track
|
# Render current track
|
||||||
if self.row_filter:
|
|
||||||
try:
|
|
||||||
if (track.title
|
|
||||||
and self.row_filter not in track.title
|
|
||||||
and track.artist
|
|
||||||
and self.row_filter not in track.artist):
|
|
||||||
self.hideRow(row)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
self.showRow(row)
|
|
||||||
except TypeError:
|
|
||||||
print(f"TypeError: {track=}")
|
|
||||||
else:
|
|
||||||
self.showRow(row)
|
|
||||||
if row == current_row:
|
if row == current_row:
|
||||||
# Set start time
|
# Set start time
|
||||||
self._set_row_start_time(
|
self._set_row_start_time(
|
||||||
@ -804,8 +778,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
last_played_str)
|
last_played_str)
|
||||||
|
|
||||||
# Calculate next_start_time
|
# Calculate next_start_time
|
||||||
next_start_time = self._calculate_row_end_time(
|
next_start_time = self._calculate_track_end_time(
|
||||||
row, self.current_track_start_time)
|
track, self.current_track_start_time)
|
||||||
|
|
||||||
# Set end time
|
# Set end time
|
||||||
self._set_row_end_time(row, next_start_time)
|
self._set_row_end_time(row, next_start_time)
|
||||||
@ -821,9 +795,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
# Render next track
|
# Render next track
|
||||||
if row == next_row:
|
if row == next_row:
|
||||||
# if there's a track playing, set start time from that
|
# if there's a track playing, set start time from that
|
||||||
if current_row is not None:
|
if current_row:
|
||||||
start_time = self._calculate_row_end_time(
|
start_time = self.current_track_start_time
|
||||||
current_row, self.current_track_start_time)
|
|
||||||
else:
|
else:
|
||||||
# No current track to base from, but don't change
|
# No current track to base from, but don't change
|
||||||
# time if it's already set
|
# time if it's already set
|
||||||
@ -833,7 +806,8 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_row_start_time(row, start_time)
|
self._set_row_start_time(row, start_time)
|
||||||
|
|
||||||
# Set end time
|
# Set end time
|
||||||
next_start_time = self._calculate_row_end_time(row, start_time)
|
next_start_time = self._calculate_track_end_time(
|
||||||
|
track, start_time)
|
||||||
self._set_row_end_time(row, next_start_time)
|
self._set_row_end_time(row, next_start_time)
|
||||||
|
|
||||||
# Set colour
|
# Set colour
|
||||||
@ -855,10 +829,10 @@ class PlaylistTab(QTableWidget):
|
|||||||
self._set_row_not_bold(row)
|
self._set_row_not_bold(row)
|
||||||
else:
|
else:
|
||||||
# Set start/end times as we haven't played it yet
|
# Set start/end times as we haven't played it yet
|
||||||
if next_start_time:
|
if next_start_time and row >= start_times_row:
|
||||||
self._set_row_start_time(row, next_start_time)
|
self._set_row_start_time(row, next_start_time)
|
||||||
next_start_time = self._calculate_row_end_time(
|
next_start_time = self._calculate_track_end_time(
|
||||||
row, next_start_time)
|
track, next_start_time)
|
||||||
# Set end time
|
# Set end time
|
||||||
self._set_row_end_time(row, next_start_time)
|
self._set_row_end_time(row, next_start_time)
|
||||||
else:
|
else:
|
||||||
@ -894,19 +868,22 @@ class PlaylistTab(QTableWidget):
|
|||||||
track: Tracks = self._get_row_track_object(row, session)
|
track: Tracks = self._get_row_track_object(row, session)
|
||||||
open_in_audacity(track.path)
|
open_in_audacity(track.path)
|
||||||
|
|
||||||
def _calculate_row_end_time(self, row, start: Optional[datetime]) \
|
@staticmethod
|
||||||
-> Optional[datetime]:
|
def _calculate_track_end_time(
|
||||||
"""Return this row's end time given its start time"""
|
track: Tracks, start: Optional[datetime]) -> Optional[datetime]:
|
||||||
|
"""Return this track's end time given its start time"""
|
||||||
|
|
||||||
if start is None:
|
if start is None:
|
||||||
return None
|
return None
|
||||||
|
if track is None:
|
||||||
|
DEBUG("_calculate_next_start_time() called with track=None")
|
||||||
|
return None
|
||||||
|
|
||||||
duration = self._get_row_duration(row)
|
duration = track.duration
|
||||||
return start + timedelta(milliseconds=duration)
|
return start + timedelta(milliseconds=duration)
|
||||||
|
|
||||||
def _context_menu(self, pos): # review
|
def _context_menu(self, pos): # review
|
||||||
|
|
||||||
assert self.menu
|
|
||||||
self.menu.exec_(self.mapToGlobal(pos))
|
self.menu.exec_(self.mapToGlobal(pos))
|
||||||
|
|
||||||
def _copy_path(self, row: int) -> None:
|
def _copy_path(self, row: int) -> None:
|
||||||
@ -1164,14 +1141,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
|
|
||||||
return self._meta_search(RowMeta.NOTE, one=False)
|
return self._meta_search(RowMeta.NOTE, one=False)
|
||||||
|
|
||||||
def _get_row_duration(self, row: int) -> int:
|
|
||||||
"""Return duration associated with this row"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
return self.item(row, self.COL_USERDATA).data(self.ROW_DURATION)
|
|
||||||
except:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
def _get_row_end_time(self, row) -> Optional[datetime]:
|
def _get_row_end_time(self, row) -> Optional[datetime]:
|
||||||
"""
|
"""
|
||||||
Return row end time as string
|
Return row end time as string
|
||||||
@ -1582,13 +1551,6 @@ class PlaylistTab(QTableWidget):
|
|||||||
self.item(row, self.COL_USERDATA).setData(
|
self.item(row, self.COL_USERDATA).setData(
|
||||||
self.CONTENT_OBJECT, object_id)
|
self.CONTENT_OBJECT, object_id)
|
||||||
|
|
||||||
def _set_row_duration(self, row: int, ms: int) -> None:
|
|
||||||
"""Set duration of this row in milliseconds"""
|
|
||||||
|
|
||||||
assert self.item(row, self.COL_USERDATA)
|
|
||||||
|
|
||||||
self.item(row, self.COL_USERDATA).setData(self.ROW_DURATION, ms)
|
|
||||||
|
|
||||||
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
|
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
|
||||||
"""Set passed row end time to passed time"""
|
"""Set passed row end time to passed time"""
|
||||||
|
|
||||||
|
|||||||
@ -782,6 +782,8 @@ border: 1px solid rgb(85, 87, 83);</string>
|
|||||||
<addaction name="actionAdd_note"/>
|
<addaction name="actionAdd_note"/>
|
||||||
<addaction name="action_Clear_selection"/>
|
<addaction name="action_Clear_selection"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
|
<addaction name="actionSelect_previous_track"/>
|
||||||
|
<addaction name="actionSelect_next_track"/>
|
||||||
<addaction name="actionSetNext"/>
|
<addaction name="actionSetNext"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionSelect_unplayed_tracks"/>
|
<addaction name="actionSelect_unplayed_tracks"/>
|
||||||
@ -790,11 +792,6 @@ border: 1px solid rgb(85, 87, 83);</string>
|
|||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="actionDownload_CSV_of_played_tracks"/>
|
<addaction name="actionDownload_CSV_of_played_tracks"/>
|
||||||
<addaction name="actionExport_playlist"/>
|
<addaction name="actionExport_playlist"/>
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="actionSelect_next_track"/>
|
|
||||||
<addaction name="actionSelect_previous_track"/>
|
|
||||||
<addaction name="separator"/>
|
|
||||||
<addaction name="actionSearch"/>
|
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menu_Music">
|
<widget class="QMenu" name="menu_Music">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -1035,14 +1032,6 @@ border: 1px solid rgb(85, 87, 83);</string>
|
|||||||
<string>Download CSV of played tracks...</string>
|
<string>Download CSV of played tracks...</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionSearch">
|
|
||||||
<property name="text">
|
|
||||||
<string>Search...</string>
|
|
||||||
</property>
|
|
||||||
<property name="shortcut">
|
|
||||||
<string>/</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="icons.qrc"/>
|
<include location="icons.qrc"/>
|
||||||
|
|||||||
@ -448,8 +448,6 @@ class Ui_MainWindow(object):
|
|||||||
self.actionImport.setObjectName("actionImport")
|
self.actionImport.setObjectName("actionImport")
|
||||||
self.actionDownload_CSV_of_played_tracks = QtWidgets.QAction(MainWindow)
|
self.actionDownload_CSV_of_played_tracks = QtWidgets.QAction(MainWindow)
|
||||||
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
|
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
|
||||||
self.actionSearch = QtWidgets.QAction(MainWindow)
|
|
||||||
self.actionSearch.setObjectName("actionSearch")
|
|
||||||
self.menuFile.addAction(self.actionImport)
|
self.menuFile.addAction(self.actionImport)
|
||||||
self.menuFile.addSeparator()
|
self.menuFile.addSeparator()
|
||||||
self.menuFile.addAction(self.actionE_xit)
|
self.menuFile.addAction(self.actionE_xit)
|
||||||
@ -464,6 +462,8 @@ class Ui_MainWindow(object):
|
|||||||
self.menuPlaylist.addAction(self.actionAdd_note)
|
self.menuPlaylist.addAction(self.actionAdd_note)
|
||||||
self.menuPlaylist.addAction(self.action_Clear_selection)
|
self.menuPlaylist.addAction(self.action_Clear_selection)
|
||||||
self.menuPlaylist.addSeparator()
|
self.menuPlaylist.addSeparator()
|
||||||
|
self.menuPlaylist.addAction(self.actionSelect_previous_track)
|
||||||
|
self.menuPlaylist.addAction(self.actionSelect_next_track)
|
||||||
self.menuPlaylist.addAction(self.actionSetNext)
|
self.menuPlaylist.addAction(self.actionSetNext)
|
||||||
self.menuPlaylist.addSeparator()
|
self.menuPlaylist.addSeparator()
|
||||||
self.menuPlaylist.addAction(self.actionSelect_unplayed_tracks)
|
self.menuPlaylist.addAction(self.actionSelect_unplayed_tracks)
|
||||||
@ -472,11 +472,6 @@ class Ui_MainWindow(object):
|
|||||||
self.menuPlaylist.addSeparator()
|
self.menuPlaylist.addSeparator()
|
||||||
self.menuPlaylist.addAction(self.actionDownload_CSV_of_played_tracks)
|
self.menuPlaylist.addAction(self.actionDownload_CSV_of_played_tracks)
|
||||||
self.menuPlaylist.addAction(self.actionExport_playlist)
|
self.menuPlaylist.addAction(self.actionExport_playlist)
|
||||||
self.menuPlaylist.addSeparator()
|
|
||||||
self.menuPlaylist.addAction(self.actionSelect_next_track)
|
|
||||||
self.menuPlaylist.addAction(self.actionSelect_previous_track)
|
|
||||||
self.menuPlaylist.addSeparator()
|
|
||||||
self.menuPlaylist.addAction(self.actionSearch)
|
|
||||||
self.menu_Music.addAction(self.actionPlay_next)
|
self.menu_Music.addAction(self.actionPlay_next)
|
||||||
self.menu_Music.addAction(self.actionSkip_next)
|
self.menu_Music.addAction(self.actionSkip_next)
|
||||||
self.menu_Music.addAction(self.actionFade)
|
self.menu_Music.addAction(self.actionFade)
|
||||||
@ -565,6 +560,4 @@ class Ui_MainWindow(object):
|
|||||||
self.actionImport.setText(_translate("MainWindow", "Import..."))
|
self.actionImport.setText(_translate("MainWindow", "Import..."))
|
||||||
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
|
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
|
||||||
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
|
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
|
||||||
self.actionSearch.setText(_translate("MainWindow", "Search..."))
|
|
||||||
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
|
|
||||||
import icons_rc
|
import icons_rc
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import os
|
|||||||
import shutil
|
import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
import helpers
|
|
||||||
from config import Config
|
from config import Config
|
||||||
from helpers import (
|
from helpers import (
|
||||||
fade_point,
|
fade_point,
|
||||||
@ -251,26 +250,24 @@ def update_db(session):
|
|||||||
# database:
|
# database:
|
||||||
|
|
||||||
for path in list(os_paths - db_paths):
|
for path in list(os_paths - db_paths):
|
||||||
DEBUG(f"utilities.update_db: {path=} not in database")
|
DEBUG(f"songdb.update_db: {path=} not in database")
|
||||||
# is filename in database?
|
# is filename in database?
|
||||||
track = Tracks.get_by_filename(session, os.path.basename(path))
|
track = Tracks.get_by_filename(session, os.path.basename(path))
|
||||||
if not track:
|
if not track:
|
||||||
messages.append(f"{path} missing from database: {path}")
|
messages.append(f"Track missing from database: {path}")
|
||||||
else:
|
else:
|
||||||
# Check track info matches found track
|
# Check track info matches found track
|
||||||
t = helpers.get_tags(path)
|
t = get_music_info(path)
|
||||||
if t['artist'] == track.artist and t['title'] == track.title:
|
if t['artist'] == track.artist and t['title'] == track.title:
|
||||||
print(f">>> Update {path=} for {track.title=}")
|
track.update_path(path)
|
||||||
track.update_path(session, path)
|
|
||||||
else:
|
else:
|
||||||
create_track_from_file(session, path)
|
create_track_from_file(session, path)
|
||||||
|
|
||||||
# Refresh database paths
|
# Refresh database paths
|
||||||
db_paths = set(Tracks.get_all_paths(session))
|
db_paths = set(Tracks.get_all_paths(session))
|
||||||
# Remove any tracks from database whose paths don't exist
|
# Remote any tracks from database whose paths don't exist
|
||||||
for path in list(db_paths - os_paths):
|
for path in list(db_paths - os_paths):
|
||||||
# Manage tracks listed in database but where path is invalid
|
# Manage tracks listed in database but where path is invalid
|
||||||
DEBUG(f"Invalid {path=} in database", True)
|
|
||||||
track = Tracks.get_by_path(session, path)
|
track = Tracks.get_by_path(session, path)
|
||||||
messages.append(f"Remove from database: {path=} {track=}")
|
messages.append(f"Remove from database: {path=} {track=}")
|
||||||
|
|
||||||
@ -282,14 +279,12 @@ def update_db(session):
|
|||||||
f"File removed: {track.title=}, {track.artist=}, "
|
f"File removed: {track.title=}, {track.artist=}, "
|
||||||
f"{track.path=}"
|
f"{track.path=}"
|
||||||
)
|
)
|
||||||
for playlist_track in track.playlists:
|
for playlist in [a.playlist for a in track.playlists]:
|
||||||
row = playlist_track.row
|
|
||||||
# Remove playlist entry
|
|
||||||
DEBUG(f"Remove {row=} from {playlist_track.playlist_id}", True)
|
|
||||||
playlist_track.playlist.remove_track(session, row)
|
|
||||||
# Create note
|
# Create note
|
||||||
DEBUG(f"Add note at {row=} to {playlist_track.playlist_id=}", True)
|
Notes(session, playlist.id, pt.row, note_txt)
|
||||||
Notes(session, playlist_track.playlist_id, row, note_txt)
|
# TODO: this needs to call playlist.add_note() now
|
||||||
|
# Remove playlist entry
|
||||||
|
playlist.remove_track(session, pt.row)
|
||||||
|
|
||||||
# Remove Track entry pointing to invalid path
|
# Remove Track entry pointing to invalid path
|
||||||
Tracks.remove_by_path(session, path)
|
Tracks.remove_by_path(session, path)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user