diff --git a/lintunes/gui/main_window.py b/lintunes/gui/main_window.py index 2953750..6662bd6 100644 --- a/lintunes/gui/main_window.py +++ b/lintunes/gui/main_window.py @@ -314,16 +314,18 @@ class MainWindow(QMainWindow): if paths: self.import_files([Path(p) for p in paths]) - def import_files(self, paths, playlist_pid: str = ""): + def import_files(self, paths, playlist_pid: str = "", + position: int | None = None): self.statusBar().showMessage(f"Importing {len(paths)} item(s)…") imported = file_importer.import_paths( - paths, self._music_import_dir(), self._manager, playlist_pid) + paths, self._music_import_dir(), self._manager, playlist_pid, + position) self.statusBar().showMessage(f"Imported {len(imported)} track(s)", 5000) if imported: self._library_view.reload() - def _import_files_to_playlist(self, paths, pid): - self.import_files([Path(p) for p in paths], pid) + def _import_files_to_playlist(self, paths, pid, position=None): + self.import_files([Path(p) for p in paths], pid, position) # ---- album art download ---- diff --git a/lintunes/gui/playlist_view.py b/lintunes/gui/playlist_view.py index 3879114..4f3c764 100644 --- a/lintunes/gui/playlist_view.py +++ b/lintunes/gui/playlist_view.py @@ -9,7 +9,7 @@ from lintunes.gui.playlist_ops import add_tracks_with_dup_check class PlaylistView(TableSettingsMixin, QWidget): play_requested = pyqtSignal(list, int) info_requested = pyqtSignal(list) - files_dropped = pyqtSignal(list, str) # paths, playlist pid + files_dropped = pyqtSignal(list, str, object) # paths, pid, row or None show_in_playlist_requested = pyqtSignal(int, str) # track_id, playlist pid def __init__(self, manager, parent=None): @@ -124,7 +124,8 @@ class PlaylistView(TableSettingsMixin, QWidget): def _on_files_dropped(self, paths, drop_row): if self._editable(): - self.files_dropped.emit(paths, self._playlist.persistent_id) + self.files_dropped.emit( + paths, self._playlist.persistent_id, drop_row) def _on_remove(self, rows): if self._editable(): diff --git a/lintunes/importers/file_importer.py b/lintunes/importers/file_importer.py index ed6c44c..c3cb928 100644 --- a/lintunes/importers/file_importer.py +++ b/lintunes/importers/file_importer.py @@ -97,8 +97,10 @@ def import_file(source: Path, music_dir: Path, manager, def import_paths(paths: list[Path], music_dir: Path, manager, - playlist_pid: str = "") -> list[Track]: - """Import files and directories (recursively); optionally add to a playlist.""" + playlist_pid: str = "", + position: int | None = None) -> list[Track]: + """Import files and directories (recursively); optionally add to a + playlist, inserting at `position` (None = append).""" files: list[Path] = [] for path in paths: path = Path(path) @@ -119,5 +121,6 @@ def import_paths(paths: list[Path], music_dir: Path, manager, imported.append(track) if playlist_pid and imported: - manager.add_tracks_to_playlist(playlist_pid, [t.track_id for t in imported]) + manager.add_tracks_to_playlist( + playlist_pid, [t.track_id for t in imported], position) return imported diff --git a/tests/test_round18.py b/tests/test_round18.py index 0a84c95..e388340 100644 --- a/tests/test_round18.py +++ b/tests/test_round18.py @@ -99,3 +99,75 @@ class TestVisualizerGrayColor: def test_visualizer_row_in_preferences_dialog(self, qapp): from lintunes.gui.preferences_dialog import _COLOR_ROWS assert "color_visualizer_gray" in [key for key, _label in _COLOR_ROWS] + + +# -------------------------------------------------------------------------- +# 2. External file drops insert at the drop position +# -------------------------------------------------------------------------- + +def _seeded_manager(tmp_path, track_ids=(1, 2, 3)): + from lintunes.models import Library, Track + from lintunes.library_manager import LibraryManager + tracks = {tid: Track(track_id=tid, name=f"T{tid}") for tid in track_ids} + return LibraryManager(Library(tracks=tracks), tmp_path / "data") + + +class TestFileDropPosition: + def test_import_paths_inserts_at_position(self, qapp, tmp_path, mp3_file): + from lintunes.importers import file_importer + manager = _seeded_manager(tmp_path) + playlist = manager.create_playlist("Drops") + manager.add_tracks_to_playlist(playlist.persistent_id, [1, 2, 3]) + + imported = file_importer.import_paths( + [mp3_file], tmp_path / "music", manager, + playlist.persistent_id, position=1) + + assert len(imported) == 1 + new_id = imported[0].track_id + assert playlist.track_ids == [1, new_id, 2, 3] + + def test_import_paths_default_appends(self, qapp, tmp_path, mp3_file): + from lintunes.importers import file_importer + manager = _seeded_manager(tmp_path) + playlist = manager.create_playlist("Drops") + manager.add_tracks_to_playlist(playlist.persistent_id, [1, 2]) + + imported = file_importer.import_paths( + [mp3_file], tmp_path / "music", manager, playlist.persistent_id) + + assert playlist.track_ids == [1, 2, imported[0].track_id] + + def test_duplicate_file_inserts_without_new_track(self, qapp, tmp_path, + mp3_file): + from lintunes.importers import file_importer + manager = _seeded_manager(tmp_path) + existing = file_importer.import_file( + mp3_file, tmp_path / "music", manager) + playlist = manager.create_playlist("Drops") + manager.add_tracks_to_playlist(playlist.persistent_id, [1, 2]) + count_before = len(manager.library.tracks) + + imported = file_importer.import_paths( + [mp3_file], tmp_path / "music", manager, + playlist.persistent_id, position=0) + + assert imported[0].track_id == existing.track_id + assert playlist.track_ids == [existing.track_id, 1, 2] + assert len(manager.library.tracks) == count_before + + def test_playlist_view_relays_drop_row(self, qapp, tmp_path): + from lintunes.gui.playlist_view import PlaylistView + manager = _seeded_manager(tmp_path) + playlist = manager.create_playlist("Drops") + view = PlaylistView(manager) + view.show_playlist(playlist) + received = [] + view.files_dropped.connect( + lambda paths, pid, row: received.append((paths, pid, row))) + + view._on_files_dropped(["/x.mp3"], 2) + view._on_files_dropped(["/y.mp3"], None) + + assert received == [(["/x.mp3"], playlist.persistent_id, 2), + (["/y.mp3"], playlist.persistent_id, None)]