Creating an ID3 Tags Reader with PHP

Creating an ID3 Tags Reader with PHP

33 246
ID3 Tags Reader with PHP

ID3 Tags Reader with PHP

Today we will work with PHP again. Today’s tutorial will tell you about how you can use PHP to obtain ID3 meta info from music files (mp3). The easiest way to save id3 info – winamp software (try to find it in old archives). So you will able to test our result script with your music files. We will extract next fields: Title, Album, Author, Discription, Genre, Publisher, OriginalArtist, URL etc.

Live Demo

Now – download the example files and lets start coding !


Step 1. HTML

As usual, we start with the HTML. This is source of demo page layout:

main_page.html

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>ID3 Tags Reader with PHP | Script-tutorials</title>
    <link rel="stylesheet" href="css/main.css" type="text/css" media="all" />
</head>
<body>
    <div class="example" id="main">
        <table style="width:100%">
            <thead>
                <td><b>Title</b></td>
                <td><b>Album</b></td>
                <td><b>Author</b></td>
                <td><b>AlbumAuthor</b></td>
                <td><b>Track</b></td>
                <td><b>Year</b></td>
                <td><b>Lenght</b></td>
                <td><b>Lyric</b></td>
                <td><b>Desc</b></td>
                <td><b>Genre</b></td>
            </thead>
            __list__
        </table>

        <hr /><h3>Extra info</h3>
        <table style="width:100%">
            <thead>
                <td><b>Title</b></td>
                <td><b>Encoded</b></td>
                <td><b>Copyright</b></td>
                <td><b>Publisher</b></td>
                <td><b>OriginalArtist</b></td>
                <td><b>URL</b></td>
                <td><b>Comments</b></td>
                <td><b>Composer</b></td>
            </thead>
            __list2__
        </table>

    </div>
</body>
</html>

I prepared 2 tables to display ID3 infos of each enumerated file

Step 2. CSS

Here are used CSS styles:

css/main.css

body{background-color:#ddd;margin:0;padding:0}
.example{position:relative;background-color:#fff;width:980px;height:700px;border:1px #000 solid;margin:20px auto;padding:20px;-moz-border-radius:3px;-webkit-border-radius:3px}

Step 3. PHP

Our most important part of project – PHP.

index.php

<?php

// set error reporting level
if (version_compare(phpversion(), '5.3.0', '>=') == 1)
  error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED);
else
  error_reporting(E_ALL & ~E_NOTICE);


// gathering all mp3 files in 'mp3' folder into array
$sDir = 'mp3/';
$aFiles = array();
$rDir = opendir($sDir);
if ($rDir) {
    while ($sFile = readdir($rDir)) {
        if ($sFile == '.' or $sFile == '..' or !is_file($sDir . $sFile))
            continue;

        $aPathInfo = pathinfo($sFile);
        $sExt = strtolower($aPathInfo['extension']);
        if ($sExt == 'mp3') {
            $aFiles[] = $sDir . $sFile;
        }
    }
    closedir( $rDir );
}

// new object of our ID3TagsReader class
$oReader = new ID3TagsReader();

// passing through located files ..
$sList = $sList2 = '';
foreach ($aFiles as $sSingleFile) {
    $aTags = $oReader->getTagsInfo($sSingleFile); // obtaining ID3 tags info
    $sList .= '<tr><td>'.$aTags['Title'].'</td><td>'.$aTags['Album'].'</td><td>'.$aTags['Author'].'</td>
                    <td>'.$aTags['AlbumAuthor'].'</td><td>'.$aTags['Track'].'</td><td>'.$aTags['Year'].'</td>
                    <td>'.$aTags['Lenght'].'</td><td>'.$aTags['Lyric'].'</td><td>'.$aTags['Desc'].'</td>
                    <td>'.$aTags['Genre'].'</td></tr>';

    $sList2 .= '<tr><td>'.$aTags['Title'].'</td><td>'.$aTags['Encoded'].'</td><td>'.$aTags['Copyright'].'</td>
                    <td>'.$aTags['Publisher'].'</td><td>'.$aTags['OriginalArtist'].'</td><td>'.$aTags['URL'].'</td>
                    <td>'.$aTags['Comments'].'</td><td>'.$aTags['Composer'].'</td></tr>';
}

// main output
echo strtr(file_get_contents('main_page.html'), array('__list__' => $sList, '__list2__' => $sList2));


// class ID3TagsReader
class ID3TagsReader {

    // variables
    var $aTV23 = array( // array of possible sys tags (for last version of ID3)
        'TIT2',
        'TALB',
        'TPE1',
        'TPE2',
        'TRCK',
        'TYER',
        'TLEN',
        'USLT',
        'TPOS',
        'TCON',
        'TENC',
        'TCOP',
        'TPUB',
        'TOPE',
        'WXXX',
        'COMM',
        'TCOM'
    );
    var $aTV23t = array( // array of titles for sys tags
        'Title',
        'Album',
        'Author',
        'AlbumAuthor',
        'Track',
        'Year',
        'Lenght',
        'Lyric',
        'Desc',
        'Genre',
        'Encoded',
        'Copyright',
        'Publisher',
        'OriginalArtist',
        'URL',
        'Comments',
        'Composer'
    );
    var $aTV22 = array( // array of possible sys tags (for old version of ID3)
        'TT2',
        'TAL',
        'TP1',
        'TRK',
        'TYE',
        'TLE',
        'ULT'
    );
    var $aTV22t = array( // array of titles for sys tags
        'Title',
        'Album',
        'Author',
        'Track',
        'Year',
        'Lenght',
        'Lyric'
    );

    // constructor
    function ID3TagsReader() {}

    // functions
    function getTagsInfo($sFilepath) {
        // read source file
        $iFSize = filesize($sFilepath);
        $vFD = fopen($sFilepath,'r');
        $sSrc = fread($vFD,$iFSize);
        fclose($vFD);

        // obtain base info
        if (substr($sSrc,0,3) == 'ID3') {
            $aInfo['FileName'] = $sFilepath;
            $aInfo['Version'] = hexdec(bin2hex(substr($sSrc,3,1))).'.'.hexdec(bin2hex(substr($sSrc,4,1)));
        }

        // passing through possible tags of idv2 (v3 and v4)
        if ($aInfo['Version'] == '4.0' || $aInfo['Version'] == '3.0') {
            for ($i = 0; $i < count($this->aTV23); $i++) {
                if (strpos($sSrc, $this->aTV23[$i].chr(0)) != FALSE) {

                    $s = '';
                    $iPos = strpos($sSrc, $this->aTV23[$i].chr(0));
                    $iLen = hexdec(bin2hex(substr($sSrc,($iPos + 5),3)));

                    $data = substr($sSrc, $iPos, 9 + $iLen);
                    for ($a = 0; $a < strlen($data); $a++) {
                        $char = substr($data, $a, 1);
                        if ($char >= ' ' && $char <= '~')
                            $s .= $char;
                    }
                    if (substr($s, 0, 4) == $this->aTV23[$i]) {
                        $iSL = 4;
                        if ($this->aTV23[$i] == 'USLT') {
                            $iSL = 7;
                        } elseif ($this->aTV23[$i] == 'TALB') {
                            $iSL = 5;
                        } elseif ($this->aTV23[$i] == 'TENC') {
                            $iSL = 6;
                        }
                        $aInfo[$this->aTV23t[$i]] = substr($s, $iSL);
                    }
                }
            }
        }

        // passing through possible tags of idv2 (v2)
        if($aInfo['Version'] == '2.0') {
            for ($i = 0; $i < count($this->aTV22); $i++) {
                if (strpos($sSrc, $this->aTV22[$i].chr(0)) != FALSE) {

                    $s = '';
                    $iPos = strpos($sSrc, $this->aTV22[$i].chr(0));
                    $iLen = hexdec(bin2hex(substr($sSrc,($iPos + 3),3)));

                    $data = substr($sSrc, $iPos, 6 + $iLen);
                    for ($a = 0; $a < strlen($data); $a++) {
                        $char = substr($data, $a, 1);
                        if ($char >= ' ' && $char <= '~')
                            $s .= $char;
                    }

                    if (substr($s, 0, 3) == $this->aTV22[$i]) {
                        $iSL = 3;
                        if ($this->aTV22[$i] == 'ULT') {
                            $iSL = 6;
                        }
                        $aInfo[$this->aTV22t[$i]] = substr($s, $iSL);
                    }
                }
            }
        }
        return $aInfo;
    }
}

?>

After put your mp3 files near these code files thats all. Yes, there are a lot of code, but everything is close. Commonly – you can check code in beginning (before defining of class), and class itself. First half is very easy – passing through files of folder, and generating output. So about class itself (ID3TagsReader) – this is more difficult. We will read hex code of each MP3 file and looking for inner information (ID3). I will try to pick all ID3 tags which can be available in Winamp (mp3 player). But I can say that the total of all possible tags is much more.


Live Demo

Conclusion

I hope today’s article was very interesting for you. This material can be of great interest for projects that work with the music. This will help you get a variety of information from music files without including another large libraries. Good luck in your work!


33 COMMENTS

  1. Hello there, while trying to implement your ID3CLASSREADER class I came upon some things I wanted to share with you so that you can maybe help me out. So first thing I love using ezsql when working with php + sql, however it seems that your class turns ezsql useless lol which is nothing really serious but just so you know. The second thing I came across is the fact that a lot of mp3′s get they’re titles or albums chopped up by exactly one letter at the end, or at the begining, and sometimes both! Also some characters like “(” or “/” appear sometimes in the titles, artists or album titles. I was trying to use your class to get all the id3 of 1000 mp3′s inside a folder while retrieving them to a sql database. Just by “echo”ing it all I came up with those small bugs.

    I’ve been using other scripts but none was able to read as much id3 as yours, without those small bugs it would render this script perfect for me.

    Could you help me out?

    John More

    • Hello John,
      Yes, I know, I noticed this too, but did not had much time to investigate it and fix. Possible I will do it in next time when will back to work with MP3 files and its tags

    • Hi,

      Great script :)

      The sample test mp3′s I’ve been using all have last characters missing from Title, Author, Year, Genre & comments.

      Is this a bug? How do you suggest I fix this?

      Regards

      Stephen

    • Hello sam,

      Why now? .. But in this case you need to accept (download) this file locally to work with this file

  2. Thank u..

    yoyr script is working correctly.

    but i also want to fetch the thumbnail of that mp3 file..

    can u help me in this…?

      • How can I get the info from a .m4v video file? I tried to change the Ext == ‘m4v’ on line 21 of index.php however when I do that all I get is a blank page…

      • Hi Morries,
        M4V format is not the ‘mp3′ format. This format doesn’t contain ID3 Tags (but it can contain another information)

  3. Hey Admin,
    This is what I have been looking for. Though I need more. Namely, make url clickable, reading ID3 info from icecast stream. Do you have ideas or could you direct me to the right place? Thank you.

    • Hello kosta,
      It is pretty easy to make links clickable, you just need to add <a href="" element here. And, I think that I know nothing about icecast stream. Does it steam mp3?

  4. Hello! I don’t know if anyone is still around for this but I’ll try anyway. I am really liking this code. I want to use it to organize a batch of mp3s that have somehow gathered in my webspace. I have created a header page for the “jukebox” then kick off a loading screen in an iframe just to let folks know something is going on while they wait which then kicks of the code I downloaded here as “example128.” First thing I guess is the wait… does this code load the whole mp3 file or just the beginning of the file with the ID3 tag? The next “problem” I had was that it SEEMS to work best with ID3v2.3. I have been playing with this for a week or so now and as far as I can recall it was missing some output fields with ID3v2.4 (so I just converted all tags to ID3v2.3). It sometimes appends extra bytes at the front of fields sometimes (looks like the field character counter, truncating an equal number off of the end). The thing I can’t figure out at all is sometimes bytes are missing on both ends of a field. I would LOVE to make this thing work but am just now starting to learn how to write a little PHP code with no previous knowledge before looking at your tutorials. Any ideas or pointers? The website so far lives at http://clubbogart.com/CaptainJukebox/
    Thanks,
    Chris

    • Hello Chris,
      1) It loads whole file completely. But yes, you are right, actually this is to no purpose. .. You can set a limit of .. 2048 symbols (as example) for ‘fread’ function here.
      2) And yes, depending on version of ID3 we can get a bit different results. Sometimes with extra bytes. I think, that in order to cleanup (to get clean results) – we have to learn ID3 structure deeper, I think that there is some new separator which we didn’t use before.

  5. Nice tutorial and wonderful code.

    I have a small issue. I want to be really fliexible. Can this work with only one mp3 file. Maybe just specify the location and file name in a variable, and not the total contents in a directory? Thanks.

    • Hello Don,
      Of course it is possible, all you need is to put your full file path into $aFiles array, and then, you can comment all code between lines 12 and 25

    • I think that this is possible to read all ID3 data from mp3 file (in case if it exists in the file). However, if you need to get it, you will need scrutinize the file to understand how it stores these images.

  6. ia m getting these errors for the above code

    1)Warning: opendir(mp3/) [function.opendir]: failed to open dir: No such file or directory in C:\wamp\www\project\index.php on line 12

    2)Warning: file_get_contents(main_page.html) [function.file-get-contents]: failed to open stream: No such file or directory in C:\wamp\www\project\index.php on line 45

    can anyone help me

    • Hi Allu,
      Make sure that there is the ‘mp3′ folder in your ‘C:\wamp\www\project\’ directory (with mp3 files inside)

  7. Hi Andrew,
    Thanks for the script , i am looking for these library but have you noticed , your script doesn’t give the full word of Title or author or many of the tags of mp3 file.please give me the solution for this.

    Thanks

  8. There’s an error on line 134

    $data = substr($sSrc, $iPos, 9 + $iLen);

    should be

    $data = substr($sSrc, $iPos, 10 + $iLen);

  9. I have used your ID3 class but can you let me know is your class able to calculate Beats Per Minute of a song or video? if yes then how.

    • Hi Neeraj,
      I think that this is possible, I investigated it a bit, this new param (beats per minute) is one of new params of ID3v2, the beats value is located in TBPM, thus, you can use it with adding it into $aTV23 array.

  10. hi . i want to Edit ID3v2 Mp3 Tags
    I can Edit text Tags
    title, Artist , copyright ….
    But I can’t Edit Pictures on Mp3 files
    Cover(front) and more Image tags

    I want to
    example.png + example.mp3 = Final.mp3
    if you Find my Answer please Send An Email to Me ;)
    Thanks

    • Hi mahmoud,
      I can not provide you with an exact code, but the image can be placed accordingly the following:
      [comments] => Array
      (
         [picture] => Array
            (
               [0] => Array
                  (
                     [data] => <binary data>
                     [image_mime] => image/jpeg
                  )
            )
      )

  11. I have a really complicated question. The above script works perfectly for me. Now, what I want to do is auto-execute the php file. This is simply done by refreshing the page every now and then.

    The complicated part is I want the results to be posted into a mysql database…and ensure that duplicate entries are not made. So just new files found would be written into the database. Any idea?:)

    • Hi Phillip,
      What is the complexity for you, in order not to duplicate record?
      The easiest way is to rely on file names (because they should be unique)

  12. Hey, Your script is working perfectly fine. I just need a little bit help with your above code.
    I was able to fetch all the above info with your code. But I need to insert the above data into my database like author info in the author column, filname in the filename column

    • Hello Yog,
      You can consider our DB class that I already presented here many times, you will need just to link it, and it will take only few lines of code to save any data into database)

Leave a Reply