Compare commits

..

No commits in common. "cf66cef60ae4514fb1b9ec9477558ed71bd37771" and "ab867c1a67a584344ee97e0ed7902106a81a833b" have entirely different histories.

10 changed files with 550 additions and 535 deletions

View File

@ -38,7 +38,7 @@ class InfoTabs(QTabWidget):
"""Search Songfacts for title""" """Search Songfacts for title"""
slug = slugify(title, replacements=([["'", ""]])) slug = slugify(title, replacements=([["'", ""]]))
log.info(f"Songfacts Infotab for {title=}") log.error(f"Songfacts Infotab for {title=}")
url = f"https://www.songfacts.com/search/songs/{slug}" url = f"https://www.songfacts.com/search/songs/{slug}"
self.open_tab(url, title) self.open_tab(url, title)
@ -47,7 +47,7 @@ class InfoTabs(QTabWidget):
"""Search Wikipedia for title""" """Search Wikipedia for title"""
str = urllib.parse.quote_plus(title) str = urllib.parse.quote_plus(title)
log.info(f"Wikipedia Infotab for {title=}") log.error(f"Wikipedia Infotab for {title=}")
url = f"https://www.wikipedia.org/w/index.php?search={str}" url = f"https://www.wikipedia.org/w/index.php?search={str}"
self.open_tab(url, title) self.open_tab(url, title)

View File

@ -44,7 +44,7 @@ class FadeTrack(QRunnable):
sleep(1 / Config.FADEOUT_STEPS_PER_SECOND) sleep(1 / Config.FADEOUT_STEPS_PER_SECOND)
self.player.stop() self.player.stop()
log.error(f"Releasing player {self.player=}") log.debug(f"Releasing player {self.player=}")
self.player.release() self.player.release()

View File

@ -238,13 +238,13 @@ class Window(QMainWindow, Ui_MainWindow):
else: else:
self.carts_init() self.carts_init()
self.disable_selection_timing = False self.disable_selection_timing = False
self.enable_play_next_controls()
self.clock_counter = 0 self.clock_counter = 0
self.timer10.start(10) self.timer10.start(10)
self.timer500.start(500) self.timer500.start(500)
self.timer1000.start(1000) self.timer1000.start(1000)
self.signals = MusicMusterSignals() self.signals = MusicMusterSignals()
self.connect_signals_slots() self.connect_signals_slots()
self.catch_return_key = False
self.load_last_playlists() self.load_last_playlists()
def about(self) -> None: def about(self) -> None:
@ -517,6 +517,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.actionDownload_CSV_of_played_tracks.triggered.connect( self.actionDownload_CSV_of_played_tracks.triggered.connect(
self.download_played_tracks self.download_played_tracks
) )
self.actionEnable_controls.triggered.connect(self.enable_play_next_controls)
self.actionExport_playlist.triggered.connect(self.export_playlist_tab) self.actionExport_playlist.triggered.connect(self.export_playlist_tab)
self.actionFade.triggered.connect(self.fade) self.actionFade.triggered.connect(self.fade)
self.actionImport.triggered.connect(self.import_track) self.actionImport.triggered.connect(self.import_track)
@ -651,6 +652,14 @@ class Window(QMainWindow, Ui_MainWindow):
else: else:
log.error("Failed to retrieve playlist") log.error("Failed to retrieve playlist")
def disable_play_next_controls(self) -> None:
"""
Disable "play next" keyboard controls
"""
self.actionPlay_next.setEnabled(False)
self.show_status_message("Play controls: Disabled", 0)
def download_played_tracks(self) -> None: def download_played_tracks(self) -> None:
"""Download a CSV of played tracks""" """Download a CSV of played tracks"""
@ -696,6 +705,14 @@ class Window(QMainWindow, Ui_MainWindow):
self.action_Clear_selection.setEnabled(enabled) self.action_Clear_selection.setEnabled(enabled)
def enable_play_next_controls(self) -> None:
"""
Enable "play next" keyboard controls
"""
self.actionPlay_next.setEnabled(True)
self.show_status_message("Play controls: Enabled", 0)
def export_playlist_tab(self) -> None: def export_playlist_tab(self) -> None:
"""Export the current playlist to an m3u file""" """Export the current playlist to an m3u file"""
@ -772,7 +789,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.active_proxy_model().hide_played_tracks(True) self.active_proxy_model().hide_played_tracks(True)
self.btnHidePlayed.setText("Show played") self.btnHidePlayed.setText("Show played")
# Reset row heights # Reset row heights
self.active_tab().resize_rows() self.active_tab().resizeRowsToContents()
def import_track(self) -> None: def import_track(self) -> None:
"""Import track file""" """Import track file"""
@ -881,14 +898,10 @@ class Window(QMainWindow, Ui_MainWindow):
def insert_track(self) -> None: def insert_track(self) -> None:
"""Show dialog box to select and add track from database""" """Show dialog box to select and add track from database"""
new_row_number = (
self.active_tab().source_model_selected_row_number()
or self.active_proxy_model().rowCount()
)
with Session() as session: with Session() as session:
dlg = TrackSelectDialog( dlg = TrackSelectDialog(
session=session, session=session,
new_row_number=new_row_number, new_row_number=self.active_tab().source_model_selected_row_number(),
source_model=self.active_proxy_model(), source_model=self.active_proxy_model(),
) )
dlg.exec() dlg.exec()
@ -1089,16 +1102,6 @@ class Window(QMainWindow, Ui_MainWindow):
- Update headers - Update headers
""" """
# Check for inadvertent press of 'return'
if self.catch_return_key:
if not helpers.ask_yes_no(
"Track playing",
"Really play next track now?",
default_yes=True,
parent=self,
):
return
log.info(f"play_next({position=})") log.info(f"play_next({position=})")
# If there is no next track set, return. # If there is no next track set, return.
@ -1141,7 +1144,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.playing = True self.playing = True
# Disable play next controls # Disable play next controls
self.catch_return_key = True self.disable_play_next_controls()
# Notify model # Notify model
self.active_proxy_model().current_track_started() self.active_proxy_model().current_track_started()
@ -1163,6 +1166,22 @@ class Window(QMainWindow, Ui_MainWindow):
break break
sleep(0.1) sleep(0.1)
# Try making playing the last thing we do here to see whether
# the occasional short pause at the start of the track can be
# eliminated.
# # Notify model
# self.active_proxy_model().current_track_started()
# # Note that track is now playing
# self.playing = True
# # Disable play next controls
# self.disable_play_next_controls()
# # Update headers
# self.update_headers()
def preview(self) -> None: def preview(self) -> None:
""" """
Preview selected or next track. We use a different mechanism to Preview selected or next track. We use a different mechanism to
@ -1268,7 +1287,7 @@ class Window(QMainWindow, Ui_MainWindow):
# Disable play controls so that 'return' in search box doesn't # Disable play controls so that 'return' in search box doesn't
# play next track # play next track
self.catch_return_key = True self.disable_play_next_controls()
self.txtSearch.setHidden(False) self.txtSearch.setHidden(False)
self.txtSearch.setFocus() self.txtSearch.setFocus()
# Select any text that may already be there # Select any text that may already be there
@ -1475,12 +1494,16 @@ class Window(QMainWindow, Ui_MainWindow):
self.update_headers() self.update_headers()
# Enable controls # Enable controls
self.catch_return_key = False self.enable_play_next_controls()
def tab_change(self): def tab_change(self):
"""Called when active tab changed""" """Called when active tab changed"""
self.active_tab().resize_rows() log.info("tab_change()")
tab = self.active_tab()
if tab:
QTimer.singleShot(300, tab.resizeRowsToContents)
def tick_10ms(self) -> None: def tick_10ms(self) -> None:
""" """
@ -1555,7 +1578,7 @@ class Window(QMainWindow, Ui_MainWindow):
css_silence = f"background: {Config.COLOUR_ENDING_TIMER}" css_silence = f"background: {Config.COLOUR_ENDING_TIMER}"
if self.frame_silent.styleSheet() != css_silence: if self.frame_silent.styleSheet() != css_silence:
self.frame_silent.setStyleSheet(css_silence) self.frame_silent.setStyleSheet(css_silence)
self.catch_return_key = False self.enable_play_next_controls()
# Set warning colour on time to silence box when fade starts # Set warning colour on time to silence box when fade starts
elif time_to_fade <= 500: elif time_to_fade <= 500:
css_fade = f"background: {Config.COLOUR_WARNING_TIMER}" css_fade = f"background: {Config.COLOUR_WARNING_TIMER}"
@ -1567,7 +1590,7 @@ class Window(QMainWindow, Ui_MainWindow):
self.frame_fade.setStyleSheet( self.frame_fade.setStyleSheet(
f"background: {Config.COLOUR_WARNING_TIMER}" f"background: {Config.COLOUR_WARNING_TIMER}"
) )
self.catch_return_key = False self.enable_play_next_controls()
else: else:
self.frame_silent.setStyleSheet("") self.frame_silent.setStyleSheet("")
self.frame_fade.setStyleSheet("") self.frame_fade.setStyleSheet("")

View File

@ -138,10 +138,10 @@ class PipeClient():
return self return self
def __init__(self): def __init__(self):
self.timer: bool = False # type: ignore self.timer: bool = False
self._start_time: float = 0 # type: ignore self._start_time: float = 0
self._write_pipe = None self._write_pipe = None
self.reply: str = '' # type: ignore self.reply: str = ''
if not self._write_pipe: if not self._write_pipe:
self._write_thread_start() self._write_thread_start()
self._read_thread_start() self._read_thread_start()

View File

@ -7,7 +7,6 @@ from datetime import datetime, timedelta
from enum import auto, Enum from enum import auto, Enum
from operator import attrgetter from operator import attrgetter
from pprint import pprint from pprint import pprint
from random import shuffle
from typing import List, Optional from typing import List, Optional
from PyQt6.QtCore import ( from PyQt6.QtCore import (
@ -255,10 +254,7 @@ class PlaylistModel(QAbstractTableModel):
""" """
row_number = track_sequence.now.plr_rownum row_number = track_sequence.now.plr_rownum
if row_number is not None:
prd = self.playlist_rows[row_number] prd = self.playlist_rows[row_number]
else:
prd = None
# Sanity check # Sanity check
if not track_sequence.now.track_id: if not track_sequence.now.track_id:
@ -289,7 +285,6 @@ class PlaylistModel(QAbstractTableModel):
log.error(f"Can't retrieve plr, {track_sequence.now.plr_id=}") log.error(f"Can't retrieve plr, {track_sequence.now.plr_id=}")
# Update track times # Update track times
if prd:
prd.start_time = track_sequence.now.start_time prd.start_time = track_sequence.now.start_time
prd.end_time = track_sequence.now.end_time prd.end_time = track_sequence.now.end_time
@ -1316,14 +1311,6 @@ class PlaylistModel(QAbstractTableModel):
self.sort_by_attribute(row_numbers, "lastplayed") self.sort_by_attribute(row_numbers, "lastplayed")
def sort_randomly(self, row_numbers: List[int]) -> None:
"""
Sort selected rows randomly
"""
shuffle(row_numbers)
self.move_rows(row_numbers, min(row_numbers))
def sort_by_title(self, row_numbers: List[int]) -> None: def sort_by_title(self, row_numbers: List[int]) -> None:
""" """
Sort selected rows by title Sort selected rows by title
@ -1612,9 +1599,6 @@ class PlaylistProxyModel(QSortFilterProxyModel):
def sort_by_lastplayed(self, row_numbers: List[int]) -> None: def sort_by_lastplayed(self, row_numbers: List[int]) -> None:
return self.source_model.sort_by_lastplayed(row_numbers) return self.source_model.sort_by_lastplayed(row_numbers)
def sort_randomly(self, row_numbers: List[int]) -> None:
return self.source_model.sort_randomly(row_numbers)
def sort_by_title(self, row_numbers: List[int]) -> None: def sort_by_title(self, row_numbers: List[int]) -> None:
return self.source_model.sort_by_title(row_numbers) return self.source_model.sort_by_title(row_numbers)

View File

@ -209,7 +209,7 @@ class PlaylistTab(QTableView):
h_header.sectionResized.connect(self._column_resize) h_header.sectionResized.connect(self._column_resize)
h_header.setStretchLastSection(True) h_header.setStretchLastSection(True)
# Setting ResizeToContents causes screen flash on load # Setting ResizeToContents causes screen flash on load
self.resize_rows() QTimer.singleShot(300, self.resizeRowsToContents)
# ########## Overridden class functions ########## # ########## Overridden class functions ##########
@ -225,7 +225,7 @@ class PlaylistTab(QTableView):
super(PlaylistTab, self).closeEditor(editor, hint) super(PlaylistTab, self).closeEditor(editor, hint)
# Optimise row heights after increasing row height for editing # Optimise row heights after increasing row height for editing
self.resize_rows() self.resizeRowsToContents()
# Update start times in case a start time in a note has been # Update start times in case a start time in a note has been
# edited # edited
@ -260,7 +260,7 @@ class PlaylistTab(QTableView):
self.clear_selection() self.clear_selection()
# Resize rows # Resize rows
self.resize_rows() self.resizeRowsToContents()
event.accept() event.accept()
@ -487,11 +487,6 @@ class PlaylistTab(QTableView):
lambda: proxy_model.sort_by_lastplayed(self.get_selected_rows()), lambda: proxy_model.sort_by_lastplayed(self.get_selected_rows()),
parent_menu=sort_menu, parent_menu=sort_menu,
) )
self._add_context_menu(
"randomly",
lambda: proxy_model.sort_randomly(self.get_selected_rows()),
parent_menu=sort_menu,
)
# Info # Info
if track_row: if track_row:
@ -716,29 +711,17 @@ class PlaylistTab(QTableView):
self.source_model.rescan_track(row_number) self.source_model.rescan_track(row_number)
self.clear_selection() self.clear_selection()
def resize_rows(self, playlist_id: Optional[int] = None) -> None: def resize_rows(self, playlist_id: int) -> None:
""" """
If playlist_id is us, resize rows If playlist_id is us, resize rows
""" """
log.info(f"resize_rows({playlist_id=}) {self.playlist_id=}") log.info(f"resize_rows({playlist_id=}) {self.playlist_id=}")
if playlist_id and playlist_id != self.playlist_id: if playlist_id != self.playlist_id:
return return
# self.resizeRowsToContents() self.resizeRowsToContents()
# Suggestion from phind.com
def resize_row(row, count=1):
row_count = self.source_model.rowCount()
for todo in range(count):
if row < row_count:
self.resizeRowToContents(row)
row += 1
if row < row_count:
QTimer.singleShot(0, lambda: resize_row(row, count))
# Start resizing from row 0, 10 rows at a time
QTimer.singleShot(0, lambda: resize_row(0, 10))
def scroll_to_top(self, row_number: int) -> None: def scroll_to_top(self, row_number: int) -> None:
""" """

View File

@ -775,6 +775,8 @@ padding-left: 8px;</string>
<addaction name="actionSetNext"/> <addaction name="actionSetNext"/>
<addaction name="action_Clear_selection"/> <addaction name="action_Clear_selection"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionEnable_controls"/>
<addaction name="separator"/>
<addaction name="actionMark_for_moving"/> <addaction name="actionMark_for_moving"/>
<addaction name="actionPaste"/> <addaction name="actionPaste"/>
</widget> </widget>

View File

@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'app/ui/main_window.ui' # Form implementation generated from reading ui file 'app/ui/main_window.ui'
# #
# Created by: PyQt6 UI code generator 6.6.1 # Created by: PyQt6 UI code generator 6.6.0
# #
# WARNING: Any manual changes made to this file will be lost when pyuic6 is # WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing. # run again. Do not edit this file unless you know what you are doing.
@ -487,6 +487,8 @@ class Ui_MainWindow(object):
self.menuPlaylist.addAction(self.actionSetNext) self.menuPlaylist.addAction(self.actionSetNext)
self.menuPlaylist.addAction(self.action_Clear_selection) self.menuPlaylist.addAction(self.action_Clear_selection)
self.menuPlaylist.addSeparator() self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionEnable_controls)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionMark_for_moving) self.menuPlaylist.addAction(self.actionMark_for_moving)
self.menuPlaylist.addAction(self.actionPaste) self.menuPlaylist.addAction(self.actionPaste)
self.menuSearc_h.addAction(self.actionSearch) self.menuSearc_h.addAction(self.actionSearch)

943
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ flakehell = "^0.9.0"
pudb = "^2023.1" pudb = "^2023.1"
sphinx = "^7.0.1" sphinx = "^7.0.1"
furo = "^2023.5.20" furo = "^2023.5.20"
black = "^24.2.0" black = "^23.3.0"
flakehell = "^0.9.0" flakehell = "^0.9.0"
mypy = "^1.7.0" mypy = "^1.7.0"
pdbp = "^1.5.0" pdbp = "^1.5.0"
@ -50,8 +50,8 @@ requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"
[tool.mypy] [tool.mypy]
# mypy_path = "/home/kae/.cache/pypoetry/virtualenvs/musicmuster-oWgGw1IG-py3.9:/home/kae/git/musicmuster/app"
mypy_path = "/home/kae/git/musicmuster/app" mypy_path = "/home/kae/git/musicmuster/app"
explicit_package_bases = true
[tool.pytest.ini_options] [tool.pytest.ini_options]
addopts = "--exitfirst --showlocals --capture=no" addopts = "--exitfirst --showlocals --capture=no"