How to extract an image (album cover) from a file in mp3 format
Generally any digital file type has a signature or a certain structure that identifies it.
This signature is always valid even when contained in another format including an image within another file type, in this case within an MP3 file.
Id3v2
These metadata, when present, may contain an image, in JPEG or PNG format, indexed to a frame designated as APIC. When this is filled with an image there is a field in this frame that indicates its literal mime type as image/jpeg or image/png. The image types for Id3v2 are described in https://mutagen-specs.readthedocs.io/en/latest/id3/id3v2.4.0-frames.html#apic .
Id3v1 does not support any type of image and other images may exist outside of Id3v2 context and all these metadata structures may coexist, or not, simultaneously or be completely absent. In fact ID3 is an extra format eventually added to the beginning or end of the audio data not being part of the MP3 specification.
There are several Id3v2 metadata editors for MP3 format that add all the necessary information including the album cover, for example https://www.mp3tag.de/en/ .
In practice you do not need to worry about the APIC identifier or the 'mime type' contained in it since, for example, Id3v2 does not have fixed offsets only maximum limits, just to get to the point and extract the image (if there is) by his signature.
The code is valid for MP3, WMA, FLAC and M4*/Mp4 audio formats.
let reader = new FileReader(), file;
reader.onloadend = () => {
player.src = URL.createObjectURL( file );
let picturesindex = [];
let buffer = reader.result;
void new Uint8Array( buffer ).forEach( ( v, i, a ) =>
( a[i] == 0xFF || a[i] == 0x89 ) &&
( a[i+1] == 0xD8 || a[i+1] == 0x50 ) &&
( a[i+2] == 0xFF || a[i+2] == 0x4E ) &&
( a[i+3] == 0xE0 || a[i+3] == 0xEE || a[i+3] == 0xE1 || a[i+3] == 0x47 ) &&
picturesindex.push( i )
);
let picturescollection = picturesindex.map( ( v ) =>
URL.createObjectURL( new Blob( [ buffer.slice( v ) ] ) )
);
let frontcover = picturescollection[0]? picturescollection.shift( ) : '';
player.poster = frontcover;
player.play();
};
input.onchange = () =>
input.files[0] && reader.readAsArrayBuffer( file = input.files[0] );
<video id="player" width="320" height="320" controls></video><br>
<input id="input" type="file" accept="audio/mpeg, audio/mp4, audio/flac, .mp4" />
Hex signatures and end of signature for jpg and png image formats:
ID3 seen on Notepad:
Or in a simplified way:
let reader = new FileReader(), file;
reader.onloadend = () => {
player.src = URL.createObjectURL( file );
let picture = reader.result.match(/\x89PNG[\x00-\xFF]+IEND|\xFF\xD8\xFF(?:\xE0|\xEE|\xE1|\xDB)[\x00-\xFF]+\xFF[\xD8-\xD9]{1}/);
player.poster = picture? 'data:;base64,' + btoa( picture ) : '';
player.play();
}
input.onchange = () =>
input.files[0] && reader.readAsBinaryString( file = input.files[0] );
<video id="player" width="320" height="320" controls></video><br>
<input id="input" type="file" accept="audio/mpeg, audio/mp4, audio/flac, .mp4" />
One hell of an answer to an unfortunate question :/ .
– Anderson