11 Commits

Author SHA1 Message Date
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
bfd146a4a0 feat: add star rating to metadata editor with write support to audio tags
- ClickableStarRating widget — 5 stars with hover preview, toggle to unrate
- Rating field in MetadataEditorDialog (0–5 stars), saved to LibraryCache (0–100)
- MetadataHandler writes rating to MP3 (POPM), M4A (\xa9rt), FLAC/Ogg (RATING)
2026-06-01 19:59: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
dd93440146 fix: hide vertical header (row numbers) in library table 2026-06-01 15:18:25 +07:00
Maksim Totmin
a1d6465730 fix: sort by # now uses track_num from UserRole instead of cell text, so EQ animation doesn't break sorting 2026-06-01 15:13:00 +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
4c703db802 Add incremental metadata/artwork sync to iPod (iTunes-style)
- content_hash() — SHA1 of first 64KB for matching local↔iPod tracks
- Nano7Database.update_track_metadata() — update DB records only,
  never touch audio files on the iPod
- Nano7Database.find_track_by_content_hash() + find_track_by_metadata()
- _write_databases_from_tracks() extracted from sync_itunescdb,
  supports artwork_overrides parameter
- LibraryCache stores content_hash for cached tracks
- Worker + UI: 'Sync Metadata to iPod' button + context menu item
- _REMOVE_ARTWORK sentinel for explicit artwork removal
- track_info.py: add play_count, rating, last_played, skip_count fields
2026-06-01 13:21:22 +07:00
Maksim Totmin
3bfb0ebbfe Fix UI freeze on iPod mount/track-load and add content_hash/sync-metadata
- 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
2026-06-01 12:37:49 +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