Serial-ATA / lofty-rs

Audio metadata library
Apache License 2.0
192 stars 36 forks source link

`mime_type()` returns PNG for JPG #465

Closed probablykasper closed 1 month ago

probablykasper commented 1 month ago

Reproducer

use lofty::file::TaggedFileExt;
use lofty::read_from_path;

fn main() {
    let path = "Omri - demons.mp3";
    let tagged_file = read_from_path(path).unwrap();

    let tag = tagged_file.primary_tag().unwrap();
    let picture = tag.pictures().get(0).unwrap();
    println!("{:?}", picture.mime_type());
}

Summary

I have an mp3 where the .mime_type() returns PNG, even though it's a JPG (Metadata viewer and Chromium both say it's JPEG).

Assets

example.zip

Use case

I'm implementing thumbnail caching. When decoding the image with the image crate using the format supplied from lofty, I get this error:

Format error decoding Png: Invalid PNG signature.
probablykasper commented 1 month ago

I tested 135 files, and ran into this for 7 of them. 4 .mp3 files and 3 .m4a files

Serial-ATA commented 1 month ago

Picture::mime_type() just returns whatever was specified in the MIME type field, in reality someone can write anything they want in there. For some reason, the cover in this case has image/png, but I can clearly see it's a jpeg in a hex editor.

You could pass Picture::data() through Picture::from_reader(), which will tell you for sure if it's a PNG or JPEG, since it reads the actual image data. The issue is it can only verify the MIME types from MimeType (which in reality probably cover any image you'll encounter, but still).

I don't think anything can really be done about this in Lofty.

probablykasper commented 1 month ago

Ah, that makes sense. I interpreted the documentation as it being determined from the image data

/// The mime_type is determined from the data, and /// is immutable.

The image crate can auto-guess the codec anyway, so I simply can rely on that

Serial-ATA commented 1 month ago

Oh wow. No idea why I worded it like that. Thanks for pointing that out.