Skip to content

Remux WAVE_FORMAT_MPEGLAYER3 WAV files to MP3 during import#6526

Merged
snejus merged 13 commits intobeetbox:masterfrom
elainec2024:fix-mpeglayer3-wav-import
Apr 14, 2026
Merged

Remux WAVE_FORMAT_MPEGLAYER3 WAV files to MP3 during import#6526
snejus merged 13 commits intobeetbox:masterfrom
elainec2024:fix-mpeglayer3-wav-import

Conversation

@elainec2024
Copy link
Copy Markdown
Contributor

Description

Fixes #6455

WAV files can contain MP3 audio streams (WAVE_FORMAT_MPEGLAYER3, wFormatTag=0x0055), but beets has no way to tag them correctly and would silently import them with wrong duration, bitrate, and format metadata.

This PR fixes the issue by automatically detecting such files during import and using ffmpeg to remux them into proper .mp3 files. The original WAV is deleted after successful remuxing, and the MP3 is imported normally with correct metadata.

This fix depends on beetbox/mediafile#105 which raises a clear FileTypeError for these files, allowing beets to detect and handle them.

To Do

  • Documentation
  • Changelog
  • Tests

WAV files containing an MP3 stream (WAVE_FORMAT_MPEGLAYER3, wFormatTag=0x0055)
were silently imported with incorrect metadata and format reported as WAVE instead of MP3.

When a WAV file fails to open due to FileTypeError, check if it is a
MPEGLAYER3 WAV. If it is, use ffmpeg to remux it to a proper MP3 file,
removing the WAV container. The original WAV file is deleted after successful
remuxing.

Adds a regression test and test fixture.

Fixes beetbox#6455
@elainec2024 elainec2024 requested a review from a team as a code owner April 11, 2026 21:44
@elainec2024 elainec2024 marked this pull request as draft April 11, 2026 21:54
@elainec2024 elainec2024 force-pushed the fix-mpeglayer3-wav-import branch from 6540242 to 7a20252 Compare April 11, 2026 22:04
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 11, 2026

Codecov Report

❌ Patch coverage is 73.07692% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.99%. Comparing base (83017c0) to head (148e0ac).
⚠️ Report is 14 commits behind head on master.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
beets/util/extension.py 78.94% 2 Missing and 2 partials ⚠️
beets/importer/tasks.py 57.14% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master    #6526   +/-   ##
=======================================
  Coverage   70.99%   70.99%           
=======================================
  Files         150      150           
  Lines       19124    19150   +26     
  Branches     3078     3082    +4     
=======================================
+ Hits        13577    13596   +19     
- Misses       4896     4900    +4     
- Partials      651      654    +3     
Files with missing lines Coverage Δ
beets/importer/tasks.py 90.29% <57.14%> (-0.44%) ⬇️
beets/util/extension.py 74.19% <78.94%> (+2.10%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jackwilsdon
Copy link
Copy Markdown
Member

I'm not sure we want to be automatically converting files on import by default - could this be achieved by a plugin? Or is there a way for us to add support for these MP3-in-WAV files?

@elainec2024
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! I understand the concern about automatically modifying files during import. I did explore the native approach earlier. Mutagen opens MPEGLAYER3 WAV files as WAVE objects and can't read or write their tags correctly. However, I found that the raw MP3 stream can be extracted from the data chunk and read correctly by mutagen.mp3.MP3. So in theory, mediafile could be extended to extract and re-open the stream as MP3, but writing tags back to the WAV container would be more complex.
Would a plugin that users can opt into be more appropriate for the automatic remuxing behavior?

@snejus
Copy link
Copy Markdown
Member

snejus commented Apr 14, 2026

@elainec2024 note I've just released mediafile v0.16.1 with your adjustment, so update pyproject.toml accordingly and run poetry lock.

@snejus
Copy link
Copy Markdown
Member

snejus commented Apr 14, 2026

I'm not sure we want to be automatically converting files on import by default - could this be achieved by a plugin? Or is there a way for us to add support for these MP3-in-WAV files?

An alternative would be to add a configuration under import section, say

import:
  remux_mp3_in_wav_to_mp3: yes

where users have the ability to opt-out if they don't want this to happen.

On the other hand, it seems like users do expect this to happen: #6455 (comment)

@jackwilsdon
Copy link
Copy Markdown
Member

jackwilsdon commented Apr 14, 2026

Happy to have some form of extract_wav_mp3 option. If we are going to pull the MP3 out of the container, is it doable without using ffmpeg?

@snejus
Copy link
Copy Markdown
Member

snejus commented Apr 14, 2026

I don't think so. We will need to have handling similar to the logic in beets.util.extension.

@elainec2024 elainec2024 force-pushed the fix-mpeglayer3-wav-import branch from bd13a2d to 623eb04 Compare April 14, 2026 12:52
@elainec2024
Copy link
Copy Markdown
Contributor Author

I've addressed the first two suggestions of bumping the mediafile dependency and adding a config option under the import section. For the third suggestion, I can replace ffmpeg with pure Python byte extraction if you'd prefer. I confirmed that the MP3 stream can be extracted directly from the WAV data chunk and read correctly by mutagen.mp3.MP3, so it's feasible without any external dependencies. Happy to implement that if that's the direction you want to go!

@snejus
Copy link
Copy Markdown
Member

snejus commented Apr 14, 2026

I confirmed that the MP3 stream can be extracted directly from the WAV data chunk and read correctly by mutagen.mp3.MP3, so it's feasible without any external dependencies. Happy to implement that if that's the direction you want to go!

This is very cool! Sounds like such solution may be less verbose, too?

Copy link
Copy Markdown
Member

@snejus snejus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will review the ffmpeg -> mutagen adjustment once it's finished!

Comment thread beets/config_default.yaml
Comment thread poetry.lock Outdated
Comment thread test/test_importer.py Outdated
@elainec2024 elainec2024 force-pushed the fix-mpeglayer3-wav-import branch 2 times, most recently from 3bf5c6c to 480e67c Compare April 14, 2026 16:42
@elainec2024
Copy link
Copy Markdown
Contributor Author

The docs formatting check is consistently failing on CI but I'm unable to reproduce it locally — running poe format-docs --check and docstrfmt directly both pass with no changes needed. The CI only reports "could be reformatted" without showing the specific diff. Could you help identify what I need to change?

@snejus
Copy link
Copy Markdown
Member

snejus commented Apr 14, 2026

The docs formatting check is consistently failing on CI but I'm unable to reproduce it locally — running poe format-docs --check and docstrfmt directly both pass with no changes needed. The CI only reports "could be reformatted" without showing the specific diff. Could you help identify what I need to change?

Just checked out your branch locally and ran poe format-docs - it indeed reformatted config.rst.

Are you using the same version of docstrfmt?

$ docstrfmt --version
docstrfmt, version 2.0.2

Make sure to poetry install otherwise.

@elainec2024
Copy link
Copy Markdown
Contributor Author

Got it! Running on a fresh clone reproduced it. Thank you!

Copy link
Copy Markdown
Member

@snejus snejus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, some final comments

Comment thread beets/importer/tasks.py Outdated
Comment thread beets/importer/tasks.py Outdated
Comment thread beets/importer/tasks.py Outdated
Comment thread beets/importer/tasks.py Outdated
Comment thread docs/changelog.rst Outdated
Comment thread test/test_importer.py Outdated
Comment thread beets/util/extension.py Outdated
@elainec2024 elainec2024 requested a review from snejus April 14, 2026 20:28
Copy link
Copy Markdown
Member

@snejus snejus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@snejus snejus enabled auto-merge April 14, 2026 22:46
@snejus snejus merged commit 73a4bd7 into beetbox:master Apr 14, 2026
15 checks passed
snejus added a commit that referenced this pull request Apr 18, 2026
Follow-up to #6526. This change moves the `remux_mpeglayer3_wav` call to
before `library.Item.from_path` in `read_item`, so beets no longer
depends on mediafile raising `FileTypeError` for MPEGLAYER3 WAV files.
This is related to beetbox/mediafile#107.
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.

beets incorrectly tags WAV files with WAVE_FORMAT_MPEGLAYER3

3 participants