feat: remember last played track, playback position, and volume across restarts
This commit is contained in:
parent
949783e76c
commit
06f6529cbe
@ -94,39 +94,53 @@ class ConfigLoader:
|
||||
if not self.config.has_section('General'):
|
||||
self.config.add_section('General')
|
||||
|
||||
if not self.config.has_option('General', 'output_dir'):
|
||||
self.config.set('General', 'output_dir', os.path.join(os.path.expanduser("~"), "Music", "iPod"))
|
||||
if not self.config.has_option('General', 'format'):
|
||||
self.config.set('General', 'format', 'm4a')
|
||||
if not self.config.has_option('General', 'quality'):
|
||||
self.config.set('General', 'quality', '256')
|
||||
|
||||
# Video section
|
||||
if not self.config.has_section('Video'):
|
||||
self.config.add_section('Video')
|
||||
|
||||
if not self.config.has_option('Video', 'enable_video'):
|
||||
self.config.set('Video', 'enable_video', 'false')
|
||||
if not self.config.has_option('Video', 'resolution'):
|
||||
self.config.set('Video', 'resolution', '640x480')
|
||||
|
||||
# Metadata section
|
||||
if not self.config.has_section('Metadata'):
|
||||
self.config.add_section('Metadata')
|
||||
|
||||
if not self.config.has_option('Metadata', 'embed_artwork'):
|
||||
self.config.set('Metadata', 'embed_artwork', 'true')
|
||||
if not self.config.has_option('Metadata', 'use_musicbrainz'):
|
||||
self.config.set('Metadata', 'use_musicbrainz', 'false')
|
||||
|
||||
# Device section
|
||||
if not self.config.has_section('Device'):
|
||||
self.config.add_section('Device')
|
||||
|
||||
if not self.config.has_option('Device', 'auto_detect'):
|
||||
self.config.set('Device', 'auto_detect', 'true')
|
||||
if not self.config.has_option('Device', 'mount_point'):
|
||||
self.config.set('Device', 'mount_point', '')
|
||||
|
||||
# Advanced section
|
||||
if not self.config.has_section('Advanced'):
|
||||
self.config.add_section('Advanced')
|
||||
|
||||
if not self.config.has_option('Advanced', 'temp_dir'):
|
||||
self.config.set('Advanced', 'temp_dir', 'temp')
|
||||
if not self.config.has_option('Advanced', 'concurrent_downloads'):
|
||||
self.config.set('Advanced', 'concurrent_downloads', '2')
|
||||
if not self.config.has_option('Advanced', 'debug'):
|
||||
self.config.set('Advanced', 'debug', 'false')
|
||||
if not self.config.has_option('Advanced', 'clean_temp'):
|
||||
self.config.set('Advanced', 'clean_temp', 'true')
|
||||
if not self.config.has_option('Advanced', 'show_download'):
|
||||
self.config.set('Advanced', 'show_download', 'false')
|
||||
|
||||
# Hotkeys section
|
||||
@ -153,8 +167,19 @@ class ConfigLoader:
|
||||
'delete_selected': 'Delete',
|
||||
}
|
||||
for key, value in hotkey_defaults.items():
|
||||
if not self.config.has_option('Hotkeys', key):
|
||||
self.config.set('Hotkeys', key, value)
|
||||
|
||||
# Playback section
|
||||
if not self.config.has_section('Playback'):
|
||||
self.config.add_section('Playback')
|
||||
if not self.config.has_option('Playback', 'last_track_path'):
|
||||
self.config.set('Playback', 'last_track_path', '')
|
||||
if not self.config.has_option('Playback', 'last_position_ms'):
|
||||
self.config.set('Playback', 'last_position_ms', '0')
|
||||
if not self.config.has_option('Playback', 'last_volume'):
|
||||
self.config.set('Playback', 'last_volume', '80')
|
||||
|
||||
def get(self, section: str, option: str, fallback: Any = None) -> Any:
|
||||
"""
|
||||
Get configuration value
|
||||
|
||||
55
src/main.py
55
src/main.py
@ -294,12 +294,17 @@ class MainWindow(QMainWindow):
|
||||
self.audio_output: Optional[QAudioOutput] = None
|
||||
self.current_playback_index: int = -1
|
||||
self._is_playing = False
|
||||
self._saved_volume: int = 80
|
||||
self._saved_track_path: str = ""
|
||||
self._saved_position_ms: int = 0
|
||||
self._pending_seek_ms: int = 0
|
||||
|
||||
self._setup_ui()
|
||||
self._setup_player()
|
||||
self._load_settings()
|
||||
self._setup_player()
|
||||
self._setup_hotkeys()
|
||||
self._scan_library()
|
||||
self._resume_playback_if_saved()
|
||||
self._on_refresh_devices_clicked()
|
||||
|
||||
def _cleanup_worker(self):
|
||||
@ -345,6 +350,10 @@ class MainWindow(QMainWindow):
|
||||
self.show_download_tab = show_download
|
||||
self.tab_widget.setTabVisible(self.download_tab_index, show_download)
|
||||
|
||||
self._saved_volume = config.get_int("Playback", "last_volume", fallback=80)
|
||||
self._saved_track_path = config.get("Playback", "last_track_path", fallback="")
|
||||
self._saved_position_ms = config.get_int("Playback", "last_position_ms", fallback=0)
|
||||
|
||||
def _save_settings(self):
|
||||
config = self.config_loader
|
||||
config.set("General", "output_dir", self.output_dir_input.text())
|
||||
@ -357,6 +366,23 @@ class MainWindow(QMainWindow):
|
||||
config.set("Advanced", "clean_temp", str(self.clean_temp_check.isChecked()).lower())
|
||||
config.set("Device", "auto_detect", str(self.auto_detect_check.isChecked()).lower())
|
||||
config.set("Advanced", "show_download", str(self.show_download_check.isChecked()).lower())
|
||||
|
||||
config.set("Playback", "last_volume", str(self.volume_slider.value()))
|
||||
if self.current_playback_index >= 0 and self.player.source().isValid():
|
||||
track_path = self.player.source().toLocalFile()
|
||||
if track_path and os.path.exists(track_path):
|
||||
config.set("Playback", "last_track_path", track_path)
|
||||
position = self.player.position()
|
||||
duration = self.player.duration()
|
||||
if duration > 0 and position > duration - 2000:
|
||||
position = 0
|
||||
config.set("Playback", "last_position_ms", str(position))
|
||||
else:
|
||||
config.set("Playback", "last_track_path", "")
|
||||
config.set("Playback", "last_position_ms", "0")
|
||||
else:
|
||||
config.set("Playback", "last_track_path", "")
|
||||
config.set("Playback", "last_position_ms", "0")
|
||||
config.save()
|
||||
|
||||
def _setup_hotkeys(self):
|
||||
@ -799,14 +825,14 @@ class MainWindow(QMainWindow):
|
||||
"""Initialize QMediaPlayer and QAudioOutput for playback."""
|
||||
self.player = QMediaPlayer()
|
||||
self.audio_output = QAudioOutput()
|
||||
self.audio_output.setVolume(0.8)
|
||||
self.audio_output.setVolume(self._saved_volume / 100.0)
|
||||
self.player.setAudioOutput(self.audio_output)
|
||||
|
||||
self.player.positionChanged.connect(self._on_media_position_changed)
|
||||
self.player.durationChanged.connect(self._on_media_duration_changed)
|
||||
self.player.mediaStatusChanged.connect(self._on_media_status_changed)
|
||||
|
||||
def _play_track_from_index(self, index: int):
|
||||
def _play_track_from_index(self, index: int, start_position_ms: int = 0):
|
||||
"""Start playing a track from the library table by row index."""
|
||||
if index < 0 or index >= self.library_table.rowCount():
|
||||
return
|
||||
@ -834,7 +860,6 @@ class MainWindow(QMainWindow):
|
||||
self.cover_label.setPixmap(pixmap)
|
||||
self.cover_label.setToolTip(track_data.get("album", ""))
|
||||
else:
|
||||
# Fallback: try looking up by artist/album in cover cache
|
||||
artist = track_data.get("artist", "")
|
||||
album = track_data.get("album", "")
|
||||
cached = cover_cache.get(artist, album) if artist else None
|
||||
@ -846,10 +871,14 @@ class MainWindow(QMainWindow):
|
||||
self.cover_label.clear()
|
||||
self.cover_label.setToolTip("")
|
||||
|
||||
# Highlight the current row
|
||||
self.library_table.clearSelection()
|
||||
self.library_table.selectRow(index)
|
||||
|
||||
if start_position_ms > 0:
|
||||
self._pending_seek_ms = start_position_ms
|
||||
else:
|
||||
self._pending_seek_ms = 0
|
||||
|
||||
def _on_play_pause_clicked(self):
|
||||
if self.player is None:
|
||||
return
|
||||
@ -946,6 +975,10 @@ class MainWindow(QMainWindow):
|
||||
elif status == QMediaPlayer.MediaStatus.NoMedia:
|
||||
self.play_btn.setText("\u25B6")
|
||||
self._is_playing = False
|
||||
elif status == QMediaPlayer.MediaStatus.LoadedMedia:
|
||||
if self._pending_seek_ms > 0:
|
||||
self.player.setPosition(self._pending_seek_ms)
|
||||
self._pending_seek_ms = 0
|
||||
|
||||
def _on_table_double_clicked(self, row: int):
|
||||
self._play_track_from_index(row)
|
||||
@ -1037,6 +1070,18 @@ class MainWindow(QMainWindow):
|
||||
self.library_scanned_sources = sources
|
||||
self._refresh_library_ui()
|
||||
|
||||
def _resume_playback_if_saved(self):
|
||||
if not self._saved_track_path or not os.path.exists(self._saved_track_path):
|
||||
return
|
||||
for row in range(self.library_table.rowCount()):
|
||||
data_item = self.library_table.item(row, 0)
|
||||
if data_item is None:
|
||||
continue
|
||||
_is_ready, track_data = data_item.data(Qt.ItemDataRole.UserRole)
|
||||
if track_data and track_data.get("path", "") == self._saved_track_path:
|
||||
self._play_track_from_index(row, start_position_ms=self._saved_position_ms)
|
||||
return
|
||||
|
||||
def _extract_ready_track(self, fpath: str, fname: str, handler) -> Dict[str, Any]:
|
||||
size = os.path.getsize(fpath)
|
||||
cover_path = None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user