Skip to content

feat(library): Add rating sync with file tags (FMPS_RATING standard)#15952

Open
SimonDedman wants to merge 5 commits intomixxxdj:2.6from
SimonDedman:feature/rating-file-tags-9477
Open

feat(library): Add rating sync with file tags (FMPS_RATING standard)#15952
SimonDedman wants to merge 5 commits intomixxxdj:2.6from
SimonDedman:feature/rating-file-tags-9477

Conversation

@SimonDedman
Copy link
Copy Markdown

Adds support for saving and loading track ratings to/from file metadata tags, addressing a long-requested feature (#9477).

Overview

This implementation uses the FMPS_RATING standard (0.0-1.0 scale), which is compatible with Strawberry, Foobar2000, Quod Libet, and other music players that follow the freedesktop.org Media Player Specs. The feature is non-destructive (does not overwrite POPM/WMP ratings) and user opt-in (disabled by default).

Key design decisions:

  • Non-destructive: Does not overwrite POPM (Windows Media Player) or other rating formats
  • User opt-in: Disabled by default, configurable in Preferences > Library
  • Bidirectional: Separate options for import and export

Changes

TagLib Layer (Phase 1)

Rating import/export functions for all supported formats:

  • ID3v2 (MP3): TXXX:FMPS_Rating frame write, POPM fallback read
  • Xiph (FLAC/OGG): FMPS_RATING Vorbis comment
  • APE (WavPack/APE): FMPS_Rating item
  • MP4 (M4A): ----:org.freedesktop.FMPS:FMPS_Rating atom

Preferences UI (Phase 2)

Two new checkboxes in Library preferences under "Track Metadata Synchronization":

  • Export rating to file tags - Saves Mixxx ratings to files on metadata export or quit
  • Import rating from file tags - Loads ratings when tracks are selected/imported

Integration (Phase 3-4)

  • MetadataSourceTagLib: New importRating() and exportRating() virtual methods
  • Track::exportMetadata(): Exports rating after metadata when preference enabled
  • SoundSourceProxy::updateTrackFromSource(): Imports rating during track metadata update
  • TrackCollectionManager::exportTrackMetadataBeforeSaving(): Respects rating export preference
  • RatingSyncWorker: Background worker for syncing ratings when opening playlists/crates

Rating Format Support

Format Tag Type Read Write
MP3 TXXX:FMPS_Rating
MP3 POPM (fallback)
FLAC/OGG FMPS_RATING
M4A FMPS_Rating atom
WavPack FMPS_Rating (APE)

Conversion Scale

Mixxx FMPS Display
0 0.0 No rating
1 0.2
2 0.4 ★★
3 0.6 ★★★
4 0.8 ★★★★
5 1.0 ★★★★★

Testing

  • ✅ Tested bidirectional sync with Strawberry music player
  • ✅ Ratings export correctly to FMPS_Rating format on quit
  • ✅ Ratings import correctly when selecting tracks in library
  • ✅ Verified with kid3: exported rating shows FMPS_Rating = 1.0 for 5★

Screenshots

image

The new preferences options are located in Preferences > Library > Track Metadata Synchronization section

Test plan

  • Enable "Export rating to file tags" preference
  • Rate a track 5 stars in Mixxx
  • Close Mixxx
  • Verify file has FMPS_Rating = 1.0 (use exiftool, kid3, or similar)
  • Enable "Import rating from file tags" preference
  • Add a file with existing FMPS_Rating to library
  • Verify rating appears in Mixxx

Closes #9477

Adds support for saving and loading track ratings to/from file metadata
tags, addressing a long-requested feature (mixxxdj#9477).

## Overview

This implementation uses the FMPS_RATING standard (0.0-1.0 scale),
which is compatible with Strawberry, Foobar2000, Quod Libet, and other
music players. The feature is non-destructive (does not overwrite
POPM/WMP ratings) and user opt-in (disabled by default).

## Changes

### TagLib Layer (Phase 1)
- ID3v2: FMPS_Rating TXXX frame read/write + POPM fallback read
- Xiph: FMPS_RATING Vorbis comment for FLAC/OGG
- APE: FMPS_Rating item for WavPack/APE files
- MP4: ----:org.freedesktop.FMPS:FMPS_Rating atom for M4A

### Preferences (Phase 2)
- Two new checkboxes in Library preferences:
  - "Export rating to file tags" - saves Mixxx ratings to files
  - "Import rating from file tags" - loads ratings from files

### Integration (Phase 3-4)
- MetadataSourceTagLib: importRating() and exportRating() methods
- Track: Exports rating after metadata export when preference enabled
- SoundSourceProxy: Imports rating during track metadata update
- TrackcollectionManager: Export-on-quit now respects rating preference
- RatingSyncWorker: Background sync when opening playlists/crates

## Rating Format Support

| Format | Tag Type | Read | Write |
|--------|----------|------|-------|
| MP3 | TXXX:FMPS_Rating | ✅ | ✅ |
| MP3 | POPM (fallback) | ✅ | ❌ |
| FLAC/OGG | FMPS_RATING | ✅ | ✅ |
| M4A | FMPS_Rating atom | ✅ | ✅ |
| WavPack | FMPS_Rating (APE) | ✅ | ✅ |

## Conversion Scale

| Mixxx | FMPS | Display |
|-------|------|---------|
| 0 | 0.0 | No rating |
| 1 | 0.2 | ★ |
| 2 | 0.4 | ★★ |
| 3 | 0.6 | ★★★ |
| 4 | 0.8 | ★★★★ |
| 5 | 1.0 | ★★★★★ |

Closes mixxxdj#9477

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@JoergAtGithub
Copy link
Copy Markdown
Member

Welcome at Mixxx!
As a first-time contributor we need you to sign the Mixxx Contributor Agreement and comment here when you have done so. It gives us permission to distribute your contribution under the GPL v2 or later license and the Apple Mac App Store. It is also helpful for us to have contact information for contributors in case we may need it in the future.

Copy link
Copy Markdown
Member

@acolombier acolombier left a comment

Choose a reason for hiding this comment

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

Some high level comments before doing a more deep dived review:

  • Please make sure to use smart pointer
  • Use snake_case for new config keys
  • Please explain why we need a custom worker as opposed to use the same metadata runner
  • We will need to have some test to ensure the supported file types are indeed well exported

@SimonDedman
Copy link
Copy Markdown
Author

Thanks for the review! All four items addressed in the latest push:

  1. Smart pointer: Removed RatingSyncWorker entirely, so the raw pointer is gone. The existing SoundSourceProxy::updateTrackFromSource() pipeline already handles rating import via syncParams.importRatingFromFile, making the custom worker unnecessary.

  2. snake_case config keys: Renamed to export_rating_to_file_tags and import_rating_from_file_tags.

  3. Why custom worker?: See master sync first merge #1 — we don't need one. Removed it along with all references in baseplaylistfeature and cratefeature.

  4. Tests: Added ratingexportimport_test.cpp with:

    • Parameterized round-trip test across all 8 supported formats (MP3, FLAC, OGG, Opus, M4A, WAV, AIFF, WavPack)
    • All rating values (1–5) round-trip test
    • Clear rating test (export 0, verify no rating returned)
    • No-rating-initially test (fresh file returns nullopt)

- Remove RatingSyncWorker (custom worker unnecessary; existing
  SoundSourceProxy pipeline handles rating import)
- Rename config keys to snake_case: export_rating_to_file_tags,
  import_rating_from_file_tags
- Add ratingexportimport_test.cpp with round-trip tests for all 8
  supported formats, all rating values, clear, and no-rating cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SimonDedman and others added 2 commits February 7, 2026 13:40
TagLib normalizes APE tag keys to uppercase on save/reload, causing
the WavPack rating round-trip test to fail. Changed FMPS_Rating to
FMPS_RATING in trackmetadata_ape.cpp to match this behavior (consistent
with the Xiph Comment code which already used uppercase).

Also fix clang-format issues: indentation in trackcollectionmanager.cpp,
trailing blank lines, include ordering in metadatasourcetaglib.h, and
double blank line in trackmetadata_mp4.cpp.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break long namespace-qualified config key identifiers across lines
to satisfy clang-format's line length rules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@SimonDedman
Copy link
Copy Markdown
Author

Some high level comments before doing a more deep dived review:

* Please make sure to use smart pointer

* Use `snake_case` for new config keys

* Please explain why we need a custom worker as opposed to use the same metadata runner

* We will need to have some test to ensure the supported file types are indeed well exported

Cheers for the notes Antoine. PR should be fixed now. Cheers!

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@SimonDedman
Copy link
Copy Markdown
Author

@acolombier Antoine any chance someone could have a peek at this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants