ID3 Reader
Lately I’ve been working on a PHP script that is able to parse an ID3 tag. It took some time, but I’ve successfully finished with version 1.0.
I spent some time reading the ID3 spec to figure out what exactly I was parsing.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<?php
function getID3 ( $file ) {
/**
* The main frames to focus on are TIT2, TALB, TPE2, TRCK
*/
$frameNames = array(
'TIT2' => 'Title',
'TALB' => 'Album',
'TPE2' => 'Artist',
'TRCK' => 'Track'
);
if ( file_exists( $file ) ) {
$id3 = array();
// Read the ID3 header
$fh = fopen( $file, 'r' );
$header = fread( $fh, 10 );
fclose( $fh );
$id3['header']['length'] = 10;
$id3['header']['str'] = $header;
/*
* Check to see if the file has an ID3 tag
* Check the first 3 bytes of the file to see if they
* are equal to 0x49 0x44 0x33 ('ID3')
*/
$id3['is_id3'] = (bool) preg_match( '/ID3/', substr( $id3['header']['str'], 0, 3 ) );
if ( $id3['is_id3'] ) {
// Find the version
$id3['header']['version'] = 'v2.' .
dechex( ord( $header{3} ) ) . '.' .
dechex( ord( $header{4} ) );
// Find flags
$id3['header']['header_flags'] = $header{5};
// Find tag length
$tagLength = 1;
for ( $i = 6; $i < 10; $i++ ) {
if ( $size = ord( $header{$i} ) ) {
$tagLength *= $size;
}
}
$id3['tag']['length'] = $tagLength;
// Get the ID3 tag string
$fh = fopen( $file, 'r' );
$id3['tag']['str'] = fread( $fh, $id3['tag']['length'] + 10 );
fclose( $fh );
$id3['tag']['str'] = substr( $id3['tag']['str'], 10, -1 );
$tagStr = $id3['tag']['str'];
// Search for the frames
foreach ( $frameNames as $pattern => $frame ) {
$strpos = strpos( $tagStr, $pattern );
$fHeader = substr( $tagStr, $strpos + 4, 4 );
$id3['tag']['frames'][$frame]['length'] = 0;
for ( $i = 0; $i < strlen( $fHeader ); $i++ ) {
$id3['tag']['frames'][$frame]['length'] += ord( $fHeader{$i} ) * pow( 256, strlen( $fHeader ) - 1 - $i ) . ' ';
}
$id3['tag']['frames'][$frame]['value'] = substr( $tagStr, $strpos + 10, $id3['tag']['frames'][$frame]['length'] );
}
}
print '<pre>' . print_r( $id3, 1 ) . '</pre>';
}
}
getID3( 'audioFiles/Jimmy Eat World/The Emo Diaries Chapter 1/Jimmy Eat World - 1 - Opener.mp3' ); |
This give the following output:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Array
(
[header] => Array
(
[length] => 10
[str] => ID3Z
[version] => v2.3.0
[header_flags] =>
)
[is_id3] => 1
[tag] => Array
(
[length] => 1080
[str] => TENC@WXXXTCOPTIT2OpenerTRCK1TYER1997PRIV'WM/MediaClassPrimaryID¼}`Ñ#ãâK†¡H¤*(DPRIV)WM/MediaClassSecondaryIDPRIVWM/WMContentID›¹1²äÿG‡z0žsÖPRIVzWM/UniqueFileIdentifierAMGa_id=R 315624;AMGp_id=VA;AMGt_id=T 3432684TPUB Deep ElmTCON(20)TALBThe Emo Diaries CompilationTPE2Jimmy Eat WorldPRIV"WM/WMCollectionID0è Ù>†¸L™Jºp¤‹jPRIV'WM/WMCollectionGroupID0è Ù>†¸L™Jºp¤‹jPRIVWM/ProviderAMGTPOS1/1PRIV!ZuneAlbumMediaID=Buۉʹ*93PRIV'ZuneAlbumArtistMediaIDªåۉʹ*93PRIV!ZuneCollectionIDæ%p
«qN®Â°a…dúgPRIVZuneMediaID=Bwۉʹ*93PRIVZuneUserEditedFieldsTOPETPE1Jimmy Eat WorldCOMM
[frames] => Array
(
[Title] => Array
(
[length] => 7
[value] => Opener
)
[Album] => Array
(
[length] => 28
[value] => The Emo Diaries Compilation
)
[Artist] => Array
(
[length] => 16
[value] => Jimmy Eat World
)
[Track] => Array
(
[length] => 2
[value] => 1
)
)
)
) |
It’s not exactly pretty or anything, but it is my first go at it. On to v1.1!
fantastic song you chose to parse.
what were you working on to parse id tags for?
I was working on a “social” online media player where people could upload their songs and then stream them using an HTML5/Flash player. Didn’t really pan out, since Spotify was released.