9 Commits

Author SHA1 Message Date
Maksim Totmin
b7367e63c4 fix: correct playlist creation on iPod (real db_track_id, metadata fallback, Settings hover fix) 2026-06-01 21:05:39 +07:00
Maksim Totmin
8861e55f32 Add shuffle and repeat controls with end-of-playlist fix 2026-06-01 20:32:56 +07:00
Maksim Totmin
aaaf354c9d fix: restore JumpStyle seek slider and fix fractional truncation bug
- Add JumpStyle(QProxyStyle) to player_header.py so clicking the seek
  bar jumps to the clicked position (lost during player_header extraction)
- Fix _on_slider_user_action: int(frac) always truncated to 0
  (e.g. 500/1000=0.5 → int(0.5)=0), now emits raw value 0-1000
- Fix _on_user_seek_frac: properly converts slider value (0-1000) to
  playback position with bounds clamping
2026-06-01 20:09:52 +07:00
Maksim Totmin
a66446120c refactor: remove dead code — unused tab_switch_requested signal, scope concept in hotkeys, redundant ipod_tab_index 2026-06-01 20:04:58 +07:00
Maksim Totmin
b68b86929b feat: drag-and-drop tracks onto playlist (sidebar) and iPod (tab + nav icon) 2026-06-01 20:00:56 +07:00
Maksim Totmin
a36312c9d7 fix: QThread crash on iPod connect and device detection — proper wait() lifecycle
Root cause: QThread C++ destructor ran while underlying OS thread was
still terminating. The pattern of checking isRunning() and using
short timeouts (wait(3000/5000)) left a window where the Python
reference was dropped and Shiboken destroyed the C++ object before
the thread fully exited.

Fixes:
- Remove all self.finished.connect(self.deleteLater) — unsafe,
  deleteLater from within run() or from worker thread races with
  thread termination
- _cleanup_worker: always call wait() (no timeout) before nulling
  the Python reference — guarantees OS thread is fully dead
- Add _is_worker_running() helper with try/except RuntimeError
  guard to safely check stale C++ objects
- DeviceMonitor._poll/stop/wait: use wait() with no timeout
- closeEvent: guard isRunning() with try/except RuntimeError
2026-06-01 19:25:30 +07:00
Maksim Totmin
199fb43f04 feat: iTunes-style sidebar + global player header + album grid
- New: sidebar_widget.py — iTunes-like sidebar with library/playlist/nav sections, GTK palette
- New: player_header.py — global player header (cover, now playing, controls) above splitter
- New: playlist_manager.py — playlist CRUD with JSON persistence
- Refactor: moved QMediaPlayer from LibraryTab to MainWindow for clean architecture
- Refactor: LibraryTab — removed all player code, added play_requested signal
- Feat: album grid (IconMode) — grouped by album name, rounded covers 8px, Spotify-like
- Feat: NumericTableItem — numeric sort for track # column (1,2,3...10,11 not 1,10,2)
- Feat: track_num animation fixed — finds row by path, not index (no freeze on sort)
- Feat: album_artist tag extracted and stored in TrackInfo
- Feat: state persistence — view_mode/filtered_album saved to config.ini on close
- Feat: Ctrl+B toggle sidebar
- Fix: sidebar splitter handle colour from GTK palette
2026-06-01 14:46:58 +07:00
Maksim Totmin
25bb8105ac feat: auto-detect iPod connection via QTimer polling + fix AlreadyMounted in _mount_linux 2026-06-01 12:29:43 +07:00
Maksim Totmin
71d162520c refactor: extract main.py into ui/ tabs + app.py, extract search_itunes into services/, remove MusicBrainz 2026-06-01 09:59:00 +07:00