Skip to content

Fix crash when writing BPM/cue data to MTP devices#38

Merged
pixelate merged 2 commits into
mainfrom
claude/fix-tp7-mtp-write-crash
Mar 25, 2026
Merged

Fix crash when writing BPM/cue data to MTP devices#38
pixelate merged 2 commits into
mainfrom
claude/fix-tp7-mtp-write-crash

Conversation

@pixelate
Copy link
Copy Markdown
Owner

@pixelate pixelate commented Mar 24, 2026

Summary

  • Restructures the sync pipeline so BPM (ACID chunk) and cue point data is injected into a local temp file before the file is installed to the device, rather than writing back to the MTP path after installation
  • The TP-7's MTP implementation does not support overwriting, deleting, or re-writing files — the only reliable operation is installing a fresh file to a non-existent destination
  • For the converted path: BPM and cue points are injected via a post-transcode block yielded inside `Audio#transcode`, after ffmpeg but before `FileUtils.install`
  • For the copied + BPM path: BPM is written directly from the source library file to the MTP target via `AcidChunk.write_bpm`, with no intermediate local copy (avoids disk space issues with large 96/24 WAV files)

FileUtils.install deletes the destination file internally before copying.
On MTP devices, deleting a file can cause the parent directory to be
auto-removed if it becomes empty, and install's own internal mkdir_p
does not reliably recreate it.

Mirror the pattern used in FileConverter: explicitly delete the file,
call mkpath to guarantee the parent directory exists, then install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pixelate
Copy link
Copy Markdown
Owner Author

What's left to investigate

The current approach still has an untested path: for the copied + BPM case, AcidChunk.write_bpm now writes directly to the MTP target path (File.open(mtp_path, 'wb')). We haven't been able to confirm this works because the disk was full (198MB free on a 932GB disk). Things to try next:

1. Free up disk space and re-run

The most important next step. With the disk at 100%, even the local bpm.tmp files used in the converted path fail. Free some space and run the sync again to see if the approach works end-to-end.

2. Verify AcidChunk.write_bpm → MTP direct write

AcidChunk.write_bpm uses File.open(output_filepath, 'wb') with no explicit mode. We know FileUtils.install (which uses File.open(dest, 'wb', s.stat.mode) with mode 0644) works for fresh files. It's likely that the no-mode variant also works, but this hasn't been confirmed on the TP-7.

If it doesn't work, the fallback is to use FileUtils.install directly after AcidChunk.write_bpm writes to a local temp — but that requires enough disk space for one copy of the file.

3. Cue point injection for the copied path

Currently, cue points are only injected for the converted path (in the post-transcode block). For the copied path, cue points are not touched — they should already be correct since no sample rate change occurs. Confirm this assumption holds for all TP-7 use cases.

4. Check safe_copy ENOENT silencing

During the last run, three Errno::ENOENT messages appeared from safe_copy before the ENOSPC crash. These are FileUtils.install failures silently swallowed. Worth investigating whether those are expected (e.g. skipping already-synced files that don't need to be re-installed) or a sign of a remaining MTP issue.

@pixelate pixelate force-pushed the claude/fix-tp7-mtp-write-crash branch from de3212e to 48c073d Compare March 24, 2026 20:15
All attempts to delete or overwrite existing files on the TP-7 via MTP
fail (ENOENT, EIO, ENOTDIR). The only reliable operation is installing
a fresh file to a non-existent destination.

Restructure the pipeline so BPM (ACID chunk) and cue point injection
happen on a local temp file before it is installed to the device,
ensuring the MTP path is written exactly once per file.

- Add a post-transcode block to Audio#transcode, yielded with the local
  temp path after ffmpeg but before FileUtils.install
- Thread the block through FileConverter#convert (before_transcode
  becomes a keyword param, freeing the block slot for post_transcode)
- For the converted path: inject BPM and cue points into the local temp
  via inject_acid_bpm / inject_cue_points private helpers
- For the copied + BPM path: write BPM directly from the source library
  file to the MTP target path via AcidChunk.write_bpm, avoiding any
  local temp copy (important when disk space is tight)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pixelate pixelate force-pushed the claude/fix-tp7-mtp-write-crash branch from 48c073d to 50e68a5 Compare March 25, 2026 07:12
@pixelate pixelate merged commit ff6253c into main Mar 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant