Read and write POPM tags
This commit is contained in:
parent
7f2c17a0b7
commit
16793744c0
@ -40,6 +40,7 @@
|
||||
#include <opusfile.h>
|
||||
#endif
|
||||
#include <oggflacfile.h>
|
||||
#include <popularimeterframe.h>
|
||||
#include <speexfile.h>
|
||||
#include <tag.h>
|
||||
#include <textidentificationframe.h>
|
||||
@ -180,6 +181,25 @@ void TagReader::ReadFile(const QString& filename,
|
||||
song);
|
||||
}
|
||||
}
|
||||
|
||||
// Check POPM tags
|
||||
// We do this after checking FMPS frames, so FMPS have precedence, as we
|
||||
// will consider POPM tags iff song has no rating/playcount already set.
|
||||
qLog(Debug) << "POPM";
|
||||
if (!map["POPM"].isEmpty()) {
|
||||
const TagLib::ID3v2::PopularimeterFrame* frame =
|
||||
dynamic_cast<const TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
||||
if (frame) {
|
||||
// Take a user rating only if there's no rating already set
|
||||
if (song->rating() <= 0 && frame->rating() > 0) {
|
||||
song->set_rating(ConvertPOPMRating(frame->rating()));
|
||||
}
|
||||
if (song->playcount() <= 0 && frame->counter() > 0) {
|
||||
song->set_playcount(frame->counter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if (TagLib::Ogg::Vorbis::File* file = dynamic_cast<TagLib::Ogg::Vorbis::File*>(fileref->file())) {
|
||||
if (file->tag()) {
|
||||
@ -486,8 +506,26 @@ bool TagReader::SaveSongStatisticsToFile(const QString& filename,
|
||||
|
||||
if (TagLib::MPEG::File* file = dynamic_cast<TagLib::MPEG::File*>(fileref->file())) {
|
||||
TagLib::ID3v2::Tag* tag = file->ID3v2Tag(true);
|
||||
|
||||
// Save as FMPS
|
||||
SetUserTextFrame("FMPS_Rating", QString::number(song.rating()), tag);
|
||||
SetUserTextFrame("FMPS_PlayCount", QString::number(song.playcount()), tag);
|
||||
|
||||
// Also save as POPM
|
||||
TagLib::ID3v2::PopularimeterFrame* frame = NULL;
|
||||
|
||||
const TagLib::ID3v2::FrameListMap& map = file->ID3v2Tag()->frameListMap();
|
||||
if (!map["POPM"].isEmpty()) {
|
||||
frame = dynamic_cast<TagLib::ID3v2::PopularimeterFrame*>(map["POPM"].front());
|
||||
}
|
||||
|
||||
if (!frame) {
|
||||
frame = new TagLib::ID3v2::PopularimeterFrame();
|
||||
tag->addFrame(frame);
|
||||
}
|
||||
|
||||
frame->setRating(ConvertToPOPMRating(song.rating()));
|
||||
frame->setCounter(song.playcount());
|
||||
} else {
|
||||
// Nothing to save: stop now
|
||||
return true;
|
||||
@ -734,3 +772,30 @@ bool TagReader::ReadCloudFile(const QUrl& download_url,
|
||||
}
|
||||
#endif // HAVE_GOOGLE_DRIVE
|
||||
|
||||
float TagReader::ConvertPOPMRating(const int POPM_rating) {
|
||||
if (POPM_rating < 0x01) {
|
||||
return 0.0;
|
||||
} else if (POPM_rating < 0x40) {
|
||||
return 0.20; // 1 star
|
||||
} else if (POPM_rating < 0x80) {
|
||||
return 0.40; // 2 stars
|
||||
} else if (POPM_rating < 0xC0) {
|
||||
return 0.60; // 3 stars
|
||||
} else if (POPM_rating < 0xFF) {
|
||||
return 0.80; // 4 stars
|
||||
}
|
||||
return 1.0; // 5 stars
|
||||
}
|
||||
|
||||
int TagReader::ConvertToPOPMRating(const float rating) {
|
||||
if (rating < 0.20) {
|
||||
return 0x00;
|
||||
} else if (rating < 0.40) {
|
||||
return 0x01;
|
||||
} else if (rating < 0.60) {
|
||||
return 0x80;
|
||||
} else if (rating < 0.80) {
|
||||
return 0xC0;
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
|
@ -93,6 +93,11 @@ class TagReader {
|
||||
TagLib::ID3v2::Tag* tag) const;
|
||||
|
||||
private:
|
||||
// Returns a float in [0.0..1.0] corresponding to the rating range we use in Clementine
|
||||
static float ConvertPOPMRating(const int POPM_rating);
|
||||
// Reciprocal
|
||||
static int ConvertToPOPMRating(const float rating);
|
||||
|
||||
FileRefFactory* factory_;
|
||||
QNetworkAccessManager* network_;
|
||||
|
||||
|
BIN
tests/data/fmpspopmrating.mp3
Normal file
BIN
tests/data/fmpspopmrating.mp3
Normal file
Binary file not shown.
BIN
tests/data/popmrating.mp3
Normal file
BIN
tests/data/popmrating.mp3
Normal file
Binary file not shown.
@ -11,6 +11,7 @@
|
||||
<file>fmpsplaycount.mp3</file>
|
||||
<file>fmpsplaycountboth.mp3</file>
|
||||
<file>fmpsplaycountuser.mp3</file>
|
||||
<file>fmpspopmrating.mp3</file>
|
||||
<file>fmpsrating.mp3</file>
|
||||
<file>fmpsratingboth.mp3</file>
|
||||
<file>fmpsratinguser.mp3</file>
|
||||
@ -18,6 +19,7 @@
|
||||
<file>manyfiles.cue</file>
|
||||
<file>manyfilesbroken.cue</file>
|
||||
<file>onesong.cue</file>
|
||||
<file>popmrating.mp3</file>
|
||||
<file>pls_one.pls</file>
|
||||
<file>pls_somafm.pls</file>
|
||||
<file>secretagent.asx</file>
|
||||
|
@ -146,4 +146,18 @@ TEST_F(SongTest, FMPSPlayCountBoth) {
|
||||
EXPECT_EQ(123, song.playcount());
|
||||
}
|
||||
|
||||
TEST_F(SongTest, POPMRating) {
|
||||
TemporaryResource r(":/testdata/popmrating.mp3");
|
||||
Song song = ReadSongFromFile(r.fileName());
|
||||
EXPECT_FLOAT_EQ(0.60, song.rating());
|
||||
}
|
||||
|
||||
TEST_F(SongTest, BothFMPSPOPMRating) {
|
||||
// fmpspopmrating.mp3 contains FMPS with rating 0.42 and POPM with 0x80
|
||||
// (corresponds to 0.60 rating for us): check that FMPS tag has precedence
|
||||
TemporaryResource r(":/testdata/fmpspopmrating.mp3");
|
||||
Song song = ReadSongFromFile(r.fileName());
|
||||
EXPECT_FLOAT_EQ(0.42, song.rating());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
Loading…
x
Reference in New Issue
Block a user