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
- 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
- Move mount_device, get_device_info, and track scanning to background
WorkerThread (new 'mount_and_load' task type) so the UI stays responsive
- Fix DeviceMonitor auto-poll to run detect_devices() in a background thread
- Refactor _on_device_detection_finished and _on_mount_clicked to use the
new worker; remove synchronous _set_device_mounted
- Avoid double scan (get_track_count + get_all_tracks) — scan once in worker
- Add content_hash fingerprint to _scan_ipod_files and library_cache for
track matching
- Add update_track_metadata / _write_databases_from_tracks with artwork
override support in Nano7Database
- Add 'Sync Metadata' button and context menu action in LibraryTab