- Change ithmb file open from 'wb' (truncate) to 'ab' (append)
- Compute write offset from actual file position via seek(0,2)
- Prevents losing all previous artwork data on each transfer
- Config is now in XDG_CONFIG_HOME (~/.config/) for write access in AppImage
- Auto-migrates existing config.ini from project root on first run
- Creates directory on save() if not exists
- Create venv with --copies to avoid system pip restrictions (PEP 668)
- Install deps into venv before PyInstaller run
- Use absolute paths for --add-data
- New src/artwork/ module with iPod Nano 7G cover art support
- codecs: RGB565/RGB555/UYVY/JPEG encode/decode via numpy
- presets: 50+ iPod artwork format definitions
- chunks: ArtworkDB binary parser/writer (MHFD→MHSD→MHLI→MHII→MHNI)
- writer: ithmb file generation + ArtworkDB assembly
- extractor: cover extraction from audio files (APIC/covr/FLAC pictures)
- cache: local JPEG cache for player UI
- iTunes-style playback bar with 60x60 cover art display
- Scan library now caches cover art by artist+album
- iPod Nano7 transfer writes artwork to ArtworkDB + ithmb
- FFmpeg conversion preserves embedded cover art (-map 0:t?)
- One-time reembed_covers.py script in /tmp
- MetadataHandler process_file() fallback to cover_path
- Bug fixes: missing read_existing_artwork import, signed PID in Q format
Library tab redesign:
- Merge 'Ready to Transfer' table and 'Source Files' list into one unified table
- Replace bottom buttons with top toolbar (Add Files, Convert, Transfer, Remove, Refresh, Search)
- Add search/filter bar to filter tracks by title
- Add context menu (right-click): Play, Convert/Transfer, Show in File Manager, Remove
- Source tracks shown in gray (dimmed), ready tracks in normal color
- Remove status column with emoji — clean iTunes-like layout
Settings:
- Add 'Show YouTube Download Tab' checkbox in Advanced section
- Download tab hidden by default, can be enabled in Settings
- Uses QTabWidget.setTabVisible() to toggle without destroying widgets
- Add QMediaPlayer + QAudioOutput for playback within the app
- Playback bar at top of Library tab with:
- Transport controls: Prev, Play/Pause, Next
- Seekable progress slider with current/total time labels
- Volume slider
- Now playing label showing Artist - Title
- Double-click table row to start playback
- Playback auto-advances to next track on end
- Move Output Directory setting from Library tab to Settings tab
- Output Directory now shown in a dedicated 'Library' group in Settings
- Add _setup_player(), _play_track_from_index(), _format_time_ms()
- Add all playback handlers for position, duration, status changes
- Replace QListWidget with QTableWidget in Library tab
- 6 columns: #, Artist, Title, Duration, Genre, Size
- Track numbers displayed without leading zeros (1, 2, 3...)
- Dash shown when track number is unknown
- Sort by track number within album by default
- Clickable column headers for sorting by any field
- Alternating row colors for readability
- Row selection (SelectRows behavior)
- Artist and Title columns stretch to fill space
- Add genre field to TrackInfo dataclass
- Include genre in metadata extract_tags() return value
- Extract genre and track_number during library scan
- Hide 'Ready to Transfer' header and list when no tracks are ready
- Hide 'Source Files — Need Conversion' header and list when no source files exist
- Sections appear/disappear automatically on scan, add, convert, delete
- Add self.library_source_files list to store manually added file paths
- _on_library_add_files now adds to library_source_files instead of
calling _scan_library() (which would clear the list)
- _refresh_source_ui merges scanned sources + library_source_files
with dedup by path. All items get proper UserRole data.
- _on_library_remove_selected now removes from library_source_files too
- Previously added files would be lost on every scan because
_scan_library() called .clear() on the source list
- _on_library_remove_selected now removes files from disk with confirmation
- Shows count, file names, sizes, and total size in dialog
- Cleans up empty parent directories after deletion
- Re-scans library after deletion to refresh the UI
- Add _setup_linux_theming() called before QApplication creation
- Auto-detect libqgtk3.so plugin from common Qt6 plugin paths
- Sets QT_QPA_PLATFORMTHEME=gtk3 so Qt follows system GTK theme
- Falls back to qt6ct if gtk3 plugin not found
- Falls back to Fusion style if no platform theme available
- Respects user-set QT_QPA_PLATFORMTHEME (never overrides)
- Add _cleanup_worker() helper: calls wait() then deleteLater() before
dropping the Python reference. This ensures the C++ thread is fully
cleaned up before the QObject is destroyed.
- Replace all 'self.worker_thread = None' with _cleanup_worker()
- Add running thread guards to all handlers: prevent starting a new
WorkerThread while one is already running
- Add timeout to wait() calls (3000ms) to avoid hanging on close
- WorkerThread.stop() now calls self.wait() to ensure clean shutdown
- Rename Convert → Library, Transfer → iPod
- Library tab scans Output Directory on startup and shows all m4a/mp3
files with metadata (artist, title, duration, size)
- Library tab has two sections: 'Ready to Transfer' and 'Source Files'
(FLAC/WAV/OGG waiting for conversion)
- Move 'Transfer to iPod' button from Transfer tab to Library tab
- iPod tab is now device-only: mount/eject, view tracks, remove, scan orphans
- No more RAM-only converted_tracks list — library is always from disk
- Auto-scan on startup, after conversion, after download, on directory change
- Add Mount/Eject buttons with device status display
- Auto-mount device on refresh
- Load and display tracks from iPod SQLite database
- Delete selected tracks from iPod with confirmation
- Scan for orphaned files (audio files without DB entries)
- Bulk delete orphaned files to reclaim space
- Show track count and free/total space
- Replace libimobiledevice dependency with udisksctl (works without root)
- Detect block devices (/dev/sdX) for iPod partitions
- Add mount_device() using udisksctl mount -b
- Add unmount_device() using udisksctl unmount + power-off
- Update transfer_file() to use Nano7Database.add_track()
- Handle duplicate skips (add_track returns None)
- Fall back to simple file copy if Nano 7 DB unavailable
- Fix avformat_info duration: store in SAMPLES not ms (firmware uses this to
determine playback length; wrong units caused tracks to stop at ~3-11 seconds)
- Add Locations.itdb insert/delete for each track (ipod_path mapping)
- Add sync_locations_cbk() with HASHAB signing via WASM module
- Detect FirewireGuid from SysInfo for cbk header signature
- Add duplicate detection in add_track (title+artist match, skip if exists)
- Add get_orphaned_files() and delete_orphaned_files() for cleanup
- Sync cbk after every add/delete operation
- Add get_all_tracks() to load all tracks with file paths via tag matching
- Add delete_track() to remove tracks from SQLite DB and delete physical files
- Add _cleanup_orphaned_entries() to remove orphaned artist/album records
- Add track count label and 'Remove Selected' button to Transfer tab
- Load existing tracks on device detection
- Confirmation dialog before deleting tracks
- Reload track list after successful transfer
- Add extract_tags() to metadata_handler.py: reads tags from FLAC, MP3, OGG, M4A
- Support ID3v2 (MP3), Vorbis comments (FLAC/OGG), MP4 tags (M4A)
- Extract cover art from METADATA_BLOCK_PICTURE (FLAC), APIC (MP3), covr (M4A)
- Update _run_convert to use extract_tags instead of guessing from filename
- Fix tag reading in ipod_nano7_db.get_audio_info() using EasyID3 for MP3
- Create ipod_nano7_db.py module for Nano 7 SQLite DB management (Library.itdb)
- Fix case-insensitive iPod label detection in device detection
- Add /run/media to mount point search paths for Arch Linux
- Support already-mounted devices without requiring ifuse
- Add local file selection in Convert tab (FLAC, WAV, OGG, etc.)
- Transfer now properly registers tracks in iPod database so they appear in UI