Compare commits
3 Commits
7f3e235e9d
...
ffb1b238f4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffb1b238f4 | ||
|
|
83780bfb68 | ||
|
|
cd793f9668 |
@ -123,6 +123,10 @@ class AudioMetadata(NamedTuple):
|
||||
|
||||
|
||||
class Col(Enum):
|
||||
"""
|
||||
Columns in playlist
|
||||
"""
|
||||
|
||||
START_GAP = 0
|
||||
TITLE = auto()
|
||||
ARTIST = auto()
|
||||
@ -142,6 +146,10 @@ class FileErrors(NamedTuple):
|
||||
|
||||
@dataclass
|
||||
class Filter:
|
||||
"""
|
||||
Filter used in queries to select tracks
|
||||
"""
|
||||
|
||||
version: int = 1
|
||||
path_type: str = "contains"
|
||||
path: str = ""
|
||||
@ -172,6 +180,10 @@ class PlaylistStyle(QProxyStyle):
|
||||
|
||||
|
||||
class QueryCol(Enum):
|
||||
"""
|
||||
Columns in querylist
|
||||
"""
|
||||
|
||||
TITLE = 0
|
||||
ARTIST = auto()
|
||||
DURATION = auto()
|
||||
@ -221,9 +233,7 @@ class MusicMusterSignals(QObject):
|
||||
- https://stackoverflow.com/questions/62654525/emit-a-signal-from-another-class-to-main-class
|
||||
"""
|
||||
|
||||
begin_reset_model_signal = pyqtSignal(int)
|
||||
enable_escape_signal = pyqtSignal(bool)
|
||||
end_reset_model_signal = pyqtSignal(int)
|
||||
next_track_changed_signal = pyqtSignal()
|
||||
resize_rows_signal = pyqtSignal(int)
|
||||
search_songfacts_signal = pyqtSignal(str)
|
||||
|
||||
16
app/ds.py
16
app/ds.py
@ -451,7 +451,7 @@ def track_set_intro(track_id: int, intro: int) -> None:
|
||||
|
||||
# @log_call
|
||||
def track_update(
|
||||
path: str, track_id: int, metadata: dict[str, str | int | float]
|
||||
track_id: int, metadata: dict[str, str | int | float]
|
||||
) -> TrackDTO:
|
||||
"""
|
||||
Update an existing track db entry return the DTO
|
||||
@ -462,14 +462,12 @@ def track_update(
|
||||
|
||||
if not track:
|
||||
raise ApplicationError(f"Can't retrieve Track ({track_id=})")
|
||||
track.path = str(metadata["path"])
|
||||
track.title = str(metadata["title"])
|
||||
track.artist = str(metadata["artist"])
|
||||
track.duration = int(metadata["duration"])
|
||||
track.start_gap = int(metadata["start_gap"])
|
||||
track.fade_at = int(metadata["fade_at"])
|
||||
track.silence_at = int(metadata["silence_at"])
|
||||
track.bitrate = int(metadata["bitrate"])
|
||||
|
||||
for key, value in metadata.items():
|
||||
if hasattr(track, key):
|
||||
setattr(track, key, value)
|
||||
else:
|
||||
raise ApplicationError(f"Tried to set attribute {key} on {track}")
|
||||
|
||||
session.commit()
|
||||
|
||||
|
||||
@ -640,9 +640,7 @@ class DoTrackImport(QThread):
|
||||
if self.track_id == 0:
|
||||
track_dto = ds.track_create(metadata)
|
||||
else:
|
||||
track_dto = ds.track_update(
|
||||
self.destination_track_path, self.track_id, metadata
|
||||
)
|
||||
track_dto = ds.track_update(self.track_id, metadata)
|
||||
|
||||
self.signals.status_message_signal.emit(
|
||||
f"{os.path.basename(self.import_file_path)} imported", 10000
|
||||
|
||||
@ -67,8 +67,8 @@ from classes import (
|
||||
Filter,
|
||||
MusicMusterSignals,
|
||||
PlaylistDTO,
|
||||
TrackAndPlaylist,
|
||||
QueryDTO,
|
||||
TrackAndPlaylist,
|
||||
TrackInfo,
|
||||
)
|
||||
from config import Config
|
||||
@ -472,7 +472,7 @@ class ManageQueries(ItemlistManager):
|
||||
# Build a list of queries
|
||||
query_list: list[ItemlistItem] = []
|
||||
|
||||
for query in ds.get_all_queries():
|
||||
for query in ds.queries_all():
|
||||
query_list.append(
|
||||
ItemlistItem(
|
||||
name=query.name, id=query.query_id, favourite=query.favourite
|
||||
@ -494,7 +494,7 @@ class ManageQueries(ItemlistManager):
|
||||
"Delete query",
|
||||
f"Delete query '{query.name}': " "Are you sure?",
|
||||
):
|
||||
ds.delete_query(query_id)
|
||||
ds.query_delete(query_id)
|
||||
|
||||
self.refresh_table()
|
||||
|
||||
@ -503,8 +503,9 @@ class ManageQueries(ItemlistManager):
|
||||
|
||||
dlg = FilterDialog(query.name, query.filter)
|
||||
if dlg.exec():
|
||||
ds.update_query_filter(query.query_id, dlg.filter)
|
||||
ds.update_query_name(query.query_id, dlg.name_text.text())
|
||||
ds.query_update_filter
|
||||
(query.query_id, dlg.filter)
|
||||
ds.query_update_name(query.query_id, dlg.name_text.text())
|
||||
|
||||
def edit_item(self, query_id: int) -> None:
|
||||
"""Edit query"""
|
||||
@ -519,7 +520,7 @@ class ManageQueries(ItemlistManager):
|
||||
def toggle_favourite(self, query_id: int, favourite: bool) -> None:
|
||||
"""Mark query as (not) favourite"""
|
||||
|
||||
ds.update_query_favourite(query_id, favourite)
|
||||
ds.query_update_favourite(query_id, favourite)
|
||||
|
||||
def new_item(self) -> None:
|
||||
"""Create new query"""
|
||||
@ -528,7 +529,7 @@ class ManageQueries(ItemlistManager):
|
||||
if not query_name:
|
||||
return
|
||||
|
||||
query = ds.create_query(query_name, Filter())
|
||||
query = ds.query_create(query_name, Filter())
|
||||
self._edit_item(query)
|
||||
self.refresh_table()
|
||||
|
||||
@ -542,7 +543,7 @@ class ManageQueries(ItemlistManager):
|
||||
if not new_name:
|
||||
return
|
||||
|
||||
ds.update_query_name(query_id, new_name)
|
||||
ds.query_update_name(query_id, new_name)
|
||||
|
||||
self.change_text(query_id, new_name)
|
||||
|
||||
@ -567,7 +568,7 @@ class ManageTemplates(ItemlistManager):
|
||||
# Build a list of templates
|
||||
template_list: list[ItemlistItem] = []
|
||||
|
||||
for template in ds.playlists_templates():
|
||||
for template in ds.playlists_templates_all():
|
||||
template_list.append(
|
||||
ItemlistItem(
|
||||
name=template.name,
|
||||
@ -602,7 +603,7 @@ class ManageTemplates(ItemlistManager):
|
||||
else:
|
||||
self.musicmuster.playlist_section.tabPlaylist.removeTab(open_idx)
|
||||
|
||||
ds.delete_playlist(template.playlist_id)
|
||||
ds.playlist_delete(template.playlist_id)
|
||||
|
||||
def edit_item(self, template_id: int) -> None:
|
||||
"""Edit template"""
|
||||
@ -619,7 +620,7 @@ class ManageTemplates(ItemlistManager):
|
||||
def toggle_favourite(self, template_id: int, favourite: bool) -> None:
|
||||
"""Mark template as (not) favourite"""
|
||||
|
||||
ds.update_template_favourite(template_id, favourite)
|
||||
ds.playlist_update_template_favourite(template_id, favourite)
|
||||
|
||||
def new_item(
|
||||
self,
|
||||
@ -634,12 +635,14 @@ class ManageTemplates(ItemlistManager):
|
||||
return
|
||||
|
||||
# Get new template name
|
||||
name = self.musicmuster.get_playlist_name(default="", prompt="New template name:")
|
||||
name = self.musicmuster.get_playlist_name(
|
||||
default="", prompt="New template name:"
|
||||
)
|
||||
if not name:
|
||||
return
|
||||
|
||||
# Create playlist for template and mark is as a template
|
||||
template = ds.create_playlist(name, template_id, as_template=True)
|
||||
template = ds.playlist_create(name, template_id, as_template=True)
|
||||
|
||||
# Open it for editing
|
||||
self.musicmuster._open_playlist(template, is_template=True)
|
||||
@ -780,7 +783,7 @@ class QueryDialog(QDialog):
|
||||
|
||||
self.query_list: list[tuple[str, int]] = []
|
||||
self.query_list.append((Config.NO_QUERY_NAME, 0))
|
||||
for query in ds.get_all_queries():
|
||||
for query in ds.queries_all():
|
||||
self.query_list.append((query.name, query.query_id))
|
||||
|
||||
self.setWindowTitle("Query Selector")
|
||||
@ -908,9 +911,7 @@ class QueryDialog(QDialog):
|
||||
return
|
||||
for column_number in range(column_count - 1):
|
||||
attr_name = f"querylist_col_{column_number}_width"
|
||||
ds.setting_set(
|
||||
attr_name, self.table_view.columnWidth(column_number)
|
||||
)
|
||||
ds.setting_set(attr_name, self.table_view.columnWidth(column_number))
|
||||
|
||||
def _column_resize(self, column_number: int, _old: int, _new: int) -> None:
|
||||
"""
|
||||
@ -1022,6 +1023,12 @@ class SelectPlaylistDialog(QDialog):
|
||||
self.accept()
|
||||
|
||||
|
||||
@dataclass
|
||||
class MoveSource:
|
||||
model: PlaylistModel
|
||||
rows: list[int]
|
||||
|
||||
|
||||
class TemplateSelectorDialog(QDialog):
|
||||
"""
|
||||
Class to manage user selection of template
|
||||
@ -1145,8 +1152,7 @@ class Window(QMainWindow):
|
||||
self.footer_section.widgetFadeVolume.setDefaultPadding(0)
|
||||
self.footer_section.widgetFadeVolume.setBackground(Config.FADE_CURVE_BACKGROUND)
|
||||
|
||||
self.move_source_rows: list[PlaylistRow] = []
|
||||
self.move_source_model: Optional[PlaylistModel] = None
|
||||
self.move_source: MoveSource | None = None
|
||||
|
||||
self.disable_selection_timing = False
|
||||
self.clock_counter = 0
|
||||
@ -1324,7 +1330,7 @@ class Window(QMainWindow):
|
||||
"separator": True,
|
||||
},
|
||||
]
|
||||
templates = ds.playlists_templates()
|
||||
templates = ds.playlists_templates_all()
|
||||
for template in templates:
|
||||
submenu_items.append(
|
||||
{
|
||||
@ -1357,7 +1363,7 @@ class Window(QMainWindow):
|
||||
"separator": True,
|
||||
},
|
||||
]
|
||||
queries = ds.get_all_queries(favourites_only=True)
|
||||
queries = ds.queries_all(favourites_only=True)
|
||||
for query in queries:
|
||||
submenu_items.append(
|
||||
{
|
||||
@ -1458,7 +1464,7 @@ class Window(QMainWindow):
|
||||
if not playlist_name:
|
||||
return
|
||||
|
||||
_ = ds.create_playlist(playlist_name, template_id)
|
||||
_ = ds.playlist_create(playlist_name, template_id)
|
||||
|
||||
# @log_call
|
||||
def delete_playlist(self, checked: bool = False) -> None:
|
||||
@ -1474,7 +1480,7 @@ class Window(QMainWindow):
|
||||
f"Delete playlist '{playlist.name}': " "Are you sure?",
|
||||
):
|
||||
if self.close_playlist_tab():
|
||||
ds.delete_playlist(self.current.playlist_id)
|
||||
ds.playlist_delete(self.current.playlist_id)
|
||||
else:
|
||||
log.error("Failed to retrieve playlist")
|
||||
|
||||
@ -1491,7 +1497,7 @@ class Window(QMainWindow):
|
||||
def save_as_template(self, checked: bool = False) -> None:
|
||||
"""Save current playlist as template"""
|
||||
|
||||
template_names = [a.name for a in ds.playlists_templates()]
|
||||
template_names = [a.name for a in ds.playlists_templates_all()]
|
||||
|
||||
while True:
|
||||
# Get name for new template
|
||||
@ -1509,7 +1515,7 @@ class Window(QMainWindow):
|
||||
helpers.show_warning(
|
||||
self, "Duplicate template", "Template name already in use"
|
||||
)
|
||||
ds.save_as_template(self.current.playlist_id, template_name)
|
||||
ds.playlist_save_as_template(self.current.playlist_id, template_name)
|
||||
helpers.show_OK("Template", "Template saved", self)
|
||||
|
||||
def get_playlist_name(
|
||||
@ -1520,7 +1526,7 @@ class Window(QMainWindow):
|
||||
dlg = QInputDialog(self)
|
||||
dlg.setInputMode(QInputDialog.InputMode.TextInput)
|
||||
dlg.setLabelText(prompt)
|
||||
all_playlist_names = [a.name for a in ds.get_all_playlists()]
|
||||
all_playlist_names = [a.name for a in ds.playlists_all()]
|
||||
|
||||
while True:
|
||||
if default:
|
||||
@ -1552,7 +1558,7 @@ class Window(QMainWindow):
|
||||
template_name_id_list: list[tuple[str, int]] = []
|
||||
template_name_id_list.append((Config.NO_TEMPLATE_NAME, 0))
|
||||
|
||||
for template in ds.playlists_templates():
|
||||
for template in ds.playlists_templates_all():
|
||||
template_name_id_list.append((template.name, template.playlist_id))
|
||||
|
||||
dlg = TemplateSelectorDialog(template_name_id_list, template_prompt)
|
||||
@ -1661,7 +1667,7 @@ class Window(QMainWindow):
|
||||
return False
|
||||
|
||||
# Record playlist as closed
|
||||
ds.playlist_mark_status(open=False)
|
||||
ds.playlist_mark_status(closing_tab_playlist_id, open=False)
|
||||
|
||||
# Close playlist and remove tab
|
||||
self.playlist_section.tabPlaylist.widget(tab_index).close()
|
||||
@ -1828,7 +1834,7 @@ class Window(QMainWindow):
|
||||
with open(path, "w") as f:
|
||||
# Required directive on first line
|
||||
f.write("#EXTM3U\n")
|
||||
for playlistrow in ds.get_playlist_rows(playlist_id):
|
||||
for playlistrow in ds.playlistrows_by_playlist(playlist_id):
|
||||
if playlistrow.track:
|
||||
f.write(
|
||||
"#EXTINF:"
|
||||
@ -1948,13 +1954,13 @@ class Window(QMainWindow):
|
||||
|
||||
# Save the selected PlaylistRows items ready for a later
|
||||
# paste
|
||||
self.move_source_rows = self.current.base_model.selected_rows
|
||||
self.move_source_model = self.current.base_model
|
||||
|
||||
log.debug(
|
||||
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
||||
self.move_source = MoveSource(
|
||||
model=self.current.base_model,
|
||||
rows=[a.row_number for a in self.current.base_model.selected_rows],
|
||||
)
|
||||
|
||||
log.debug(f"mark_rows_for_moving(): {self.move_source=}")
|
||||
|
||||
# @log_call
|
||||
def move_playlist_rows(self, row_numbers: list[int]) -> None:
|
||||
"""
|
||||
@ -1965,7 +1971,7 @@ class Window(QMainWindow):
|
||||
playlists = []
|
||||
source_playlist_id = self.current.playlist_id
|
||||
|
||||
for playlist in ds.get_all_playlists():
|
||||
for playlist in ds.playlists_all():
|
||||
if playlist.id == source_playlist_id:
|
||||
continue
|
||||
else:
|
||||
@ -2035,36 +2041,37 @@ class Window(QMainWindow):
|
||||
# @log_call
|
||||
def paste_rows(self, checked: bool = False) -> None:
|
||||
"""
|
||||
Paste earlier cut rows.
|
||||
Paste earlier rows identified in self.mark_rows_for_moving()
|
||||
|
||||
'checked' is a dummy parameter passed to us by the menu
|
||||
"""
|
||||
|
||||
if not self.move_source_rows or not self.move_source_model:
|
||||
if not self.move_source:
|
||||
return
|
||||
|
||||
to_playlist_model = self.current.base_model
|
||||
destination_row = self.current_row_or_end()
|
||||
from_playlist_model = self.move_source.model
|
||||
to_row = self.current_row_or_end()
|
||||
from_rows = self.move_source.rows
|
||||
|
||||
# If we move a row to immediately under the current track, make
|
||||
# that moved row the next track
|
||||
set_next_row: Optional[int] = None
|
||||
if (
|
||||
self.track_sequence.current
|
||||
and self.track_sequence.current.playlist_id == to_playlist_model.playlist_id
|
||||
and destination_row == self.track_sequence.current.row_number + 1
|
||||
):
|
||||
set_next_row = destination_row
|
||||
|
||||
if to_playlist_model.playlist_id == self.move_source_model.playlist_id:
|
||||
self.move_source_model.move_rows(self.move_source_rows, destination_row)
|
||||
if from_playlist_model == to_playlist_model:
|
||||
from_playlist_model.move_rows(from_rows, to_row)
|
||||
else:
|
||||
self.move_source_model.move_rows_between_playlists(
|
||||
self.move_source_rows, destination_row, to_playlist_model.playlist_id
|
||||
from_playlist_model.move_rows_between_playlists(
|
||||
from_rows, to_row, to_playlist_model.playlist_id
|
||||
)
|
||||
|
||||
self.active_tab().resize_rows()
|
||||
self.active_tab().clear_selection()
|
||||
|
||||
if set_next_row:
|
||||
to_playlist_model.set_next_row(set_next_row)
|
||||
# If we move a row to immediately under the current track, make
|
||||
# that moved row the next track
|
||||
if (
|
||||
self.track_sequence.current
|
||||
and self.track_sequence.current.playlist_id == to_playlist_model.playlist_id
|
||||
and to_row == self.track_sequence.current.row_number + 1
|
||||
):
|
||||
to_playlist_model.set_next_row(to_row)
|
||||
|
||||
# @log_call
|
||||
def play_next(
|
||||
@ -2141,8 +2148,10 @@ class Window(QMainWindow):
|
||||
|
||||
# Notify others
|
||||
self.signals.signal_track_started.emit(
|
||||
TrackAndPlaylist(self.track_sequence.current.playlist_id,
|
||||
self.track_sequence.current.track_id)
|
||||
TrackAndPlaylist(
|
||||
self.track_sequence.current.playlist_id,
|
||||
self.track_sequence.current.track_id,
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: ensure signal_track_started does all this:
|
||||
@ -2192,7 +2201,9 @@ class Window(QMainWindow):
|
||||
f"musicmuster.preview: unable to retreive track {track_info.track_id=}"
|
||||
)
|
||||
self.preview_manager.set_track_info(
|
||||
track_id=track.track_id, track_path=track.path, track_intro=track.intro or 0
|
||||
track_id=track.track_id,
|
||||
track_path=track.path,
|
||||
track_intro=track.intro or 0,
|
||||
)
|
||||
self.preview_manager.play()
|
||||
self.show_status_message(
|
||||
@ -2236,7 +2247,7 @@ class Window(QMainWindow):
|
||||
return
|
||||
|
||||
intro = round(self.preview_manager.get_playtime() / 100) * 100
|
||||
ds.set_track_intro(track_id, intro)
|
||||
ds.track_set_intro(track_id, intro)
|
||||
self.preview_manager.set_intro(intro)
|
||||
self.current.base_model.refresh_row(row_number)
|
||||
roles = [
|
||||
@ -2274,7 +2285,7 @@ class Window(QMainWindow):
|
||||
if playlist:
|
||||
new_name = self.get_playlist_name(playlist.name)
|
||||
if new_name:
|
||||
ds.playlist_rename(playlist.id, new_name)
|
||||
ds.playlist_rename(playlist.playlist_id, new_name)
|
||||
idx = self.tabBar.currentIndex()
|
||||
self.tabBar.setTabText(idx, new_name)
|
||||
|
||||
|
||||
@ -92,8 +92,6 @@ class PlaylistModel(QAbstractTableModel):
|
||||
self.played_tracks_hidden = False
|
||||
|
||||
# Connect signals
|
||||
self.signals.begin_reset_model_signal.connect(self.begin_reset_model)
|
||||
self.signals.end_reset_model_signal.connect(self.end_reset_model)
|
||||
self.signals.signal_add_track_to_header.connect(self.add_track_to_header)
|
||||
self.signals.signal_begin_insert_rows.connect(self.begin_insert_rows)
|
||||
self.signals.signal_end_insert_rows.connect(self.end_insert_rows)
|
||||
@ -239,16 +237,6 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
return QBrush()
|
||||
|
||||
# @log_call
|
||||
def begin_reset_model(self, playlist_id: int) -> None:
|
||||
"""
|
||||
Reset model if playlist_id is ours
|
||||
"""
|
||||
|
||||
if playlist_id != self.playlist_id:
|
||||
return
|
||||
super().beginResetModel()
|
||||
|
||||
def columnCount(self, parent: QModelIndex = QModelIndex()) -> int:
|
||||
"""Standard function for view"""
|
||||
|
||||
@ -463,19 +451,6 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
return ""
|
||||
|
||||
# @log_call
|
||||
def end_reset_model(self, playlist_id: int) -> None:
|
||||
"""
|
||||
End model reset if this is our playlist
|
||||
"""
|
||||
|
||||
if playlist_id != self.playlist_id:
|
||||
return
|
||||
self.refresh_data()
|
||||
super().endResetModel()
|
||||
self.track_sequence.update()
|
||||
self.update_track_times()
|
||||
|
||||
def _edit_role(self, row: int, column: int, plr: PlaylistRow) -> str | int:
|
||||
"""
|
||||
Return value for editing
|
||||
@ -1063,7 +1038,7 @@ class PlaylistModel(QAbstractTableModel):
|
||||
|
||||
track = self.playlist_rows[row_number]
|
||||
metadata = get_all_track_metadata(track.path)
|
||||
_ = ds.track_update(track.path, track.track_id, metadata)
|
||||
_ = ds.track_update(track.track_id, metadata)
|
||||
|
||||
roles = [
|
||||
Qt.ItemDataRole.BackgroundRole,
|
||||
|
||||
@ -574,7 +574,7 @@ class PlaylistTab(QTableView):
|
||||
"Rescan track", lambda: self._rescan(model_row_number)
|
||||
)
|
||||
self._add_context_menu("Mark for moving", lambda: self._mark_for_moving())
|
||||
if self.musicmuster.move_source_rows:
|
||||
if self.musicmuster.move_source:
|
||||
self._add_context_menu(
|
||||
"Move selected rows here", lambda: self._move_selected_rows()
|
||||
)
|
||||
|
||||
@ -228,7 +228,7 @@ class QuerylistModel(QAbstractTableModel):
|
||||
row = 0
|
||||
|
||||
try:
|
||||
results = ds.get_filtered_tracks(self.filter)
|
||||
results = ds.tracks_filtered(self.filter)
|
||||
for result in results:
|
||||
queryrow = QueryRow(
|
||||
artist=result.artist,
|
||||
|
||||
@ -91,7 +91,6 @@ def update_bitrates() -> None:
|
||||
for track in ds.tracks_all():
|
||||
try:
|
||||
t = get_tags(track.path)
|
||||
# TODO this won't persist as we're updating DTO
|
||||
track.bitrate = t.bitrate
|
||||
ds.track_update(t)
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
Loading…
Reference in New Issue
Block a user