"""Round 22: start-time fixes in the player. Editing a track's start time saved fine but replaying the *already loaded* track ignored it: QMediaPlayer.setSource() no-ops on an unchanged URL, so the LoadedMedia status change that consumes the armed start time never re-fired (and forcing a clear+reload races the FFmpeg backend, which snaps the seek back to 0). _load_current now detects the unchanged source and seeks directly — the media is already loaded. Also, "previous"-restart now rewinds to the custom start time instead of 0:00. """ from unittest.mock import MagicMock, patch from PyQt6.QtCore import QUrl from lintunes.models import Library, Track from lintunes import player as player_module from lintunes.player import Player, RESTART_THRESHOLD_MS def _track(tid, **kw): return Track(track_id=tid, name=kw.pop("name", f"Track {tid}"), **kw) def _player(qapp, tracks): library = Library() for t in tracks: library.tracks[t.track_id] = t manager = MagicMock() manager.library = library with patch.multiple( player_module, QMediaPlayer=MagicMock(), QAudioOutput=MagicMock(), QAudioBufferOutput=MagicMock(), QMediaDevices=MagicMock(), ): player = Player(manager) return player, manager class TestSameSourceReplay: def test_unchanged_url_seeks_to_freshly_edited_start(self, qapp, tmp_path): (tmp_path / "a.mp3").write_bytes(b"x") loc = str(tmp_path / "a.mp3") track = _track(1, location=loc, total_time=200_000) player, _ = _player(qapp, [track]) url = QUrl.fromLocalFile(loc) player.play_queue([1], 0) # first load: source was empty, plain set assert player._media.setSource.call_args_list[-1].args == (url,) # The track is now the loaded source; the user edits its start time # and plays it again. The media is already loaded, so the player must # seek directly instead of waiting for a LoadedMedia that never comes. player._media.source.return_value = url track.start_time = 30_000 player._media.setSource.reset_mock() player.play_queue([1], 0) player._media.setSource.assert_not_called() player._media.stop.assert_called() player._media.setPosition.assert_called_with(30_000) assert player._pending_start_ms == 0 # consumed player._media.play.assert_called() def test_unchanged_url_without_start_time_restarts_at_zero( self, qapp, tmp_path): (tmp_path / "a.mp3").write_bytes(b"x") loc = str(tmp_path / "a.mp3") track = _track(1, location=loc, total_time=200_000) player, _ = _player(qapp, [track]) player.play_queue([1], 0) player._media.source.return_value = QUrl.fromLocalFile(loc) player._media.setPosition.reset_mock() player.play_queue([1], 0) # stop() rewinds to 0; no seek should be issued. player._media.stop.assert_called() player._media.setPosition.assert_not_called() def test_different_url_loads_via_setsource(self, qapp, tmp_path): for name in ("a.mp3", "b.mp3"): (tmp_path / name).write_bytes(b"x") t1 = _track(1, location=str(tmp_path / "a.mp3"), total_time=200_000) t2 = _track(2, location=str(tmp_path / "b.mp3"), total_time=200_000, start_time=30_000) player, _ = _player(qapp, [t1, t2]) player._media.source.return_value = QUrl.fromLocalFile(t1.location) player.play_queue([1, 2], 1) # loading b.mp3 while a.mp3 is the source assert [c.args for c in player._media.setSource.call_args_list] \ == [(QUrl.fromLocalFile(t2.location),)] # New source: the seek waits for LoadedMedia as before. assert player._pending_start_ms == 30_000 player._on_media_status(player_module.QMediaPlayer.MediaStatus.LoadedMedia) player._media.setPosition.assert_called_with(30_000) assert player._pending_start_ms == 0 class TestPreviousRestart: def test_restart_seeks_to_custom_start_time(self, qapp, tmp_path): (tmp_path / "a.mp3").write_bytes(b"x") track = _track(1, location=str(tmp_path / "a.mp3"), total_time=200_000, start_time=30_000) player, _ = _player(qapp, [track]) player.play_queue([1], 0) player._media.position.return_value = RESTART_THRESHOLD_MS + 1 player.previous() player._media.setPosition.assert_called_with(30_000) def test_restart_without_start_time_seeks_to_zero(self, qapp, tmp_path): (tmp_path / "a.mp3").write_bytes(b"x") track = _track(1, location=str(tmp_path / "a.mp3"), total_time=200_000) player, _ = _player(qapp, [track]) player.play_queue([1], 0) player._media.position.return_value = RESTART_THRESHOLD_MS + 1 player.previous() player._media.setPosition.assert_called_with(0)