Compare commits
No commits in common. "aec994bafd62a5e8b359d30e5cb2d008e81a49a4" and "ac685426d9e185574ffd8e402d2e7da67e35c638" have entirely different histories.
aec994bafd
...
ac685426d9
@ -207,7 +207,7 @@ class InsertTrack:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TrackAndPlaylist:
|
class PlayTrack:
|
||||||
playlist_id: int
|
playlist_id: int
|
||||||
track_id: int
|
track_id: int
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ class MusicMusterSignals(QObject):
|
|||||||
search_songfacts_signal = pyqtSignal(str)
|
search_songfacts_signal = pyqtSignal(str)
|
||||||
search_wikipedia_signal = pyqtSignal(str)
|
search_wikipedia_signal = pyqtSignal(str)
|
||||||
show_warning_signal = pyqtSignal(str, str)
|
show_warning_signal = pyqtSignal(str, str)
|
||||||
signal_add_track_to_header = pyqtSignal(int)
|
signal_add_track_to_header = pyqtSignal(InsertTrack)
|
||||||
signal_begin_insert_rows = pyqtSignal(InsertRows)
|
signal_begin_insert_rows = pyqtSignal(InsertRows)
|
||||||
signal_end_insert_rows = pyqtSignal(int)
|
signal_end_insert_rows = pyqtSignal(int)
|
||||||
signal_insert_track = pyqtSignal(InsertTrack)
|
signal_insert_track = pyqtSignal(InsertTrack)
|
||||||
@ -239,7 +239,7 @@ class MusicMusterSignals(QObject):
|
|||||||
# specify that here as it requires us to import PlaylistRow from
|
# specify that here as it requires us to import PlaylistRow from
|
||||||
# playlistrow.py, which itself imports MusicMusterSignals
|
# playlistrow.py, which itself imports MusicMusterSignals
|
||||||
signal_set_next_track = pyqtSignal(object)
|
signal_set_next_track = pyqtSignal(object)
|
||||||
signal_track_started = pyqtSignal(TrackAndPlaylist)
|
signal_track_started = pyqtSignal(PlayTrack)
|
||||||
span_cells_signal = pyqtSignal(int, int, int, int, int)
|
span_cells_signal = pyqtSignal(int, int, int, int, int)
|
||||||
status_message_signal = pyqtSignal(str, int)
|
status_message_signal = pyqtSignal(str, int)
|
||||||
track_ended_signal = pyqtSignal()
|
track_ended_signal = pyqtSignal()
|
||||||
|
|||||||
@ -18,12 +18,7 @@ from PyQt6.QtWidgets import (
|
|||||||
# Third party imports
|
# Third party imports
|
||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
from classes import (
|
from classes import ApplicationError, InsertTrack, MusicMusterSignals
|
||||||
ApplicationError,
|
|
||||||
InsertTrack,
|
|
||||||
MusicMusterSignals,
|
|
||||||
TrackAndPlaylist,
|
|
||||||
)
|
|
||||||
from helpers import (
|
from helpers import (
|
||||||
get_relative_date,
|
get_relative_date,
|
||||||
ms_to_mmss,
|
ms_to_mmss,
|
||||||
@ -99,8 +94,8 @@ class TrackInsertDialog(QDialog):
|
|||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
self.resize(800, 600)
|
self.resize(800, 600)
|
||||||
|
|
||||||
width = ds.setting_get("dbdialog_width") or 800
|
width = ds.get_setting("dbdialog_width") or 800
|
||||||
height = ds.setting_get("dbdialog_height") or 800
|
height = ds.get_setting("dbdialog_height") or 800
|
||||||
self.resize(width, height)
|
self.resize(width, height)
|
||||||
|
|
||||||
self.signals = MusicMusterSignals()
|
self.signals = MusicMusterSignals()
|
||||||
@ -146,12 +141,7 @@ class TrackInsertDialog(QDialog):
|
|||||||
self.title_edit.setFocus()
|
self.title_edit.setFocus()
|
||||||
|
|
||||||
if self.add_to_header:
|
if self.add_to_header:
|
||||||
# The model will have the right-clicked row marked as a
|
self.signals.signal_add_track_to_header.emit(insert_track_data)
|
||||||
# selected_row so we only need to pass the playlist_id and
|
|
||||||
# track_id.
|
|
||||||
self.signals.signal_add_track_to_header.emit(
|
|
||||||
TrackAndPlaylist(playlist_id=self.playlist_id, track_id=track_id)
|
|
||||||
)
|
|
||||||
self.accept()
|
self.accept()
|
||||||
else:
|
else:
|
||||||
self.signals.signal_insert_track.emit(insert_track_data)
|
self.signals.signal_insert_track.emit(insert_track_data)
|
||||||
|
|||||||
@ -640,7 +640,7 @@ class DoTrackImport(QThread):
|
|||||||
if self.track_id == 0:
|
if self.track_id == 0:
|
||||||
track_dto = ds.create_track(self.destination_track_path, metadata)
|
track_dto = ds.create_track(self.destination_track_path, metadata)
|
||||||
else:
|
else:
|
||||||
track_dto = ds.track_update(
|
track_dto = ds.update_track(
|
||||||
self.destination_track_path, self.track_id, metadata
|
self.destination_track_path, self.track_id, metadata
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -168,7 +168,7 @@ def get_name(prompt: str, default: str = "") -> str | None:
|
|||||||
|
|
||||||
|
|
||||||
def get_relative_date(
|
def get_relative_date(
|
||||||
past_date: Optional[dt.datetime], reference_date: Optional[dt.datetime] = None
|
past_date: Optional[dt.datetime], now: Optional[dt.datetime] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Return how long before reference_date past_date is as string.
|
Return how long before reference_date past_date is as string.
|
||||||
@ -182,37 +182,33 @@ def get_relative_date(
|
|||||||
|
|
||||||
if not past_date or past_date == Config.EPOCH:
|
if not past_date or past_date == Config.EPOCH:
|
||||||
return "Never"
|
return "Never"
|
||||||
if not reference_date:
|
if not now:
|
||||||
reference_date = dt.datetime.now()
|
now = dt.datetime.now()
|
||||||
|
|
||||||
# Check parameters
|
# Check parameters
|
||||||
if past_date > reference_date:
|
if past_date > now:
|
||||||
raise ApplicationError("get_relative_date() past_date is after relative_date")
|
raise ApplicationError("get_relative_date() past_date is after relative_date")
|
||||||
|
|
||||||
delta = reference_date - past_date
|
delta = now - past_date
|
||||||
days = delta.days
|
days = delta.days
|
||||||
|
|
||||||
if days == 0:
|
if days == 0:
|
||||||
return Config.LAST_PLAYED_TODAY_STRING + " " + past_date.strftime("%H:%M")
|
return "(Today)"
|
||||||
|
|
||||||
elif days == 1:
|
elif days == 1:
|
||||||
return "(Yesterday)"
|
return "(Yesterday)"
|
||||||
|
|
||||||
years, days_remain_years = divmod(days, 365)
|
years, days_remain = divmod(days, 365)
|
||||||
months, days_remain_months = divmod(days_remain_years, 30)
|
months, days_final = divmod(days_remain, 30)
|
||||||
weeks, days_final = divmod(days_remain_months, 7)
|
|
||||||
|
|
||||||
parts = []
|
parts = []
|
||||||
if years:
|
if years:
|
||||||
parts.append(f"{years}y")
|
parts.append(f"{years}y")
|
||||||
if months:
|
if months:
|
||||||
parts.append(f"{months}m")
|
parts.append(f"{months}m")
|
||||||
if weeks:
|
|
||||||
parts.append(f"{weeks}w")
|
|
||||||
if days_final:
|
if days_final:
|
||||||
parts.append(f"{days_final}d")
|
parts.append(f"{days_final}d")
|
||||||
formatted = ", ".join(parts)
|
formatted = " ".join(parts)
|
||||||
return formatted
|
return f"({formatted} ago)"
|
||||||
|
|
||||||
|
|
||||||
def get_tags(path: str) -> Tags:
|
def get_tags(path: str) -> Tags:
|
||||||
|
|||||||
@ -66,8 +66,7 @@ from classes import (
|
|||||||
ApplicationError,
|
ApplicationError,
|
||||||
Filter,
|
Filter,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
PlaylistDTO,
|
PlayTrack,
|
||||||
TrackAndPlaylist,
|
|
||||||
QueryDTO,
|
QueryDTO,
|
||||||
TrackInfo,
|
TrackInfo,
|
||||||
)
|
)
|
||||||
@ -482,7 +481,7 @@ class ManageQueries(ItemlistManager):
|
|||||||
|
|
||||||
self.populate_table(query_list)
|
self.populate_table(query_list)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def delete_item(self, query_id: int) -> None:
|
def delete_item(self, query_id: int) -> None:
|
||||||
"""delete query"""
|
"""delete query"""
|
||||||
|
|
||||||
@ -579,7 +578,7 @@ class ManageTemplates(ItemlistManager):
|
|||||||
|
|
||||||
self.populate_table(template_list)
|
self.populate_table(template_list)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def delete_item(self, template_id: int) -> None:
|
def delete_item(self, template_id: int) -> None:
|
||||||
"""delete template"""
|
"""delete template"""
|
||||||
|
|
||||||
@ -1403,7 +1402,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
# # # # # # # # # # Playlist management functions # # # # # # # # # #
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def _create_playlist(self, name: str, template_id: int) -> Playlists:
|
def _create_playlist(self, name: str, template_id: int) -> Playlists:
|
||||||
"""
|
"""
|
||||||
Create a playlist in the database, populate it from the template
|
Create a playlist in the database, populate it from the template
|
||||||
@ -1412,8 +1411,8 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
return ds.create_playlist(name, template_id)
|
return ds.create_playlist(name, template_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def _open_playlist(self, playlist: PlaylistDTO, is_template: bool = False) -> int:
|
def _open_playlist(self, playlist: Playlists, is_template: bool = False) -> int:
|
||||||
"""
|
"""
|
||||||
With passed playlist:
|
With passed playlist:
|
||||||
- create models
|
- create models
|
||||||
@ -1441,7 +1440,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
return idx
|
return idx
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def create_playlist_from_template(self, template_id: int) -> None:
|
def create_playlist_from_template(self, template_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
Prompt for new playlist name and create from passed template_id
|
Prompt for new playlist name and create from passed template_id
|
||||||
@ -1461,7 +1460,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
_ = ds.create_playlist(playlist_name, template_id)
|
_ = ds.create_playlist(playlist_name, template_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def delete_playlist(self, checked: bool = False) -> None:
|
def delete_playlist(self, checked: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Delete current playlist. checked paramater passed by menu system
|
Delete current playlist. checked paramater passed by menu system
|
||||||
@ -1595,7 +1594,7 @@ class Window(QMainWindow):
|
|||||||
except subprocess.CalledProcessError as exc_info:
|
except subprocess.CalledProcessError as exc_info:
|
||||||
git_tag = str(exc_info.output)
|
git_tag = str(exc_info.output)
|
||||||
|
|
||||||
dbname = ds.db_name_get()
|
dbname = ds.get_db_name()
|
||||||
|
|
||||||
QMessageBox.information(
|
QMessageBox.information(
|
||||||
self,
|
self,
|
||||||
@ -1705,7 +1704,7 @@ class Window(QMainWindow):
|
|||||||
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
self.signals.search_songfacts_signal.connect(self.open_songfacts_browser)
|
||||||
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
self.signals.search_wikipedia_signal.connect(self.open_wikipedia_browser)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def current_row_or_end(self) -> int:
|
def current_row_or_end(self) -> int:
|
||||||
"""
|
"""
|
||||||
If a row or rows are selected, return the row number of the first
|
If a row or rows are selected, return the row number of the first
|
||||||
@ -1756,7 +1755,7 @@ class Window(QMainWindow):
|
|||||||
self.footer_section.btnDrop3db.isChecked()
|
self.footer_section.btnDrop3db.isChecked()
|
||||||
)
|
)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def enable_escape(self, enabled: bool) -> None:
|
def enable_escape(self, enabled: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Manage signal to enable/disable handling ESC character.
|
Manage signal to enable/disable handling ESC character.
|
||||||
@ -1768,7 +1767,7 @@ class Window(QMainWindow):
|
|||||||
if "clear_selection" in self.menu_actions:
|
if "clear_selection" in self.menu_actions:
|
||||||
self.menu_actions["clear_selection"].setEnabled(enabled)
|
self.menu_actions["clear_selection"].setEnabled(enabled)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def end_of_track_actions(self) -> None:
|
def end_of_track_actions(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -1905,7 +1904,7 @@ class Window(QMainWindow):
|
|||||||
dlg = TrackInsertDialog(parent=self, playlist_id=self.active_tab().playlist_id)
|
dlg = TrackInsertDialog(parent=self, playlist_id=self.active_tab().playlist_id)
|
||||||
dlg.exec()
|
dlg.exec()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def load_last_playlists(self) -> None:
|
def load_last_playlists(self) -> None:
|
||||||
"""Load the playlists that were open when app was last closed"""
|
"""Load the playlists that were open when app was last closed"""
|
||||||
|
|
||||||
@ -1956,7 +1955,7 @@ class Window(QMainWindow):
|
|||||||
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
f"mark_rows_for_moving(): {self.move_source_rows=} {self.move_source_model=}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def move_playlist_rows(self, row_numbers: list[int]) -> None:
|
def move_playlist_rows(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Move passed playlist rows to another playlist
|
Move passed playlist rows to another playlist
|
||||||
@ -2033,7 +2032,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
webbrowser.get("browser").open_new_tab(url)
|
webbrowser.get("browser").open_new_tab(url)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def paste_rows(self, checked: bool = False) -> None:
|
def paste_rows(self, checked: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Paste earlier cut rows.
|
Paste earlier cut rows.
|
||||||
@ -2067,7 +2066,7 @@ class Window(QMainWindow):
|
|||||||
if set_next_row:
|
if set_next_row:
|
||||||
to_playlist_model.set_next_row(set_next_row)
|
to_playlist_model.set_next_row(set_next_row)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def play_next(
|
def play_next(
|
||||||
self, position: Optional[float] = None, checked: bool = False
|
self, position: Optional[float] = None, checked: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -2142,7 +2141,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
# Notify others
|
# Notify others
|
||||||
self.signals.signal_track_started.emit(
|
self.signals.signal_track_started.emit(
|
||||||
TrackAndPlaylist(self.track_sequence.current.playlist_id,
|
PlayTrack(self.track_sequence.current.playlist_id,
|
||||||
self.track_sequence.current.track_id)
|
self.track_sequence.current.track_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2422,7 +2421,7 @@ class Window(QMainWindow):
|
|||||||
height = ds.get_setting("mainwindow_height") or 100
|
height = ds.get_setting("mainwindow_height") or 100
|
||||||
self.setGeometry(x, y, width, height)
|
self.setGeometry(x, y, width, height)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def set_selected_track_next(self, checked: bool = False) -> None:
|
def set_selected_track_next(self, checked: bool = False) -> None:
|
||||||
"""
|
"""
|
||||||
Set currently-selected row on visible playlist tab as next track
|
Set currently-selected row on visible playlist tab as next track
|
||||||
@ -2436,7 +2435,7 @@ class Window(QMainWindow):
|
|||||||
# else:
|
# else:
|
||||||
# log.error("No active tab")
|
# log.error("No active tab")
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
|
def set_tab_colour(self, widget: PlaylistTab, colour: QColor) -> None:
|
||||||
"""
|
"""
|
||||||
Find the tab containing the widget and set the text colour
|
Find the tab containing the widget and set the text colour
|
||||||
@ -2498,7 +2497,7 @@ class Window(QMainWindow):
|
|||||||
|
|
||||||
self.active_tab().scroll_to_top(playlist_track.row_number)
|
self.active_tab().scroll_to_top(playlist_track.row_number)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def stop(self, checked: bool = False) -> None:
|
def stop(self, checked: bool = False) -> None:
|
||||||
"""Stop playing immediately"""
|
"""Stop playing immediately"""
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ from classes import (
|
|||||||
InsertRows,
|
InsertRows,
|
||||||
InsertTrack,
|
InsertTrack,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
TrackAndPlaylist,
|
PlayTrack,
|
||||||
)
|
)
|
||||||
from config import Config
|
from config import Config
|
||||||
from helpers import (
|
from helpers import (
|
||||||
@ -102,7 +102,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.signals.signal_track_started.connect(self.track_started)
|
self.signals.signal_track_started.connect(self.track_started)
|
||||||
|
|
||||||
# Populate self.playlist_rows
|
# Populate self.playlist_rows
|
||||||
for dto in ds.playlistrows_by_playlist(self.playlist_id):
|
for dto in ds.get_playlist_rows(self.playlist_id):
|
||||||
self.playlist_rows[dto.row_number] = PlaylistRow(dto)
|
self.playlist_rows[dto.row_number] = PlaylistRow(dto)
|
||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
|
|
||||||
@ -147,17 +147,17 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return header_row
|
return header_row
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def add_track_to_header(self, track_and_playlist: TrackAndPlaylist) -> None:
|
def add_track_to_header(self, track_details: InsertTrack) -> None:
|
||||||
"""
|
"""
|
||||||
Handle signal_add_track_to_header
|
Handle signal_add_track_to_header
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if track_and_playlist.playlist_id != self.playlist_id:
|
if track_details.playlist_id != self.playlist_id:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.selected_rows:
|
if not self.selected_rows:
|
||||||
raise ApplicationError("Add track to header but no row selected")
|
return
|
||||||
|
|
||||||
if len(self.selected_rows) > 1:
|
if len(self.selected_rows) > 1:
|
||||||
self.signals.show_warning_signal.emit(
|
self.signals.show_warning_signal.emit(
|
||||||
@ -172,7 +172,9 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_row.track_id = track_and_playlist.track_id
|
if selected_row.note:
|
||||||
|
selected_row.note += " " + track_details.note
|
||||||
|
selected_row.track_id = track_details.track_id
|
||||||
|
|
||||||
# Update local copy
|
# Update local copy
|
||||||
self.refresh_row(selected_row.row_number)
|
self.refresh_row(selected_row.row_number)
|
||||||
@ -239,7 +241,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return QBrush()
|
return QBrush()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def begin_reset_model(self, playlist_id: int) -> None:
|
def begin_reset_model(self, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
Reset model if playlist_id is ours
|
Reset model if playlist_id is ours
|
||||||
@ -254,8 +256,8 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return len(Col)
|
return len(Col)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def track_started(self, play_track: TrackAndPlaylist) -> None:
|
def track_started(self, play_track: PlayTrack) -> None:
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that the current track has just
|
Notification from musicmuster that the current track has just
|
||||||
started playing
|
started playing
|
||||||
@ -294,7 +296,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.obs_scene_change(row_number)
|
self.obs_scene_change(row_number)
|
||||||
|
|
||||||
# Update Playdates in database
|
# Update Playdates in database
|
||||||
ds.playdates_update(track_id)
|
ds.update_playdates(track_id)
|
||||||
|
|
||||||
# Mark track as played in playlist
|
# Mark track as played in playlist
|
||||||
playlist_dto.played = True
|
playlist_dto.played = True
|
||||||
@ -380,7 +382,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return QVariant()
|
return QVariant()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def delete_rows(self, row_numbers: list[int]) -> None:
|
def delete_rows(self, row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Delete passed rows from model
|
Delete passed rows from model
|
||||||
@ -463,7 +465,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def end_reset_model(self, playlist_id: int) -> None:
|
def end_reset_model(self, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
End model reset if this is our playlist
|
End model reset if this is our playlist
|
||||||
@ -542,7 +544,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return boldfont
|
return boldfont
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def get_duplicate_rows(self) -> list[int]:
|
def get_duplicate_rows(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
Return a list of duplicate rows. If track appears in rows 2, 3 and 4, return [3, 4]
|
||||||
@ -563,7 +565,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def _get_new_row_number(self) -> int:
|
def _get_new_row_number(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get row number for new row.
|
Get row number for new row.
|
||||||
@ -584,7 +586,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return self.playlist_rows[row_number]
|
return self.playlist_rows[row_number]
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def get_row_track_id(self, row_number: int) -> Optional[int]:
|
def get_row_track_id(self, row_number: int) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return id of track associated with row or None if no track associated
|
Return id of track associated with row or None if no track associated
|
||||||
@ -701,7 +703,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_row(row_number, roles)
|
self.invalidate_row(row_number, roles)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def insert_row(self, track_id: Optional[int] = None, note: str = "",) -> None:
|
def insert_row(self, track_id: Optional[int] = None, note: str = "",) -> None:
|
||||||
"""
|
"""
|
||||||
Insert a row.
|
Insert a row.
|
||||||
@ -711,15 +713,18 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
super().beginInsertRows(QModelIndex(), new_row_number, new_row_number)
|
||||||
|
|
||||||
_ = ds.playlist_insert_row(
|
new_row = ds.insert_row(
|
||||||
playlist_id=self.playlist_id,
|
playlist_id=self.playlist_id,
|
||||||
row_number=new_row_number,
|
row_number=new_row_number,
|
||||||
track_id=track_id,
|
track_id=track_id,
|
||||||
note=note,
|
note=note,
|
||||||
)
|
)
|
||||||
# Need to refresh self.playlist_rows because row numbers will have
|
|
||||||
# changed
|
# Move rows down to make room
|
||||||
self.refresh_data()
|
for destination_row in range(len(self.playlist_rows), new_row_number, -1):
|
||||||
|
self.playlist_rows[destination_row] = self.playlist_rows[destination_row - 1]
|
||||||
|
# Insert into self.playlist_rows
|
||||||
|
self.playlist_rows[new_row_number] = PlaylistRow(new_row)
|
||||||
|
|
||||||
super().endInsertRows()
|
super().endInsertRows()
|
||||||
|
|
||||||
@ -736,7 +741,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
list(range(new_row_number, len(self.playlist_rows))), roles_to_invalidate
|
list(range(new_row_number, len(self.playlist_rows))), roles_to_invalidate
|
||||||
)
|
)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def invalidate_row(self, modified_row: int, roles: list[Qt.ItemDataRole]) -> None:
|
def invalidate_row(self, modified_row: int, roles: list[Qt.ItemDataRole]) -> None:
|
||||||
"""
|
"""
|
||||||
Signal to view to refresh invalidated row
|
Signal to view to refresh invalidated row
|
||||||
@ -768,7 +773,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
return self.playlist_rows[row_number].path == ""
|
return self.playlist_rows[row_number].path == ""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def is_played_row(self, row_number: int) -> bool:
|
def is_played_row(self, row_number: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Return True if row is an unplayed track row, else False
|
Return True if row is an unplayed track row, else False
|
||||||
@ -804,7 +809,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_rows(row_numbers, roles)
|
self.invalidate_rows(row_numbers, roles)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def move_rows(self, from_rows: list[int], to_row_number: int) -> bool:
|
def move_rows(self, from_rows: list[int], to_row_number: int) -> bool:
|
||||||
"""
|
"""
|
||||||
Move the playlist rows in from_rows to to_row. Return True if successful
|
Move the playlist rows in from_rows to to_row. Return True if successful
|
||||||
@ -854,7 +859,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
# self.invalidate_rows(list(row_map.keys()), roles)
|
# self.invalidate_rows(list(row_map.keys()), roles)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def move_rows_between_playlists(
|
def move_rows_between_playlists(
|
||||||
self,
|
self,
|
||||||
from_rows: list[PlaylistRow],
|
from_rows: list[PlaylistRow],
|
||||||
@ -922,7 +927,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
super().endInsertRows()
|
super().endInsertRows()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def move_track_add_note(
|
def move_track_add_note(
|
||||||
self, new_row_number: int, existing_plr: PlaylistRow, note: str
|
self, new_row_number: int, existing_plr: PlaylistRow, note: str
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -941,7 +946,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.move_rows([existing_plr.row_number], new_row_number)
|
self.move_rows([existing_plr.row_number], new_row_number)
|
||||||
self.signals.resize_rows_signal.emit(self.playlist_id)
|
self.signals.resize_rows_signal.emit(self.playlist_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def obs_scene_change(self, row_number: int) -> None:
|
def obs_scene_change(self, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
Check this row and any preceding headers for OBS scene change command
|
Check this row and any preceding headers for OBS scene change command
|
||||||
@ -976,7 +981,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
log.warning(f"{self}: OBS connection refused")
|
log.warning(f"{self}: OBS connection refused")
|
||||||
return
|
return
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def previous_track_ended(self) -> None:
|
def previous_track_ended(self) -> None:
|
||||||
"""
|
"""
|
||||||
Notification from musicmuster that the previous track has ended.
|
Notification from musicmuster that the previous track has ended.
|
||||||
@ -1020,7 +1025,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
# build a new playlist_rows
|
# build a new playlist_rows
|
||||||
new_playlist_rows: dict[int, PlaylistRow] = {}
|
new_playlist_rows: dict[int, PlaylistRow] = {}
|
||||||
for dto in ds.playlistrows_by_playlist(self.playlist_id):
|
for dto in ds.get_playlist_rows(self.playlist_id):
|
||||||
if dto.playlistrow_id not in plrid_to_row:
|
if dto.playlistrow_id not in plrid_to_row:
|
||||||
new_playlist_rows[dto.row_number] = PlaylistRow(dto)
|
new_playlist_rows[dto.row_number] = PlaylistRow(dto)
|
||||||
else:
|
else:
|
||||||
@ -1041,7 +1046,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
self.playlist_rows[row_number] = PlaylistRow(refreshed_row)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def remove_track(self, row_number: int) -> None:
|
def remove_track(self, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
Remove track from row, retaining row as a header row
|
Remove track from row, retaining row as a header row
|
||||||
@ -1072,7 +1077,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.invalidate_row(row_number, roles)
|
self.invalidate_row(row_number, roles)
|
||||||
self.signals.resize_rows_signal.emit(self.playlist_id)
|
self.signals.resize_rows_signal.emit(self.playlist_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def reset_track_sequence_row_numbers(self) -> None:
|
def reset_track_sequence_row_numbers(self) -> None:
|
||||||
"""
|
"""
|
||||||
Signal handler for when row ordering has changed.
|
Signal handler for when row ordering has changed.
|
||||||
@ -1112,7 +1117,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
]
|
]
|
||||||
self.invalidate_rows(row_numbers, roles)
|
self.invalidate_rows(row_numbers, roles)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def _reversed_contiguous_row_groups(
|
def _reversed_contiguous_row_groups(
|
||||||
self, row_numbers: list[int]
|
self, row_numbers: list[int]
|
||||||
) -> list[list[int]]:
|
) -> list[list[int]]:
|
||||||
@ -1253,7 +1258,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def set_selected_rows(self, playlist_id: int, selected_row_numbers: list[int]) -> None:
|
def set_selected_rows(self, playlist_id: int, selected_row_numbers: list[int]) -> None:
|
||||||
"""
|
"""
|
||||||
Handle signal_playlist_selected_rows to keep track of which rows
|
Handle signal_playlist_selected_rows to keep track of which rows
|
||||||
@ -1265,7 +1270,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
self.selected_rows = [self.playlist_rows[a] for a in selected_row_numbers]
|
self.selected_rows = [self.playlist_rows[a] for a in selected_row_numbers]
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def set_next_row(self, playlist_id: int) -> None:
|
def set_next_row(self, playlist_id: int) -> None:
|
||||||
"""
|
"""
|
||||||
Handle signal_set_next_row
|
Handle signal_set_next_row
|
||||||
@ -1310,7 +1315,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
self.signals.next_track_changed_signal.emit()
|
self.signals.next_track_changed_signal.emit()
|
||||||
self.update_track_times()
|
self.update_track_times()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def setData(
|
def setData(
|
||||||
self,
|
self,
|
||||||
index: QModelIndex,
|
index: QModelIndex,
|
||||||
@ -1447,7 +1452,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
|
|
||||||
return ds.get_last_played_dates(track_id)
|
return ds.get_last_played_dates(track_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def update_or_insert(self, track_id: int, row_number: int) -> None:
|
def update_or_insert(self, track_id: int, row_number: int) -> None:
|
||||||
"""
|
"""
|
||||||
If the passed track_id exists in this playlist, update the
|
If the passed track_id exists in this playlist, update the
|
||||||
@ -1471,7 +1476,7 @@ class PlaylistModel(QAbstractTableModel):
|
|||||||
else:
|
else:
|
||||||
self.insert_row(track_id=track_id)
|
self.insert_row(track_id=track_id)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def update_track_times(self) -> None:
|
def update_track_times(self) -> None:
|
||||||
"""
|
"""
|
||||||
Update track start/end times in self.playlist_rows
|
Update track start/end times in self.playlist_rows
|
||||||
|
|||||||
@ -161,7 +161,7 @@ class PlaylistRow:
|
|||||||
if self.track_id > 0:
|
if self.track_id > 0:
|
||||||
raise ApplicationError("Attempting to add track to row with existing track ({self=}")
|
raise ApplicationError("Attempting to add track to row with existing track ({self=}")
|
||||||
|
|
||||||
ds.add_track_to_header(playlistrow_id=self.playlistrow_id, track_id=track_id)
|
ds.add_track_to_header(track_id)
|
||||||
|
|
||||||
# Need to update with track information
|
# Need to update with track information
|
||||||
track = ds.track_by_id(track_id)
|
track = ds.track_by_id(track_id)
|
||||||
@ -169,6 +169,12 @@ class PlaylistRow:
|
|||||||
for attr, value in track.__dataclass_fields__.items():
|
for attr, value in track.__dataclass_fields__.items():
|
||||||
setattr(self, attr, value)
|
setattr(self, attr, value)
|
||||||
|
|
||||||
|
# TODO: set up write access to track_id. Should only update if
|
||||||
|
# track_id == 0. Need to update all other track fields at the
|
||||||
|
# same time.
|
||||||
|
print(f"set track_id attribute for {self=}, {value=}")
|
||||||
|
pass
|
||||||
|
|
||||||
# Expose PlaylistRowDTO fields as properties
|
# Expose PlaylistRowDTO fields as properties
|
||||||
@property
|
@property
|
||||||
def note(self):
|
def note(self):
|
||||||
@ -577,7 +583,7 @@ class TrackSequence:
|
|||||||
for ts in [self.next, self.current, self.previous]:
|
for ts in [self.next, self.current, self.previous]:
|
||||||
if not ts:
|
if not ts:
|
||||||
continue
|
continue
|
||||||
playlist_row_dto = ds.playlistrow_by_id(ts.playlistrow_id)
|
playlist_row_dto = ds.get_playlist_row(ts.playlistrow_id)
|
||||||
if not playlist_row_dto:
|
if not playlist_row_dto:
|
||||||
raise ApplicationError(f"(Can't retrieve PlaylistRows entry, {self=}")
|
raise ApplicationError(f"(Can't retrieve PlaylistRows entry, {self=}")
|
||||||
ts = PlaylistRow(playlist_row_dto)
|
ts = PlaylistRow(playlist_row_dto)
|
||||||
|
|||||||
@ -40,7 +40,7 @@ from classes import (
|
|||||||
Col,
|
Col,
|
||||||
MusicMusterSignals,
|
MusicMusterSignals,
|
||||||
PlaylistStyle,
|
PlaylistStyle,
|
||||||
TrackAndPlaylist,
|
PlayTrack,
|
||||||
TrackInfo
|
TrackInfo
|
||||||
)
|
)
|
||||||
from config import Config
|
from config import Config
|
||||||
@ -367,7 +367,7 @@ class PlaylistTab(QTableView):
|
|||||||
# Deselect edited line
|
# Deselect edited line
|
||||||
self.clear_selection()
|
self.clear_selection()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def dropEvent(self, event: Optional[QDropEvent]) -> None:
|
def dropEvent(self, event: Optional[QDropEvent]) -> None:
|
||||||
"""
|
"""
|
||||||
Move dropped rows
|
Move dropped rows
|
||||||
@ -519,9 +519,7 @@ class PlaylistTab(QTableView):
|
|||||||
return menu_item
|
return menu_item
|
||||||
|
|
||||||
def _add_track(self) -> None:
|
def _add_track(self) -> None:
|
||||||
"""
|
"""Add a track to a section header making it a normal track row"""
|
||||||
Add a track to a section header making it a normal track row.
|
|
||||||
"""
|
|
||||||
|
|
||||||
dlg = TrackInsertDialog(
|
dlg = TrackInsertDialog(
|
||||||
parent=self.musicmuster,
|
parent=self.musicmuster,
|
||||||
@ -690,7 +688,7 @@ class PlaylistTab(QTableView):
|
|||||||
self.resizeRowsToContents()
|
self.resizeRowsToContents()
|
||||||
|
|
||||||
# Save settings
|
# Save settings
|
||||||
ds.setting_set(
|
ds.set_setting(
|
||||||
f"playlist_col_{column_number}_width", self.columnWidth(column_number)
|
f"playlist_col_{column_number}_width", self.columnWidth(column_number)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -725,8 +723,8 @@ class PlaylistTab(QTableView):
|
|||||||
cb.clear(mode=cb.Mode.Clipboard)
|
cb.clear(mode=cb.Mode.Clipboard)
|
||||||
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
cb.setText(track_path, mode=cb.Mode.Clipboard)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def track_started(self, play_track: TrackAndPlaylist) -> None:
|
def track_started(self, play_track: PlayTrack) -> None:
|
||||||
"""
|
"""
|
||||||
Called when track starts playing
|
Called when track starts playing
|
||||||
"""
|
"""
|
||||||
@ -818,7 +816,7 @@ class PlaylistTab(QTableView):
|
|||||||
else:
|
else:
|
||||||
return TrackInfo(track_id, selected_row)
|
return TrackInfo(track_id, selected_row)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def get_selected_row(self) -> Optional[int]:
|
def get_selected_row(self) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return selected row number. If no rows or multiple rows selected, return None
|
Return selected row number. If no rows or multiple rows selected, return None
|
||||||
@ -830,7 +828,7 @@ class PlaylistTab(QTableView):
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def get_selected_rows(self) -> list[int]:
|
def get_selected_rows(self) -> list[int]:
|
||||||
"""Return a list of model-selected row numbers sorted by row"""
|
"""Return a list of model-selected row numbers sorted by row"""
|
||||||
|
|
||||||
@ -843,7 +841,7 @@ class PlaylistTab(QTableView):
|
|||||||
|
|
||||||
return sorted(list(set([self.model().mapToSource(a).row() for a in selected_indexes])))
|
return sorted(list(set([self.model().mapToSource(a).row() for a in selected_indexes])))
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def get_top_visible_row(self) -> int:
|
def get_top_visible_row(self) -> int:
|
||||||
"""
|
"""
|
||||||
Get the viewport of the table view
|
Get the viewport of the table view
|
||||||
@ -1012,7 +1010,7 @@ class PlaylistTab(QTableView):
|
|||||||
# Reset selection mode
|
# Reset selection mode
|
||||||
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def source_model_selected_row_number(self) -> Optional[int]:
|
def source_model_selected_row_number(self) -> Optional[int]:
|
||||||
"""
|
"""
|
||||||
Return the model row number corresponding to the selected row or None
|
Return the model row number corresponding to the selected row or None
|
||||||
@ -1023,7 +1021,7 @@ class PlaylistTab(QTableView):
|
|||||||
return None
|
return None
|
||||||
return self.model().mapToSource(selected_index).row()
|
return self.model().mapToSource(selected_index).row()
|
||||||
|
|
||||||
# @log_call
|
@log_call
|
||||||
def selected_model_row_numbers(self) -> list[int]:
|
def selected_model_row_numbers(self) -> list[int]:
|
||||||
"""
|
"""
|
||||||
Return a list of model row numbers corresponding to the selected rows or
|
Return a list of model row numbers corresponding to the selected rows or
|
||||||
|
|||||||
@ -271,4 +271,4 @@ class QuerylistModel(QAbstractTableModel):
|
|||||||
track_id = self.querylist_rows[row].track_id
|
track_id = self.querylist_rows[row].track_id
|
||||||
if not track_id:
|
if not track_id:
|
||||||
return QVariant()
|
return QVariant()
|
||||||
return ds.playdates_get_last(track_id)
|
return ds.get_last_played_dates(track_id)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ def check_db() -> None:
|
|||||||
Check all paths in database exist
|
Check all paths in database exist
|
||||||
"""
|
"""
|
||||||
|
|
||||||
db_paths = set([a.path for a in ds.tracks_all()])
|
db_paths = set([a.path for a in ds.get_all_tracks()])
|
||||||
|
|
||||||
os_paths_list = []
|
os_paths_list = []
|
||||||
for root, _dirs, files in os.walk(Config.ROOT):
|
for root, _dirs, files in os.walk(Config.ROOT):
|
||||||
@ -88,7 +88,7 @@ def update_bitrates() -> None:
|
|||||||
Update bitrates on all tracks in database
|
Update bitrates on all tracks in database
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for track in ds.tracks_all():
|
for track in ds.get_all_tracks():
|
||||||
try:
|
try:
|
||||||
t = get_tags(track.path)
|
t = get_tags(track.path)
|
||||||
# TODO this won't persist as we're updating DTO
|
# TODO this won't persist as we're updating DTO
|
||||||
|
|||||||
@ -1,480 +0,0 @@
|
|||||||
"""
|
|
||||||
Tests are named 'test_nnn_xxxx' where 'nn n' is a number. This is used to ensure that
|
|
||||||
the tests run in order as we rely (in some cases) upon the results of an earlier test.
|
|
||||||
Yes, we shouldn't do that.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Standard library imports
|
|
||||||
import os
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
import unittest
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
# PyQt imports
|
|
||||||
from PyQt6.QtWidgets import QDialog, QFileDialog
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from mutagen.mp3 import MP3 # type: ignore
|
|
||||||
import pytest
|
|
||||||
from pytestqt.plugin import QtBot # type: ignore
|
|
||||||
|
|
||||||
# App imports
|
|
||||||
from app import ds, musicmuster
|
|
||||||
from app.models import (
|
|
||||||
db,
|
|
||||||
Tracks,
|
|
||||||
)
|
|
||||||
from config import Config
|
|
||||||
from file_importer import FileImporter
|
|
||||||
|
|
||||||
|
|
||||||
# Custom fixture to adapt qtbot for use with unittest.TestCase
|
|
||||||
@pytest.fixture(scope="class")
|
|
||||||
def qtbot_adapter(qapp, request):
|
|
||||||
"""Adapt qtbot fixture for usefixtures and unittest.TestCase"""
|
|
||||||
request.cls.qtbot = QtBot(request)
|
|
||||||
|
|
||||||
|
|
||||||
# Fixture for tmp_path to be available in the class
|
|
||||||
@pytest.fixture(scope="class")
|
|
||||||
def class_tmp_path(request, tmp_path_factory):
|
|
||||||
"""Provide a class-wide tmp_path"""
|
|
||||||
request.cls.tmp_path = tmp_path_factory.mktemp("pytest_tmp")
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("qtbot_adapter", "class_tmp_path")
|
|
||||||
class MyTestCase(unittest.TestCase):
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
"""Runs once before any test in this class"""
|
|
||||||
|
|
||||||
db.create_all()
|
|
||||||
|
|
||||||
cls.widget = musicmuster.Window()
|
|
||||||
|
|
||||||
# Create a playlist for all tests
|
|
||||||
playlist_name = "file importer playlist"
|
|
||||||
playlist = ds.playlist_create(name=playlist_name, template_id=0)
|
|
||||||
cls.widget._open_playlist(playlist)
|
|
||||||
|
|
||||||
# Create our musicstore
|
|
||||||
cls.import_source = tempfile.mkdtemp(suffix="_MMsource_pytest", dir="/tmp")
|
|
||||||
Config.REPLACE_FILES_DEFAULT_SOURCE = cls.import_source
|
|
||||||
cls.musicstore = tempfile.mkdtemp(suffix="_MMstore_pytest", dir="/tmp")
|
|
||||||
Config.IMPORT_DESTINATION = cls.musicstore
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
"""Runs once after all tests"""
|
|
||||||
|
|
||||||
db.drop_all()
|
|
||||||
shutil.rmtree(cls.musicstore)
|
|
||||||
shutil.rmtree(cls.import_source)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Runs before each test"""
|
|
||||||
|
|
||||||
with self.qtbot.waitExposed(self.widget):
|
|
||||||
self.widget.show()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
"""Runs after each test"""
|
|
||||||
self.widget.close() # Close UI to prevent side effects
|
|
||||||
|
|
||||||
def wait_for_workers(self, timeout: int = 10000):
|
|
||||||
"""
|
|
||||||
Let import threads workers run to completion
|
|
||||||
"""
|
|
||||||
|
|
||||||
def workers_empty():
|
|
||||||
assert FileImporter.workers == {}
|
|
||||||
|
|
||||||
self.qtbot.waitUntil(workers_empty, timeout=timeout)
|
|
||||||
|
|
||||||
def test_001_import_no_files(self):
|
|
||||||
"""Try importing with no files to import"""
|
|
||||||
|
|
||||||
with patch("file_importer.show_OK") as mock_show_ok:
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
mock_show_ok.assert_called_once_with(
|
|
||||||
"File import",
|
|
||||||
f"No files in {Config.REPLACE_FILES_DEFAULT_SOURCE} to import",
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_002_import_file_and_cancel(self):
|
|
||||||
"""Cancel file import"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/isa.mp3"
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
|
|
||||||
with (
|
|
||||||
patch("file_importer.PickMatch") as MockPickMatch,
|
|
||||||
patch("file_importer.show_OK") as mock_show_ok,
|
|
||||||
):
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Rejected
|
|
||||||
mock_dialog_instance.selected_track_id = -1 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="I'm So Afraid (Fleetwood Mac)",
|
|
||||||
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
|
|
||||||
default=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
# Ensure selected_track_id was accessed after dialog.exec()
|
|
||||||
assert mock_dialog_instance.selected_track_id < 0
|
|
||||||
|
|
||||||
mock_show_ok.assert_called_once_with(
|
|
||||||
"File not imported",
|
|
||||||
"isa.mp3 will not be imported because you asked not to import this file",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_003_import_first_file(self):
|
|
||||||
"""Import file into empty directory"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/isa.mp3"
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
|
|
||||||
with patch("file_importer.PickMatch") as MockPickMatch:
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
|
|
||||||
mock_dialog_instance.selected_track_id = 0 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="I'm So Afraid (Fleetwood Mac)",
|
|
||||||
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
|
|
||||||
default=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
# Ensure selected_track_id was accessed after dialog.exec()
|
|
||||||
assert mock_dialog_instance.selected_track_id == 0
|
|
||||||
|
|
||||||
self.wait_for_workers()
|
|
||||||
|
|
||||||
# Check track was imported
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
assert len(tracks) == 1
|
|
||||||
track = tracks[0]
|
|
||||||
assert track.title == "I'm So Afraid"
|
|
||||||
assert track.artist == "Fleetwood Mac"
|
|
||||||
track_file = os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
)
|
|
||||||
assert track.path == track_file
|
|
||||||
assert os.path.exists(track_file)
|
|
||||||
assert os.listdir(self.import_source) == []
|
|
||||||
|
|
||||||
def test_004_import_second_file(self):
|
|
||||||
"""Import a second file"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
|
|
||||||
with patch("file_importer.PickMatch") as MockPickMatch:
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
|
|
||||||
mock_dialog_instance.selected_track_id = 0 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="The Lovecats (The Cure)",
|
|
||||||
choices=[("Do not import", -1, ""), ("Import as new track", 0, "")],
|
|
||||||
default=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
# Ensure selected_track_id was accessed after dialog.exec()
|
|
||||||
assert mock_dialog_instance.selected_track_id == 0
|
|
||||||
|
|
||||||
self.wait_for_workers()
|
|
||||||
|
|
||||||
# Check track was imported
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
assert len(tracks) == 2
|
|
||||||
track = tracks[1]
|
|
||||||
assert track.title == "The Lovecats"
|
|
||||||
assert track.artist == "The Cure"
|
|
||||||
track_file = os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
)
|
|
||||||
assert track.path == track_file
|
|
||||||
assert os.path.exists(track_file)
|
|
||||||
assert os.listdir(self.import_source) == []
|
|
||||||
|
|
||||||
def test_005_replace_file(self):
|
|
||||||
"""Import the same file again and update existing track"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
|
|
||||||
with patch("file_importer.PickMatch") as MockPickMatch:
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
|
|
||||||
mock_dialog_instance.selected_track_id = 2 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="The Lovecats (The Cure)",
|
|
||||||
choices=[
|
|
||||||
("Do not import", -1, ""),
|
|
||||||
("Import as new track", 0, ""),
|
|
||||||
(
|
|
||||||
"The Lovecats (The Cure) (100%)",
|
|
||||||
2,
|
|
||||||
os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
default=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
self.wait_for_workers()
|
|
||||||
|
|
||||||
# Check track was imported
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
assert len(tracks) == 2
|
|
||||||
track = tracks[1]
|
|
||||||
assert track.title == "The Lovecats"
|
|
||||||
assert track.artist == "The Cure"
|
|
||||||
assert track.track_id == 2
|
|
||||||
track_file = os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
)
|
|
||||||
assert track.path == track_file
|
|
||||||
assert os.path.exists(track_file)
|
|
||||||
assert os.listdir(self.import_source) == []
|
|
||||||
|
|
||||||
def test_006_import_file_no_tags(self) -> None:
|
|
||||||
"""Try to import untagged file"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
test_filename = os.path.basename(test_track_path)
|
|
||||||
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
import_file = os.path.join(self.import_source, test_filename)
|
|
||||||
assert os.path.exists(import_file)
|
|
||||||
|
|
||||||
# Remove tags
|
|
||||||
src = MP3(import_file)
|
|
||||||
src.delete()
|
|
||||||
src.save()
|
|
||||||
|
|
||||||
with patch("file_importer.show_OK") as mock_show_ok:
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
mock_show_ok.assert_called_once_with(
|
|
||||||
"File not imported",
|
|
||||||
f"{test_filename} will not be imported because of tag errors "
|
|
||||||
f"(Missing tags in {import_file})",
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_007_import_unreadable_file(self) -> None:
|
|
||||||
"""Import unreadable file"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
test_filename = os.path.basename(test_track_path)
|
|
||||||
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
import_file = os.path.join(self.import_source, test_filename)
|
|
||||||
assert os.path.exists(import_file)
|
|
||||||
|
|
||||||
# Make undreadable
|
|
||||||
os.chmod(import_file, 0)
|
|
||||||
|
|
||||||
with patch("file_importer.show_OK") as mock_show_ok:
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
mock_show_ok.assert_called_once_with(
|
|
||||||
"File not imported",
|
|
||||||
f"{test_filename} will not be imported because {import_file} is unreadable",
|
|
||||||
)
|
|
||||||
|
|
||||||
# clean up
|
|
||||||
os.chmod(import_file, 0o777)
|
|
||||||
os.unlink(import_file)
|
|
||||||
|
|
||||||
def test_008_import_new_file_existing_destination(self) -> None:
|
|
||||||
"""Import duplicate file"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
test_filename = os.path.basename(test_track_path)
|
|
||||||
new_destination = os.path.join(self.musicstore, "lc2.mp3")
|
|
||||||
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
import_file = os.path.join(self.import_source, test_filename)
|
|
||||||
assert os.path.exists(import_file)
|
|
||||||
|
|
||||||
with (
|
|
||||||
patch("file_importer.PickMatch") as MockPickMatch,
|
|
||||||
patch.object(
|
|
||||||
QFileDialog, "getSaveFileName", return_value=(new_destination, "")
|
|
||||||
) as mock_file_dialog,
|
|
||||||
patch("file_importer.show_OK") as mock_show_ok,
|
|
||||||
):
|
|
||||||
mock_file_dialog.return_value = (
|
|
||||||
new_destination,
|
|
||||||
"",
|
|
||||||
) # Ensure mock correctly returns expected value
|
|
||||||
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
|
|
||||||
mock_dialog_instance.selected_track_id = 0 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="The Lovecats (The Cure)",
|
|
||||||
choices=[
|
|
||||||
("Do not import", -1, ""),
|
|
||||||
("Import as new track", 0, ""),
|
|
||||||
(
|
|
||||||
"The Lovecats (The Cure) (100%)",
|
|
||||||
2,
|
|
||||||
os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
default=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
destination = os.path.join(self.musicstore, test_filename)
|
|
||||||
mock_show_ok.assert_called_once_with(
|
|
||||||
title="Desintation path exists",
|
|
||||||
msg=f"New import requested but default destination path ({destination}) "
|
|
||||||
"already exists. Click OK and choose where to save this track",
|
|
||||||
parent=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.wait_for_workers()
|
|
||||||
|
|
||||||
# Ensure QFileDialog was called and returned expected value
|
|
||||||
assert mock_file_dialog.called # Ensure the mock was used
|
|
||||||
result = mock_file_dialog()
|
|
||||||
assert result[0] == new_destination # Validate return value
|
|
||||||
|
|
||||||
# Check track was imported
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
track = tracks[2]
|
|
||||||
assert track.title == "The Lovecats"
|
|
||||||
assert track.artist == "The Cure"
|
|
||||||
assert track.track_id == 3
|
|
||||||
assert track.path == new_destination
|
|
||||||
assert os.path.exists(new_destination)
|
|
||||||
assert os.listdir(self.import_source) == []
|
|
||||||
|
|
||||||
# Remove file so as not to interfere with later tests
|
|
||||||
ds.delete(track)
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
assert len(tracks) == 2
|
|
||||||
|
|
||||||
os.unlink(new_destination)
|
|
||||||
assert not os.path.exists(new_destination)
|
|
||||||
|
|
||||||
def test_009_import_similar_file(self) -> None:
|
|
||||||
"""Import file with similar, but different, title"""
|
|
||||||
|
|
||||||
test_track_path = "testdata/lovecats.mp3"
|
|
||||||
test_filename = os.path.basename(test_track_path)
|
|
||||||
|
|
||||||
shutil.copy(test_track_path, self.import_source)
|
|
||||||
import_file = os.path.join(self.import_source, test_filename)
|
|
||||||
assert os.path.exists(import_file)
|
|
||||||
|
|
||||||
# Change title tag
|
|
||||||
src = MP3(import_file)
|
|
||||||
src["TIT2"].text[0] += " xyz"
|
|
||||||
src.save()
|
|
||||||
|
|
||||||
with patch("file_importer.PickMatch") as MockPickMatch:
|
|
||||||
# Create a mock instance of PickMatch
|
|
||||||
mock_dialog_instance = MagicMock()
|
|
||||||
MockPickMatch.return_value = mock_dialog_instance
|
|
||||||
|
|
||||||
# Simulate the user clicking OK in the dialog
|
|
||||||
mock_dialog_instance.exec.return_value = QDialog.DialogCode.Accepted
|
|
||||||
mock_dialog_instance.selected_track_id = 2 # Simulated return value
|
|
||||||
|
|
||||||
self.widget.import_files_wrapper()
|
|
||||||
|
|
||||||
# Ensure PickMatch was instantiated correctly
|
|
||||||
MockPickMatch.assert_called_once_with(
|
|
||||||
new_track_description="The Lovecats xyz (The Cure)",
|
|
||||||
choices=[
|
|
||||||
("Do not import", -1, ""),
|
|
||||||
("Import as new track", 0, ""),
|
|
||||||
(
|
|
||||||
"The Lovecats (The Cure) (93%)",
|
|
||||||
2,
|
|
||||||
os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
default=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify exec() was called
|
|
||||||
mock_dialog_instance.exec.assert_called_once()
|
|
||||||
|
|
||||||
self.wait_for_workers()
|
|
||||||
|
|
||||||
# Check track was imported
|
|
||||||
tracks = ds.get_all_tracks()
|
|
||||||
assert len(tracks) == 2
|
|
||||||
track = tracks[1]
|
|
||||||
assert track.title == "The Lovecats xyz"
|
|
||||||
assert track.artist == "The Cure"
|
|
||||||
assert track.track_id == 2
|
|
||||||
track_file = os.path.join(
|
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
|
||||||
)
|
|
||||||
assert track.path == track_file
|
|
||||||
assert os.path.exists(track_file)
|
|
||||||
assert os.listdir(self.import_source) == []
|
|
||||||
@ -20,8 +20,12 @@ import pytest
|
|||||||
from pytestqt.plugin import QtBot # type: ignore
|
from pytestqt.plugin import QtBot # type: ignore
|
||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
from app import ds, musicmuster
|
from app import musicmuster
|
||||||
from app.models import db
|
from app.models import (
|
||||||
|
db,
|
||||||
|
Playlists,
|
||||||
|
Tracks,
|
||||||
|
)
|
||||||
from config import Config
|
from config import Config
|
||||||
from file_importer import FileImporter
|
from file_importer import FileImporter
|
||||||
|
|
||||||
@ -52,8 +56,9 @@ class MyTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
# Create a playlist for all tests
|
# Create a playlist for all tests
|
||||||
playlist_name = "file importer playlist"
|
playlist_name = "file importer playlist"
|
||||||
playlist = ds.playlist_create(name=playlist_name, template_id=0)
|
with db.Session() as session:
|
||||||
cls.widget._open_playlist(playlist)
|
playlist = Playlists(session=session, name=playlist_name, template_id=0)
|
||||||
|
cls.widget._open_playlist(playlist)
|
||||||
|
|
||||||
# Create our musicstore
|
# Create our musicstore
|
||||||
cls.import_source = tempfile.mkdtemp(suffix="_MMsource_pytest", dir="/tmp")
|
cls.import_source = tempfile.mkdtemp(suffix="_MMsource_pytest", dir="/tmp")
|
||||||
@ -171,17 +176,18 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self.wait_for_workers()
|
self.wait_for_workers()
|
||||||
|
|
||||||
# Check track was imported
|
# Check track was imported
|
||||||
tracks = ds.get_all_tracks()
|
with db.Session() as session:
|
||||||
assert len(tracks) == 1
|
tracks = Tracks.get_all(session)
|
||||||
track = tracks[0]
|
assert len(tracks) == 1
|
||||||
assert track.title == "I'm So Afraid"
|
track = tracks[0]
|
||||||
assert track.artist == "Fleetwood Mac"
|
assert track.title == "I'm So Afraid"
|
||||||
track_file = os.path.join(
|
assert track.artist == "Fleetwood Mac"
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
track_file = os.path.join(
|
||||||
)
|
self.musicstore, os.path.basename(test_track_path)
|
||||||
assert track.path == track_file
|
)
|
||||||
assert os.path.exists(track_file)
|
assert track.path == track_file
|
||||||
assert os.listdir(self.import_source) == []
|
assert os.path.exists(track_file)
|
||||||
|
assert os.listdir(self.import_source) == []
|
||||||
|
|
||||||
def test_004_import_second_file(self):
|
def test_004_import_second_file(self):
|
||||||
"""Import a second file"""
|
"""Import a second file"""
|
||||||
@ -216,17 +222,18 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self.wait_for_workers()
|
self.wait_for_workers()
|
||||||
|
|
||||||
# Check track was imported
|
# Check track was imported
|
||||||
tracks = ds.get_all_tracks()
|
with db.Session() as session:
|
||||||
assert len(tracks) == 2
|
tracks = Tracks.get_all(session)
|
||||||
track = tracks[1]
|
assert len(tracks) == 2
|
||||||
assert track.title == "The Lovecats"
|
track = tracks[1]
|
||||||
assert track.artist == "The Cure"
|
assert track.title == "The Lovecats"
|
||||||
track_file = os.path.join(
|
assert track.artist == "The Cure"
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
track_file = os.path.join(
|
||||||
)
|
self.musicstore, os.path.basename(test_track_path)
|
||||||
assert track.path == track_file
|
)
|
||||||
assert os.path.exists(track_file)
|
assert track.path == track_file
|
||||||
assert os.listdir(self.import_source) == []
|
assert os.path.exists(track_file)
|
||||||
|
assert os.listdir(self.import_source) == []
|
||||||
|
|
||||||
def test_005_replace_file(self):
|
def test_005_replace_file(self):
|
||||||
"""Import the same file again and update existing track"""
|
"""Import the same file again and update existing track"""
|
||||||
@ -268,18 +275,19 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self.wait_for_workers()
|
self.wait_for_workers()
|
||||||
|
|
||||||
# Check track was imported
|
# Check track was imported
|
||||||
tracks = ds.get_all_tracks()
|
with db.Session() as session:
|
||||||
assert len(tracks) == 2
|
tracks = Tracks.get_all(session)
|
||||||
track = tracks[1]
|
assert len(tracks) == 2
|
||||||
assert track.title == "The Lovecats"
|
track = tracks[1]
|
||||||
assert track.artist == "The Cure"
|
assert track.title == "The Lovecats"
|
||||||
assert track.track_id == 2
|
assert track.artist == "The Cure"
|
||||||
track_file = os.path.join(
|
assert track.id == 2
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
track_file = os.path.join(
|
||||||
)
|
self.musicstore, os.path.basename(test_track_path)
|
||||||
assert track.path == track_file
|
)
|
||||||
assert os.path.exists(track_file)
|
assert track.path == track_file
|
||||||
assert os.listdir(self.import_source) == []
|
assert os.path.exists(track_file)
|
||||||
|
assert os.listdir(self.import_source) == []
|
||||||
|
|
||||||
def test_006_import_file_no_tags(self) -> None:
|
def test_006_import_file_no_tags(self) -> None:
|
||||||
"""Try to import untagged file"""
|
"""Try to import untagged file"""
|
||||||
@ -397,22 +405,25 @@ class MyTestCase(unittest.TestCase):
|
|||||||
assert result[0] == new_destination # Validate return value
|
assert result[0] == new_destination # Validate return value
|
||||||
|
|
||||||
# Check track was imported
|
# Check track was imported
|
||||||
tracks = ds.get_all_tracks()
|
with db.Session() as session:
|
||||||
track = tracks[2]
|
tracks = Tracks.get_all(session)
|
||||||
assert track.title == "The Lovecats"
|
assert len(tracks) == 3
|
||||||
assert track.artist == "The Cure"
|
track = tracks[2]
|
||||||
assert track.track_id == 3
|
assert track.title == "The Lovecats"
|
||||||
assert track.path == new_destination
|
assert track.artist == "The Cure"
|
||||||
assert os.path.exists(new_destination)
|
assert track.id == 3
|
||||||
assert os.listdir(self.import_source) == []
|
assert track.path == new_destination
|
||||||
|
assert os.path.exists(new_destination)
|
||||||
|
assert os.listdir(self.import_source) == []
|
||||||
|
|
||||||
# Remove file so as not to interfere with later tests
|
# Remove file so as not to interfere with later tests
|
||||||
ds.delete(track)
|
session.delete(track)
|
||||||
tracks = ds.get_all_tracks()
|
tracks = Tracks.get_all(session)
|
||||||
assert len(tracks) == 2
|
assert len(tracks) == 2
|
||||||
|
session.commit()
|
||||||
|
|
||||||
os.unlink(new_destination)
|
os.unlink(new_destination)
|
||||||
assert not os.path.exists(new_destination)
|
assert not os.path.exists(new_destination)
|
||||||
|
|
||||||
def test_009_import_similar_file(self) -> None:
|
def test_009_import_similar_file(self) -> None:
|
||||||
"""Import file with similar, but different, title"""
|
"""Import file with similar, but different, title"""
|
||||||
@ -463,15 +474,16 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self.wait_for_workers()
|
self.wait_for_workers()
|
||||||
|
|
||||||
# Check track was imported
|
# Check track was imported
|
||||||
tracks = ds.get_all_tracks()
|
with db.Session() as session:
|
||||||
assert len(tracks) == 2
|
tracks = Tracks.get_all(session)
|
||||||
track = tracks[1]
|
assert len(tracks) == 2
|
||||||
assert track.title == "The Lovecats xyz"
|
track = tracks[1]
|
||||||
assert track.artist == "The Cure"
|
assert track.title == "The Lovecats xyz"
|
||||||
assert track.track_id == 2
|
assert track.artist == "The Cure"
|
||||||
track_file = os.path.join(
|
assert track.id == 2
|
||||||
self.musicstore, os.path.basename(test_track_path)
|
track_file = os.path.join(
|
||||||
)
|
self.musicstore, os.path.basename(test_track_path)
|
||||||
assert track.path == track_file
|
)
|
||||||
assert os.path.exists(track_file)
|
assert track.path == track_file
|
||||||
assert os.listdir(self.import_source) == []
|
assert os.path.exists(track_file)
|
||||||
|
assert os.listdir(self.import_source) == []
|
||||||
|
|||||||
@ -64,9 +64,9 @@ class TestMMHelpers(unittest.TestCase):
|
|||||||
today_at_11 = dt.datetime.now().replace(hour=11, minute=0)
|
today_at_11 = dt.datetime.now().replace(hour=11, minute=0)
|
||||||
assert get_relative_date(today_at_10, today_at_11) == "Today 10:00"
|
assert get_relative_date(today_at_10, today_at_11) == "Today 10:00"
|
||||||
eight_days_ago = today_at_10 - dt.timedelta(days=8)
|
eight_days_ago = today_at_10 - dt.timedelta(days=8)
|
||||||
assert get_relative_date(eight_days_ago, today_at_11) == "1w, 1d"
|
assert get_relative_date(eight_days_ago, today_at_11) == "1 week, 1 day"
|
||||||
sixteen_days_ago = today_at_10 - dt.timedelta(days=16)
|
sixteen_days_ago = today_at_10 - dt.timedelta(days=16)
|
||||||
assert get_relative_date(sixteen_days_ago, today_at_11) == "2w, 2d"
|
assert get_relative_date(sixteen_days_ago, today_at_11) == "2 weeks, 2 days"
|
||||||
|
|
||||||
def test_leading_silence(self):
|
def test_leading_silence(self):
|
||||||
test_track_path = "testdata/isa.mp3"
|
test_track_path = "testdata/isa.mp3"
|
||||||
|
|||||||
@ -8,15 +8,12 @@ from PyQt6.QtCore import Qt, QModelIndex
|
|||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
from app.helpers import get_all_track_metadata
|
from app.helpers import get_all_track_metadata
|
||||||
from app import ds, playlistmodel
|
from app import playlistmodel
|
||||||
from app.models import (
|
from app.models import (
|
||||||
db,
|
db,
|
||||||
Playlists,
|
Playlists,
|
||||||
Tracks,
|
Tracks,
|
||||||
)
|
)
|
||||||
from classes import (
|
|
||||||
TrackAndPlaylist,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestMMMiscTracks(unittest.TestCase):
|
class TestMMMiscTracks(unittest.TestCase):
|
||||||
@ -36,13 +33,18 @@ class TestMMMiscTracks(unittest.TestCase):
|
|||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
# Create a playlist and model
|
# Create a playlist and model
|
||||||
self.playlist = ds.playlist_create(PLAYLIST_NAME, template_id=0)
|
with db.Session() as session:
|
||||||
self.model = playlistmodel.PlaylistModel(self.playlist.playlist_id, is_template=False)
|
self.playlist = Playlists(session, PLAYLIST_NAME, template_id=0)
|
||||||
|
self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False)
|
||||||
|
|
||||||
for row in range(len(self.test_tracks)):
|
for row in range(len(self.test_tracks)):
|
||||||
track_path = self.test_tracks[row % len(self.test_tracks)]
|
track_path = self.test_tracks[row % len(self.test_tracks)]
|
||||||
track = ds.track_create(**get_all_track_metadata(track_path))
|
track = Tracks(session, **get_all_track_metadata(track_path))
|
||||||
self.model.insert_row(track_id=track.id, note=f"{row=}")
|
self.model.insert_row(
|
||||||
|
proposed_row_number=row, track_id=track.id, note=f"{row=}"
|
||||||
|
)
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
@ -60,11 +62,8 @@ class TestMMMiscTracks(unittest.TestCase):
|
|||||||
START_ROW = 0
|
START_ROW = 0
|
||||||
END_ROW = 2
|
END_ROW = 2
|
||||||
|
|
||||||
# Fake selected row in model
|
self.model.insert_row(proposed_row_number=START_ROW, note="start+")
|
||||||
self.model.selected_rows = [self.model.playlist_rows[START_ROW]]
|
self.model.insert_row(proposed_row_number=END_ROW, note="-")
|
||||||
self.model.insert_row(note="start+")
|
|
||||||
self.model.selected_rows = [self.model.playlist_rows[END_ROW]]
|
|
||||||
self.model.insert_row(note="-")
|
|
||||||
|
|
||||||
prd = self.model.playlist_rows[START_ROW]
|
prd = self.model.playlist_rows[START_ROW]
|
||||||
qv_value = self.model._display_role(
|
qv_value = self.model._display_role(
|
||||||
@ -93,25 +92,28 @@ class TestMMMiscNoPlaylist(unittest.TestCase):
|
|||||||
|
|
||||||
def test_insert_track_new_playlist(self):
|
def test_insert_track_new_playlist(self):
|
||||||
# insert a track into a new playlist
|
# insert a track into a new playlist
|
||||||
playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
|
with db.Session() as session:
|
||||||
# Create a model
|
playlist = Playlists(session, self.PLAYLIST_NAME, template_id=0)
|
||||||
model = playlistmodel.PlaylistModel(playlist.id, is_template=False)
|
# Create a model
|
||||||
# test repr
|
model = playlistmodel.PlaylistModel(playlist.id, is_template=False)
|
||||||
_ = str(model)
|
# test repr
|
||||||
|
_ = str(model)
|
||||||
|
|
||||||
track_path = self.test_tracks[0]
|
track_path = self.test_tracks[0]
|
||||||
metadata = get_all_track_metadata(track_path)
|
metadata = get_all_track_metadata(track_path)
|
||||||
track = ds.track_create(metadata)
|
track = Tracks(session, **metadata)
|
||||||
model.insert_row(track_id=track.id)
|
model.insert_row(proposed_row_number=0, track_id=track.id)
|
||||||
|
|
||||||
prd = model.playlist_rows[model.rowCount() - 1]
|
prd = model.playlist_rows[model.rowCount() - 1]
|
||||||
# test repr
|
# test repr
|
||||||
_ = str(prd)
|
_ = str(prd)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
model._edit_role(model.rowCount() - 1, playlistmodel.Col.TITLE.value, prd)
|
model._edit_role(
|
||||||
== metadata["title"]
|
model.rowCount() - 1, playlistmodel.Col.TITLE.value, prd
|
||||||
)
|
)
|
||||||
|
== metadata["title"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestMMMiscRowMove(unittest.TestCase):
|
class TestMMMiscRowMove(unittest.TestCase):
|
||||||
@ -121,10 +123,13 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
self.playlist = ds.playlist_create(self.PLAYLIST_NAME, template_id=0)
|
with db.Session() as session:
|
||||||
self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False)
|
self.playlist = Playlists(session, self.PLAYLIST_NAME, template_id=0)
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
self.model = playlistmodel.PlaylistModel(self.playlist.id, is_template=False)
|
||||||
self.model.insert_row(note=str(row))
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
|
self.model.insert_row(proposed_row_number=row, note=str(row))
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
db.drop_all()
|
db.drop_all()
|
||||||
@ -135,7 +140,7 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
note_text = "test text"
|
note_text = "test text"
|
||||||
|
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE
|
assert self.model.rowCount() == self.ROWS_TO_CREATE
|
||||||
self.model.insert_row(note=note_text)
|
self.model.insert_row(proposed_row_number=None, note=note_text)
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
prd = self.model.playlist_rows[self.model.rowCount() - 1]
|
prd = self.model.playlist_rows[self.model.rowCount() - 1]
|
||||||
# Test against edit_role because display_role for headers is
|
# Test against edit_role because display_role for headers is
|
||||||
@ -153,10 +158,7 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
note_text = "test text"
|
note_text = "test text"
|
||||||
insert_row = 6
|
insert_row = 6
|
||||||
|
|
||||||
# Fake selected row in model
|
self.model.insert_row(proposed_row_number=insert_row, note=note_text)
|
||||||
self.model.selected_rows = [self.model.playlist_rows[insert_row]]
|
|
||||||
|
|
||||||
self.model.insert_row(note=note_text)
|
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
prd = self.model.playlist_rows[insert_row]
|
prd = self.model.playlist_rows[insert_row]
|
||||||
# Test against edit_role because display_role for headers is
|
# Test against edit_role because display_role for headers is
|
||||||
@ -172,16 +174,11 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
note_text = "test text"
|
note_text = "test text"
|
||||||
insert_row = 6
|
insert_row = 6
|
||||||
|
|
||||||
self.model.insert_row(note=note_text)
|
self.model.insert_row(proposed_row_number=insert_row, note=note_text)
|
||||||
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
assert self.model.rowCount() == self.ROWS_TO_CREATE + 1
|
||||||
|
|
||||||
# Fake selected row in model
|
|
||||||
self.model.selected_rows = [self.model.playlist_rows[insert_row]]
|
|
||||||
|
|
||||||
prd = self.model.playlist_rows[1]
|
prd = self.model.playlist_rows[1]
|
||||||
self.model.add_track_to_header(
|
self.model.add_track_to_header(insert_row, prd.track_id)
|
||||||
TrackAndPlaylist(playlist_id=self.model.playlist_id, track_id=prd.track_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_reverse_row_groups_one_row(self):
|
def test_reverse_row_groups_one_row(self):
|
||||||
rows_to_move = [3]
|
rows_to_move = [3]
|
||||||
@ -201,19 +198,17 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
def test_move_one_row_between_playlists_to_end(self):
|
def test_move_one_row_between_playlists_to_end(self):
|
||||||
from_rows = [3]
|
from_rows = [3]
|
||||||
to_row = self.ROWS_TO_CREATE
|
to_row = self.ROWS_TO_CREATE
|
||||||
destination_playlist_name = "destination"
|
destination_playlist = "destination"
|
||||||
|
|
||||||
model_src = self.model
|
model_src = self.model
|
||||||
playlist_dst = ds.playlist_create(destination_playlist_name, template_id=0)
|
with db.Session() as session:
|
||||||
model_dst = playlistmodel.PlaylistModel(
|
playlist_dst = Playlists(session, destination_playlist, template_id=0)
|
||||||
playlist_dst.playlist_id, is_template=False
|
model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False)
|
||||||
)
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
model_dst.insert_row(proposed_row_number=row, note=str(row))
|
||||||
model_dst.insert_row(note=str(row))
|
|
||||||
|
|
||||||
ds.playlist_move_rows(
|
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
|
||||||
from_rows, self.playlist.playlist_id, to_row, playlist_dst.playlist_id
|
model_dst.refresh_data(session)
|
||||||
)
|
|
||||||
|
|
||||||
assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows)
|
assert len(model_src.playlist_rows) == self.ROWS_TO_CREATE - len(from_rows)
|
||||||
assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows)
|
assert len(model_dst.playlist_rows) == self.ROWS_TO_CREATE + len(from_rows)
|
||||||
@ -224,19 +219,17 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
def test_move_one_row_between_playlists_to_middle(self):
|
def test_move_one_row_between_playlists_to_middle(self):
|
||||||
from_rows = [3]
|
from_rows = [3]
|
||||||
to_row = 2
|
to_row = 2
|
||||||
destination_playlist_name = "destination"
|
destination_playlist = "destination"
|
||||||
|
|
||||||
model_src = self.model
|
model_src = self.model
|
||||||
playlist_dst = ds.playlist_create(destination_playlist_name, template_id=0)
|
with db.Session() as session:
|
||||||
model_dst = playlistmodel.PlaylistModel(
|
playlist_dst = Playlists(session, destination_playlist, template_id=0)
|
||||||
playlist_dst.playlist_id, is_template=False
|
model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False)
|
||||||
)
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
model_dst.insert_row(proposed_row_number=row, note=str(row))
|
||||||
model_dst.insert_row(note=str(row))
|
|
||||||
|
|
||||||
ds.playlist_move_rows(
|
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
|
||||||
from_rows, self.playlist.playlist_id, to_row, playlist_dst.playlist_id
|
model_dst.refresh_data(session)
|
||||||
)
|
|
||||||
|
|
||||||
# Check the rows of the destination model
|
# Check the rows of the destination model
|
||||||
row_notes = []
|
row_notes = []
|
||||||
@ -253,20 +246,17 @@ class TestMMMiscRowMove(unittest.TestCase):
|
|||||||
def test_move_multiple_rows_between_playlists_to_end(self):
|
def test_move_multiple_rows_between_playlists_to_end(self):
|
||||||
from_rows = [1, 3, 4]
|
from_rows = [1, 3, 4]
|
||||||
to_row = 2
|
to_row = 2
|
||||||
destination_playlist_name = "destination"
|
destination_playlist = "destination"
|
||||||
|
|
||||||
model_src = self.model
|
model_src = self.model
|
||||||
|
with db.Session() as session:
|
||||||
|
playlist_dst = Playlists(session, destination_playlist, template_id=0)
|
||||||
|
model_dst = playlistmodel.PlaylistModel(playlist_dst.id, is_template=False)
|
||||||
|
for row in range(self.ROWS_TO_CREATE):
|
||||||
|
model_dst.insert_row(proposed_row_number=row, note=str(row))
|
||||||
|
|
||||||
playlist_dst = ds.playlist_create(destination_playlist_name, template_id=0)
|
model_src.move_rows_between_playlists(from_rows, to_row, model_dst.playlist_id)
|
||||||
model_dst = playlistmodel.PlaylistModel(
|
model_dst.refresh_data(session)
|
||||||
playlist_dst.playlist_id, is_template=False
|
|
||||||
)
|
|
||||||
for row in range(self.ROWS_TO_CREATE):
|
|
||||||
model_dst.insert_row(note=str(row))
|
|
||||||
|
|
||||||
ds.playlist_move_rows(
|
|
||||||
from_rows, self.playlist.id, playlist_dst.playlist_id, to_row
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check the rows of the destination model
|
# Check the rows of the destination model
|
||||||
row_notes = []
|
row_notes = []
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import unittest
|
|||||||
|
|
||||||
# App imports
|
# App imports
|
||||||
from app import playlistmodel
|
from app import playlistmodel
|
||||||
from app import ds
|
from app import repository
|
||||||
from app.models import db
|
from app.models import db
|
||||||
from classes import PlaylistDTO
|
from classes import PlaylistDTO
|
||||||
from helpers import get_all_track_metadata
|
from helpers import get_all_track_metadata
|
||||||
@ -41,7 +41,7 @@ class MyTestCase(unittest.TestCase):
|
|||||||
self, playlist_name: str
|
self, playlist_name: str
|
||||||
) -> (PlaylistDTO, PlaylistModel):
|
) -> (PlaylistDTO, PlaylistModel):
|
||||||
# Create a playlist and model
|
# Create a playlist and model
|
||||||
playlist = ds.create_playlist(name=playlist_name, template_id=0)
|
playlist = repository.create_playlist(name=playlist_name, template_id=0)
|
||||||
assert playlist
|
assert playlist
|
||||||
model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
|
model = playlistmodel.PlaylistModel(playlist.playlist_id, is_template=False)
|
||||||
assert model
|
assert model
|
||||||
@ -52,25 +52,25 @@ class MyTestCase(unittest.TestCase):
|
|||||||
(playlist, model) = self.create_playlist_and_model(playlist_name)
|
(playlist, model) = self.create_playlist_and_model(playlist_name)
|
||||||
# Create tracks
|
# Create tracks
|
||||||
metadata1 = get_all_track_metadata(self.isa_path)
|
metadata1 = get_all_track_metadata(self.isa_path)
|
||||||
self.track1 = ds.create_track(self.isa_path, metadata1)
|
self.track1 = repository.create_track(self.isa_path, metadata1)
|
||||||
|
|
||||||
metadata2 = get_all_track_metadata(self.mom_path)
|
metadata2 = get_all_track_metadata(self.mom_path)
|
||||||
self.track2 = ds.create_track(self.mom_path, metadata2)
|
self.track2 = repository.create_track(self.mom_path, metadata2)
|
||||||
|
|
||||||
# Add tracks and header to playlist
|
# Add tracks and header to playlist
|
||||||
self.row0 = ds.insert_row(
|
self.row0 = repository.insert_row(
|
||||||
playlist.playlist_id,
|
playlist.playlist_id,
|
||||||
row_number=0,
|
row_number=0,
|
||||||
track_id=self.track1.track_id,
|
track_id=self.track1.track_id,
|
||||||
note="track 1",
|
note="track 1",
|
||||||
)
|
)
|
||||||
self.row1 = ds.insert_row(
|
self.row1 = repository.insert_row(
|
||||||
playlist.playlist_id,
|
playlist.playlist_id,
|
||||||
row_number=1,
|
row_number=1,
|
||||||
track_id=0,
|
track_id=0,
|
||||||
note="Header row",
|
note="Header row",
|
||||||
)
|
)
|
||||||
self.row2 = ds.insert_row(
|
self.row2 = repository.insert_row(
|
||||||
playlist.playlist_id,
|
playlist.playlist_id,
|
||||||
row_number=2,
|
row_number=2,
|
||||||
track_id=self.track2.track_id,
|
track_id=self.track2.track_id,
|
||||||
@ -82,7 +82,7 @@ class MyTestCase(unittest.TestCase):
|
|||||||
) -> (PlaylistDTO, PlaylistModel):
|
) -> (PlaylistDTO, PlaylistModel):
|
||||||
(playlist, model) = self.create_playlist_and_model(playlist_name)
|
(playlist, model) = self.create_playlist_and_model(playlist_name)
|
||||||
for row_number in range(number_of_rows):
|
for row_number in range(number_of_rows):
|
||||||
ds.insert_row(
|
repository.insert_row(
|
||||||
playlist.playlist_id, row_number, None, str(row_number)
|
playlist.playlist_id, row_number, None, str(row_number)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -97,60 +97,60 @@ class MyTestCase(unittest.TestCase):
|
|||||||
"""Add a track to a header row"""
|
"""Add a track to a header row"""
|
||||||
|
|
||||||
self.create_playlist_model_tracks("my playlist")
|
self.create_playlist_model_tracks("my playlist")
|
||||||
ds.add_track_to_header(self.row1.playlistrow_id, self.track2.track_id)
|
repository.add_track_to_header(self.row1.playlistrow_id, self.track2.track_id)
|
||||||
result = ds.get_playlist_row(self.row1.playlistrow_id)
|
result = repository.get_playlist_row(self.row1.playlistrow_id)
|
||||||
assert result.track.track_id == self.track2.track_id
|
assert result.track.track_id == self.track2.track_id
|
||||||
|
|
||||||
def test_create_track(self):
|
def test_create_track(self):
|
||||||
metadata = get_all_track_metadata(self.isa_path)
|
metadata = get_all_track_metadata(self.isa_path)
|
||||||
ds.create_track(self.isa_path, metadata)
|
repository.create_track(self.isa_path, metadata)
|
||||||
results = ds.get_all_tracks()
|
results = repository.get_all_tracks()
|
||||||
assert len(results) == 1
|
assert len(results) == 1
|
||||||
assert results[0].path == self.isa_path
|
assert results[0].path == self.isa_path
|
||||||
|
|
||||||
def test_get_track_by_id(self):
|
def test_get_track_by_id(self):
|
||||||
metadata = get_all_track_metadata(self.isa_path)
|
metadata = get_all_track_metadata(self.isa_path)
|
||||||
dto = ds.create_track(self.isa_path, metadata)
|
dto = repository.create_track(self.isa_path, metadata)
|
||||||
result = ds.track_by_id(dto.track_id)
|
result = repository.track_by_id(dto.track_id)
|
||||||
assert result.path == self.isa_path
|
assert result.path == self.isa_path
|
||||||
|
|
||||||
def test_get_track_by_artist(self):
|
def test_get_track_by_artist(self):
|
||||||
metadata = get_all_track_metadata(self.isa_path)
|
metadata = get_all_track_metadata(self.isa_path)
|
||||||
_ = ds.create_track(self.isa_path, metadata)
|
_ = repository.create_track(self.isa_path, metadata)
|
||||||
metadata = get_all_track_metadata(self.mom_path)
|
metadata = get_all_track_metadata(self.mom_path)
|
||||||
_ = ds.create_track(self.mom_path, metadata)
|
_ = repository.create_track(self.mom_path, metadata)
|
||||||
result_isa = ds.tracks_by_artist(self.isa_artist)
|
result_isa = repository.tracks_by_artist(self.isa_artist)
|
||||||
assert len(result_isa) == 1
|
assert len(result_isa) == 1
|
||||||
assert result_isa[0].artist == self.isa_artist
|
assert result_isa[0].artist == self.isa_artist
|
||||||
result_mom = ds.tracks_by_artist(self.mom_artist)
|
result_mom = repository.tracks_by_artist(self.mom_artist)
|
||||||
assert len(result_mom) == 1
|
assert len(result_mom) == 1
|
||||||
assert result_mom[0].artist == self.mom_artist
|
assert result_mom[0].artist == self.mom_artist
|
||||||
|
|
||||||
def test_get_track_by_title(self):
|
def test_get_track_by_title(self):
|
||||||
metadata_isa = get_all_track_metadata(self.isa_path)
|
metadata_isa = get_all_track_metadata(self.isa_path)
|
||||||
_ = ds.create_track(self.isa_path, metadata_isa)
|
_ = repository.create_track(self.isa_path, metadata_isa)
|
||||||
metadata_mom = get_all_track_metadata(self.mom_path)
|
metadata_mom = get_all_track_metadata(self.mom_path)
|
||||||
_ = ds.create_track(self.mom_path, metadata_mom)
|
_ = repository.create_track(self.mom_path, metadata_mom)
|
||||||
result_isa = ds.tracks_by_title(self.isa_title)
|
result_isa = repository.tracks_by_title(self.isa_title)
|
||||||
assert len(result_isa) == 1
|
assert len(result_isa) == 1
|
||||||
assert result_isa[0].title == self.isa_title
|
assert result_isa[0].title == self.isa_title
|
||||||
result_mom = ds.tracks_by_title(self.mom_title)
|
result_mom = repository.tracks_by_title(self.mom_title)
|
||||||
assert len(result_mom) == 1
|
assert len(result_mom) == 1
|
||||||
assert result_mom[0].title == self.mom_title
|
assert result_mom[0].title == self.mom_title
|
||||||
|
|
||||||
def test_tracks_get_all_tracks(self):
|
def test_tracks_get_all_tracks(self):
|
||||||
self.create_playlist_model_tracks(playlist_name="test_track_get_all_tracks")
|
self.create_playlist_model_tracks(playlist_name="test_track_get_all_tracks")
|
||||||
all_tracks = ds.get_all_tracks()
|
all_tracks = repository.get_all_tracks()
|
||||||
assert len(all_tracks) == 2
|
assert len(all_tracks) == 2
|
||||||
|
|
||||||
def test_tracks_by_path(self):
|
def test_tracks_by_path(self):
|
||||||
metadata_isa = get_all_track_metadata(self.isa_path)
|
metadata_isa = get_all_track_metadata(self.isa_path)
|
||||||
_ = ds.create_track(self.isa_path, metadata_isa)
|
_ = repository.create_track(self.isa_path, metadata_isa)
|
||||||
metadata_mom = get_all_track_metadata(self.mom_path)
|
metadata_mom = get_all_track_metadata(self.mom_path)
|
||||||
_ = ds.create_track(self.mom_path, metadata_mom)
|
_ = repository.create_track(self.mom_path, metadata_mom)
|
||||||
result_isa = ds.track_by_path(self.isa_path)
|
result_isa = repository.track_by_path(self.isa_path)
|
||||||
assert result_isa.title == self.isa_title
|
assert result_isa.title == self.isa_title
|
||||||
result_mom = ds.track_by_path(self.mom_path)
|
result_mom = repository.track_by_path(self.mom_path)
|
||||||
assert result_mom.title == self.mom_title
|
assert result_mom.title == self.mom_title
|
||||||
|
|
||||||
def test_move_rows_test1(self):
|
def test_move_rows_test1(self):
|
||||||
@ -159,11 +159,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 10
|
number_of_rows = 10
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test1", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test1", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([3], playlist.playlist_id, 5)
|
repository.move_rows([3], playlist.playlist_id, 5)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 4, 5, 3, 6, 7, 8, 9]
|
assert new_order == [0, 1, 2, 4, 5, 3, 6, 7, 8, 9]
|
||||||
|
|
||||||
@ -173,11 +173,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 10
|
number_of_rows = 10
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test2", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test2", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([4], playlist.playlist_id, 3)
|
repository.move_rows([4], playlist.playlist_id, 3)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 4, 3, 5, 6, 7, 8, 9]
|
assert new_order == [0, 1, 2, 4, 3, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
@ -187,11 +187,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 10
|
number_of_rows = 10
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test3", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test3", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([4], playlist.playlist_id, 2)
|
repository.move_rows([4], playlist.playlist_id, 2)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 4, 2, 3, 5, 6, 7, 8, 9]
|
assert new_order == [0, 1, 4, 2, 3, 5, 6, 7, 8, 9]
|
||||||
|
|
||||||
@ -201,11 +201,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 11
|
number_of_rows = 11
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test4", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test4", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([1, 4, 5, 10], playlist.playlist_id, 8)
|
repository.move_rows([1, 4, 5, 10], playlist.playlist_id, 8)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 2, 3, 6, 7, 8, 1, 4, 5, 10, 9]
|
assert new_order == [0, 2, 3, 6, 7, 8, 1, 4, 5, 10, 9]
|
||||||
|
|
||||||
@ -215,11 +215,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 11
|
number_of_rows = 11
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test5", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test5", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([3, 6], playlist.playlist_id, 5)
|
repository.move_rows([3, 6], playlist.playlist_id, 5)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 4, 5, 3, 6, 7, 8, 9, 10]
|
assert new_order == [0, 1, 2, 4, 5, 3, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
@ -229,11 +229,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 11
|
number_of_rows = 11
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test6", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test6", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([3, 5, 6], playlist.playlist_id, 8)
|
repository.move_rows([3, 5, 6], playlist.playlist_id, 8)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 4, 7, 8, 9, 10, 3, 5, 6]
|
assert new_order == [0, 1, 2, 4, 7, 8, 9, 10, 3, 5, 6]
|
||||||
|
|
||||||
@ -243,11 +243,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 11
|
number_of_rows = 11
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test7", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test7", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([7, 8, 10], playlist.playlist_id, 5)
|
repository.move_rows([7, 8, 10], playlist.playlist_id, 5)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 3, 4, 7, 8, 10, 5, 6, 9]
|
assert new_order == [0, 1, 2, 3, 4, 7, 8, 10, 5, 6, 9]
|
||||||
|
|
||||||
@ -258,11 +258,11 @@ class MyTestCase(unittest.TestCase):
|
|||||||
number_of_rows = 11
|
number_of_rows = 11
|
||||||
(playlist, model) = self.create_rows("test_move_rows_test8", number_of_rows)
|
(playlist, model) = self.create_rows("test_move_rows_test8", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows([0, 1, 2, 3], playlist.playlist_id, 0)
|
repository.move_rows([0, 1, 2, 3], playlist.playlist_id, 0)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order = []
|
new_order = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist.playlist_id):
|
for row in repository.get_playlist_rows(playlist.playlist_id):
|
||||||
new_order.append(int(row.note))
|
new_order.append(int(row.note))
|
||||||
assert new_order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
assert new_order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
@ -274,17 +274,17 @@ class MyTestCase(unittest.TestCase):
|
|||||||
(playlist_src, model_src) = self.create_rows("src playlist", number_of_rows)
|
(playlist_src, model_src) = self.create_rows("src playlist", number_of_rows)
|
||||||
(playlist_dst, model_dst) = self.create_rows("dst playlist", number_of_rows)
|
(playlist_dst, model_dst) = self.create_rows("dst playlist", number_of_rows)
|
||||||
|
|
||||||
ds.move_rows(
|
repository.move_rows(
|
||||||
rows_to_move, playlist_src.playlist_id, to_row, playlist_dst.playlist_id
|
rows_to_move, playlist_src.playlist_id, to_row, playlist_dst.playlist_id
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check we have all rows and plr_rownums are correct
|
# Check we have all rows and plr_rownums are correct
|
||||||
new_order_src = []
|
new_order_src = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist_src.playlist_id):
|
for row in repository.get_playlist_rows(playlist_src.playlist_id):
|
||||||
new_order_src.append(int(row.note))
|
new_order_src.append(int(row.note))
|
||||||
assert new_order_src == [0, 1, 3, 5, 7, 8, 9, 10]
|
assert new_order_src == [0, 1, 3, 5, 7, 8, 9, 10]
|
||||||
new_order_dst = []
|
new_order_dst = []
|
||||||
for row in ds.playlistrows_by_playlist(playlist_dst.playlist_id):
|
for row in repository.get_playlist_rows(playlist_dst.playlist_id):
|
||||||
new_order_dst.append(int(row.note))
|
new_order_dst.append(int(row.note))
|
||||||
assert new_order_dst == [0, 1, 2, 3, 4, 2, 4, 6, 5, 6, 7, 8, 9, 10]
|
assert new_order_dst == [0, 1, 2, 3, 4, 2, 4, 6, 5, 6, 7, 8, 9, 10]
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user