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.
This commit is contained in:
2026-07-02 16:54:54 -04:00
parent a1eb8aec8b
commit 2b02f109f6
4 changed files with 87 additions and 9 deletions
+6 -4
View File
@@ -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 ----
+3 -2
View File
@@ -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():
+6 -3
View File
@@ -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
+72
View File
@@ -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)]