File drops from the file manager insert at the drop position
The table already computed and emitted the drop row for external URL drops, but PlaylistView._on_files_dropped threw it away, so imported files always appended. Thread it through: playlist_view.files_dropped gains a row slot (None = append), MainWindow.import_files and file_importer.import_paths take an optional position and hand it to add_tracks_to_playlist, which already supported positional insert. Files already in the library dedup to the existing track and insert at the drop position without duplicating the library entry. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@ -314,16 +314,18 @@ class MainWindow(QMainWindow):
|
|||||||
if paths:
|
if paths:
|
||||||
self.import_files([Path(p) for p in 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)…")
|
self.statusBar().showMessage(f"Importing {len(paths)} item(s)…")
|
||||||
imported = file_importer.import_paths(
|
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)
|
self.statusBar().showMessage(f"Imported {len(imported)} track(s)", 5000)
|
||||||
if imported:
|
if imported:
|
||||||
self._library_view.reload()
|
self._library_view.reload()
|
||||||
|
|
||||||
def _import_files_to_playlist(self, paths, pid):
|
def _import_files_to_playlist(self, paths, pid, position=None):
|
||||||
self.import_files([Path(p) for p in paths], pid)
|
self.import_files([Path(p) for p in paths], pid, position)
|
||||||
|
|
||||||
# ---- album art download ----
|
# ---- album art download ----
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from lintunes.gui.playlist_ops import add_tracks_with_dup_check
|
|||||||
class PlaylistView(TableSettingsMixin, QWidget):
|
class PlaylistView(TableSettingsMixin, QWidget):
|
||||||
play_requested = pyqtSignal(list, int)
|
play_requested = pyqtSignal(list, int)
|
||||||
info_requested = pyqtSignal(list)
|
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
|
show_in_playlist_requested = pyqtSignal(int, str) # track_id, playlist pid
|
||||||
|
|
||||||
def __init__(self, manager, parent=None):
|
def __init__(self, manager, parent=None):
|
||||||
@ -124,7 +124,8 @@ class PlaylistView(TableSettingsMixin, QWidget):
|
|||||||
|
|
||||||
def _on_files_dropped(self, paths, drop_row):
|
def _on_files_dropped(self, paths, drop_row):
|
||||||
if self._editable():
|
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):
|
def _on_remove(self, rows):
|
||||||
if self._editable():
|
if self._editable():
|
||||||
|
|||||||
@ -97,8 +97,10 @@ def import_file(source: Path, music_dir: Path, manager,
|
|||||||
|
|
||||||
|
|
||||||
def import_paths(paths: list[Path], music_dir: Path, manager,
|
def import_paths(paths: list[Path], music_dir: Path, manager,
|
||||||
playlist_pid: str = "") -> list[Track]:
|
playlist_pid: str = "",
|
||||||
"""Import files and directories (recursively); optionally add to a playlist."""
|
position: int | None = None) -> list[Track]:
|
||||||
|
"""Import files and directories (recursively); optionally add to a
|
||||||
|
playlist, inserting at `position` (None = append)."""
|
||||||
files: list[Path] = []
|
files: list[Path] = []
|
||||||
for path in paths:
|
for path in paths:
|
||||||
path = Path(path)
|
path = Path(path)
|
||||||
@ -119,5 +121,6 @@ def import_paths(paths: list[Path], music_dir: Path, manager,
|
|||||||
imported.append(track)
|
imported.append(track)
|
||||||
|
|
||||||
if playlist_pid and imported:
|
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
|
return imported
|
||||||
|
|||||||
@ -99,3 +99,75 @@ class TestVisualizerGrayColor:
|
|||||||
def test_visualizer_row_in_preferences_dialog(self, qapp):
|
def test_visualizer_row_in_preferences_dialog(self, qapp):
|
||||||
from lintunes.gui.preferences_dialog import _COLOR_ROWS
|
from lintunes.gui.preferences_dialog import _COLOR_ROWS
|
||||||
assert "color_visualizer_gray" in [key for key, _label in _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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user