Proper way to setup Popularimeter? #581
Replies: 1 comment 4 replies
-
|
So the docs about the POPM frames are actually outdated. They haven't been converted into The issue is some formats just use a single numeric star rating, others use an email + star rating, then there's ID3v2 which uses a star rating, email, and a play counter. I guess I could encode them in a special string format like So I'm imagining: // Rating scales are also different between formats, so
// they'll need to be unified in some way.
//
// Unfortunately, that *probably* means disallowing fractional ratings, since
// as far as I know, only some formats support them.
enum StarRating {
Zero,
One,
Two,
Three,
Four,
Five,
}
impl TryFrom<char> for StarRating {
type Error = ();
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'0' => Ok(Self::Zero),
'1' => Ok(Self::One),
'2' => Ok(Self::Two),
'3' => Ok(Self::Three),
'4' => Ok(Self::Four),
'5' => Ok(Self::Five),
_ => Err(()),
}
}
}
impl Display for StarRating {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(
f,
"{}",
match value {
Self::Zero => '0',
Self::One => '1',
Self::Two => '2',
Self::Three => '3',
Self::Four => '4',
Self::Five => '5',
}
)
}
}
// Generic popularimeter, almost identical to `PopularimeterFrame`, since
// it's the most feature packed implementation
struct Popularimeter {
email: Option<String>,
rating: StarRating,
/// NOTE: Only used in ID3v2, ...
counter: u64,
}
impl Display for Popularimeter {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
write!(f, "{}|{}|{}", self.email.unwrap_or(""), self.rating, self.counter)
}
}
impl Popularimeter {
// won't accept partial inputs, to encourage users to properly encode them
pub fn parse(input: &str) -> Option<Self> {
let mut parts = input.splitn(3, '|');
let Some(email) = parts.next() else {
return None;
};
let Some(rating) = parts
.next()
.and_then(|rating_str| rating_str.chars().next())
.and_then(|rating| StarRating::try_from(rating).ok())
else {
return None;
};
let Some(counter) = parts.next().and_then(|counter| counter.parse().ok()) else {
return None;
};
Some(Self {
email,
rating,
counter,
})
}
}ConversionSo if you read an ID3v2 tag with a POPM frame: PopularimeterFrame {
email: "[email protected]",
rating: 128,
counter: 50,
}It gets converted into a let popularimeter = Popularimeter {
email: "[email protected]",
rating: StarRating::Three,
counter: 50,
};Then into a string TagItem::new(ItemKey::Popularimeter, ItemValue::Text(popularimeter.to_string()));UsageThen the docs on impl Tag {
pub fn popularimeter(&self) -> Option<Popularimeter> {
self.get_string(ItemKey::Popularimeter).and_then(Popularimeter::parse)
}
}And then encourage insertion like: tag.insert_text(ItemKey::Popularimeter, Popularimeter {
email: "[email protected]",
rating: StarRating::Three,
counter: 50,
}.to_string());Rather than manual construction of the encoded string. Let me know if that flow makes sense to you. Sorry for the code dump, just jotting down the first thing that comes to mind. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
So for insertion I've done
and this W.I.P for reading
however, so far reading the
Popularimetergets me an error:Error: Popularimeter frame not found in the tag.even though the tag is there if I use external editors
popularimeter = ":10:0"So what is the correct way to get this working?
Beta Was this translation helpful? Give feedback.
All reactions