28 Commits

Author SHA1 Message Date
Maksim Totmin
54cd87654f fix: reliable play count sync with auto-sync + deferred iTunesDB commit
Stops double-counting when iPod Nano 7G regenerates Play Counts
file after a DB write (firmware quirk, new mtime with stale data).

Architecture:
- Mount: only max(cache, base) — never reads Play Counts delta
- 2s after mount → auto-sync worker: reads delta, subtracts stored
  (persistent guard), applies effective to cache, defers DB write
- iTunesDB commit deferred to next stable mount (checks W_OK first)
- read_play_stats() uses 3s retry + read_play_counts_delta() fallback
  (filesystem-based PC parsing, independent of iTunesDB I/O errors)

Changes:
- ipod_nano7_db.py: sync_itunescdb() returns bool; new
  read_play_counts_delta() method; get_all_tracks() no longer adds
  delta; _merge_play_stats() drops delta addition; suppress iOpenPod
  ERROR logs to CRITICAL
- library_cache.py: schema v2 with playcounts_log table +
  get/set_last_synced_delta, migration from v1
- worker.py: session guards (_processed_fwids, _auto_synced_fwids,
  _pending_commit_fwids); auto_sync_playcounts task; deferred commit
  in mount
- app.py: QTimer.singleShot(2000) after mount for auto-sync
- ipod_tab.py: _on_auto_state_changed rewritten (no unmount, mp guard)
- library_tab.py: refresh_play_counts_from_cache() method
2026-06-02 00:54:18 +07:00
Maksim Totmin
d08125c7b3 fix: use correct case for Location key in read_play_stats 2026-06-01 22:32:17 +07:00
Maksim Totmin
2d77b3ea3e feat: modernize iPod tab with table view, search, and sorting, fix LibraryCache API 2026-06-01 22:11:36 +07:00
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
309ba994a5 fix: rating no longer reset to -1 during mount/sync from iPod Play Counts
The Play Counts parser uses rating=-1 as sentinel for 'no change',
but the merge logic used  which treats -1 as truthy, overwriting
real ratings with -1 in all three paths: mount-time sync (worker.py),
DB regeneration (_merge_play_stats), and track listing (get_all_tracks).
2026-06-01 19:45:32 +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
a0b1aba50e fix: resolve iOpenPod import conflict by removing src/ from sys.path during iop imports
iTunesDB_Writer's internal 'from ipod_device import ChecksumType' was resolving
to our local src/ipod_device.py instead of iOpenPod's version, even with
iop path at sys.path[0] (Python 3.14 import resolution quirk).

Now temporarily removes project src/ from sys.path during iOpenPod imports,
restoring it afterwards. The validated flow: main.py imports local ipod_device
first, then _import_iop evicts and replaces it when iPod tab is used.
2026-06-01 00:55:12 +07:00
Maksim Totmin
5ba346cc84 fix: add project-local iOpenPod/ fallback so /tmp cleanup won't break the app 2026-06-01 00:46:44 +07:00
Maksim Totmin
e3666c8c64 refactor: dynamic iOpenPod discovery, add to requirements
- Replace hardcoded /tmp/iOpenPod with importlib.util.find_spec lookup
  with fallback to /tmp/iOpenPod and ~/iOpenPod for dev/backward compat
- Add iopenpod>=1.0.53 to requirements.txt
- Remove deprecated mkdir temp/downloads/converted from install.sh
  (all paths now XDG-compliant)
2026-06-01 00:40:06 +07:00
Maksim Totmin
63d72adae6 fix: move iPod track deletion to background thread with progress bar, batch DB sync 2026-05-31 22:43:59 +07:00
Maksim Totmin
3da5e15fe0 fix: add +global_header +genpts ffmpeg flags, preserve cover art streams, gapless metadata for iPod Nano 7 2026-05-31 22:13:47 +07:00
Maksim Totmin
71928d3478 feat: integrate artwork module into sync pipeline for Nano 7G 2026-05-31 21:45:51 +07:00
Maksim Totmin
0f12c4985f fix: robust iOpenPod imports and sync error handling
_import_iop() now permanently evicts local ipod_device from sys.modules
on first call (instead of restoring it), caches all iOpenPod modules,
and avoids the spurious ModuleNotFoundError cascade on rapid calls.

sync_itunescdb() wrapped in try/except — no longer propagates
import/sync errors to add_track() callers.

ipod_device.py fallback no longer duplicates files to /Music/ when
add_track() already copied them to F00/.
2026-05-31 21:20:42 +07:00
Maksim Totmin
4f7bd2709c fix: resolve ipod_device import conflict with lazy imports
main.py imports our local ipod_device first, which caches it in
sys.modules.  When ipod_nano7_db later tried 'from ipod_device import
ChecksumType', Python returned the cached local module (which lacks
ChecksumType), causing 'ipod_nano7_db not available' and falling back
to simple file copy into wrong /Music/ directory.

Fix: replace top-level iOpenPod imports with lazy _import_iop() helper
that temporarily evicts local ipod_device from sys.modules, adds
/tmp/iOpenPod to sys.path, loads the iOpenPod module, then restores
the path.  All iOpenPod imports now happen inside method bodies only.
2026-05-31 21:03:11 +07:00
Maksim Totmin
4c4a19640d feat: integrate iOpenPod SQLiteDB_Writer and iTunesDB_Writer
Replace custom SQLite schema and binary mhbd writer with iOpenPod's
battle-tested database generators.  The Nano 7G firmware reads SQLite
exclusively and cross-references db_id between binary and SQLite —
the old code had mismatched IDs, missing base_location table, and
incomplete schema.

Changes:
- ipod_nano7_db.py: full rewrite — use iOpenPod write_sqlite_databases
  + write_itunesdb instead of manual SQLite inserts and broken mhfd
- add_track() now copies file then regenerates ALL databases atomically
- sync_itunescdb() scans iPod filesystem, builds TrackInfo, calls
  iOpenPod writers with matching db_pid
- get_all_tracks() scans filesystem with mutagen tag extraction
- ipod_mhbd_writer.py: kept as standalone reference/utility

Fix: music finally shows on iPod Nano 7G (xC695)
2026-05-31 20:54:45 +07:00
Maksim Totmin
fba3fe7d05 Add HASHAB signing to iTunesCDB header + fix sync_itunescdb
- Set hashing_scheme=3 at offset 0x30 (iPod Nano 7G requirement)
- Compute and write HASHAB signature at offset 0xAB
- Import compute_hashab from hashab module
- iTunesCDB.ext creation restored (was removed accidentally)
- 0-byte iTunesDB placeholder (firmware expects this file)
2026-05-31 19:10:44 +07:00
Maksim Totmin
01902d252a Fix container, version_info, db_info schemas per iOpenPod reference
- container: 24 columns including smart_is_dynamic, smart_is_filtered
- version_info: full schema (id, major, minor, compatibility, update_level)
- db_info: full schema (pid, audio_language=-1, subtitle_language=-1)
- genre_map: rewritten to match reference schema
- item: add genre_id column
- Music container: smart_is_dynamic=1, smart_is_filtered=1
2026-05-31 19:01:05 +07:00
Maksim Totmin
29e4cc7398 Fix missing DB tables: container, version_info, db_info, genre_map, extra DBs
- Add container table with Master (iPod) and Music containers
- Add version_info, db_info, genre_map, location_kind_map, category_map
- Create Dynamic.itdb, Extras.itdb, Genius.itdb with minimal schemas
- Insert default container entries with correct PIDs
- Nano 7G firmware requires all these for music display
2026-05-31 18:55:20 +07:00
Maksim Totmin
75ea0204ff Fix iTunesCDB header: always generate proper mhfd header
- Remove broken header-preservation logic (was reusing all-zero header)
- Always write header with correct total_size at offset 8
- Add version field 0x00020002 at offset 12
- Delete old iTunesDB (iTunesDB.old) to avoid firmware confusion
2026-05-31 18:47:36 +07:00
Maksim Totmin
ab912c2e49 Fix iPod transfer: create Music/F00 dir and iTunesCDB on first run
- copy_track_to_ipod: create target directory via os.makedirs
- sync_itunescdb: create iTunesCDB from scratch if missing
- sync_itunescdb: create iTunesCDB.ext with hash if missing
2026-05-31 18:39:25 +07:00
Maksim Totmin
cc429eed31 Add _ensure_schema() to create SQLite tables if missing
- Creates all required tables (item, artist, album, avformat_info,
  store_info, item_to_container, track_size_calc) in Library.itdb
- Creates location table in Locations.itdb
- Called from Nano7Database.__init__(), so empty DB is always valid
- Fixes 'no such table: item' error on iPod with wiped DB
2026-05-31 18:31:26 +07:00
Maksim Totmin
489d99c068 Add cover art pipeline (extract/encode/cache) and iTunes-style player
- 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
2026-05-31 17:45:01 +07:00
Maksim Totmin
885a401ad0 Rebrand project to neo-pod-desktop with updated description and improvements
- Rename project from 'youtube-to-ipod-nano' to 'neo-pod-desktop' across all files
- Update description: 'Desktop application for downloading, converting,
  managing and transferring music to iPod Nano devices'
- Update setup.py: name, description, url, entry_points, author
- Update run.py: docstring and argparse description
- Update src/__init__.py: module docstring
- Update src/main.py: docstring, window title, about label
- Update src/cli.py: docstring, class docstring, argparse description
- Update README.md: title and project description
- Improve duplicate track detection in ipod_nano7_db.py:
  case-insensitive matching, bulk removal with file cleanup
2026-05-31 16:19:15 +07:00
Maksim Totmin
11c40c8a99 Fix Nano 7 database: duration samples, HASHAB cbk, duplicate detection, Locations.itdb
- 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
2026-05-31 14:39:04 +07:00
Maksim Totmin
47bfdf0011 Add track management: view and delete tracks on iPod from Transfer tab
- 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
2026-05-31 12:37:13 +07:00
Maksim Totmin
1b4c092a04 Add metadata extraction from local audio files for proper tag preservation
- 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
2026-05-31 12:26:53 +07:00
Maksim Totmin
761f896ea8 Add iPod Nano 7 SQLite database support for proper track transfer
- 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
2026-05-31 12:07:17 +07:00