-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Normal
-
Affects Version/s: None
-
Component/s: File Formats
-
None
When an ASF/WMA/WMV file has a rating of 0, Picard writes it to the file correctly but drops it when reading the file back. Saving a 0 rating and reloading returns no rating at all. Every other supported format (ID3, Vorbis, MP4, APEv2) preserves a rating of 0, so ASF is inconsistent.
Steps to reproduce
- Load a WMA/WMV/ASF file in Picard.
- Set its rating to 0 (no stars).
- Save the file.
- Remove the file and load it again (or inspect the loaded metadata).
Expected result
The rating loads back as 0, matching what was saved and matching the behavior of all other formats.
Actual result
The rating is missing after reload. The ~rating tag is absent from the loaded metadata even though WM/SharedUserRating was written to the file correctly (verified as ASFDWordAttribute(0)).
Root cause
In ASFFile._load() (picard/formats/asf.py), the WM/SharedUserRating value is normalized from the 0-99 WMA scale to Picard's 0-5 scale and left as an int:
values[0] = int(round(int(str(values[0])) / 99.0 * (config.setting['rating_steps'] - 1)))
A few lines later, all tag values pass through a truthiness filter:
values = [str(value) for value in values if value]
The rating is still an int at this point, so a value of 0 is falsy and is silently dropped. Empty strings must still be filtered out, so the filter itself is correct; the problem is that the rating is tested for truthiness as an int before being converted to a string.
Suggested fix
Convert the normalized rating to str immediately, so 0 becomes '0' and survives the truthiness filter:
values[0] = str(int(round(int(str(values[0])) / 99.0 * (config.setting['rating_steps'] - 1))))
This makes ASF consistent with ID3, Vorbis, MP4, and APEv2, all of which already preserve a rating of 0. Empty strings continue to be filtered as before.
History
- Latent since commit 31af96b5d1 ("Support ratings in WMA files", Philipp Wolfer, 2009-02-20), which introduced the int-normalization plus truthiness-filter pattern. Under Python 2 the code was filter(bool, map(unicode, values)), which stringified before filtering, so 0 (-> u'0', truthy) survived.
- Became a live bug at commit 8834ec3384 ("Fix filter,map and reduce behaviour for py3", Sambhav Kothari, 2017-03-31), which rewrote the line as [str(value) for value in values if value]. This tests the still-int rating 0 for truthiness before converting it, so 0 is now dropped. Shipped from Picard 2.0 onward.
- Commit 683b9346858 ("Drop use of string_(), deprecated", 2018-09-06) only renamed string_() -> str() and did not change the behaviour.