├── .gitignore
├── Tests
├── TestFiles
│ ├── thumb.jpeg
│ └── Exodus - 06 - Piranha.mp3
├── bootstrap.php
└── Id3TagsReaderTest.php
├── composer.json
├── phpunit.xml.dist
├── LICENSE
├── README.md
└── PhpId3
├── Id3TagsReader.php
├── BinaryFileReader.php
└── Id3Tags.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | /nbproject/private/
--------------------------------------------------------------------------------
/Tests/TestFiles/thumb.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubhamjain/PHP-ID3/HEAD/Tests/TestFiles/thumb.jpeg
--------------------------------------------------------------------------------
/Tests/TestFiles/Exodus - 06 - Piranha.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shubhamjain/PHP-ID3/HEAD/Tests/TestFiles/Exodus - 06 - Piranha.mp3
--------------------------------------------------------------------------------
/Tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | =5.3.3"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ./Tests
6 |
7 |
8 |
9 |
10 | ./
11 |
12 | ./Resources
13 | ./Tests
14 | ./vendor
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Shubham Jain
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Tests/Id3TagsReaderTest.php:
--------------------------------------------------------------------------------
1 | id3 = new Id3TagsReader(fopen(__DIR__ . $this->mp3File, "rb"));
18 | $this->id3->readAllTags();
19 | }
20 |
21 | public function testGetImage()
22 | {
23 | $image = $this->id3->getImage();
24 |
25 | $this->assertEquals("mage/jpg", $image[0]);
26 | $this->assertEquals(file_get_contents(__DIR__ . $this->albumCover), $image[1]);
27 | }
28 |
29 | public function testGetId3TagsArray()
30 | {
31 | $id3Tags = $this->id3->getId3Array();
32 |
33 | $this->assertEquals($id3Tags["TIT2"]["body"], 'Piranha');
34 | $this->assertEquals($id3Tags["TRCK"]["body"], '6');
35 | $this->assertEquals($id3Tags["TCON"]["body"], 'Heavy Metal');
36 | $this->assertEquals($id3Tags["TALB"]["body"], 'Bonded by Blood');
37 | $this->assertEquals($id3Tags["TYER"]["body"], '1985');
38 | $this->assertEquals($id3Tags["TPE1"]["body"], 'Exodus');
39 | $this->assertEquals($id3Tags["TLEN"]["body"], '228963');
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #PHP-ID3
2 |
3 | PHP-ID3 makes use of native PHP to read [ID3 Tags](http://en.wikipedia.org/wiki/ID3) and thumbnail from a MP3 file. There have been many revisions to ID3 Tags specification; this program makes use of v3.2 of the [spec](http://id3.org/id3v2.3.0).
4 |
5 | To read binary data more effectively, I have created a sclass, [BinaryFileReader](https://gist.github.com/shubhamjain/5964350), which reads data in named chunks.
6 |
7 | ##How to Install
8 |
9 | Into your composer.json
10 |
11 | ```json
12 | {
13 | "require" : {
14 | "shubhamjain/php-id3": "dev-master"
15 | }
16 | }
17 | ```
18 |
19 | ##How to Use
20 |
21 | You will first need to include the autoload.php generated by composer and then you can use the classes in PhpId3 namespace.
22 |
23 | ```php
24 | readAllTags(); //Calling this is necesarry before others
35 |
36 | foreach($id3->getId3Array() as $key => $value) {
37 | if( $key !== "APIC" ) { //Skip Image data
38 | echo $value["FullTagName"] . ": " . $value["Body"] . "
";
39 | }
40 | }
41 |
42 | list($mimeType, $image) = $id3->getImage();
43 |
44 | file_put_contents("thumb.jpeg", $image ); //Note the image type depends upon MimeType
45 |
46 | //...
47 | ```
48 |
49 | ##LICENSE
50 |
51 | See ``LICENSE`` for more informations
52 |
53 | ##Feedback
54 |
55 | If you used this project or liked it or have any doubt about the source, send your valuable thoughts at .
56 |
--------------------------------------------------------------------------------
/PhpId3/Id3TagsReader.php:
--------------------------------------------------------------------------------
1 |
12 | * @license MIT License
13 | */
14 | class Id3TagsReader
15 | {
16 |
17 | private $fileReader;
18 | private $id3Array;
19 | private $validMp3 = TRUE;
20 |
21 | public function __construct($fileHandle)
22 | {
23 | $this->fileReader = new BinaryFileReader($fileHandle, array(
24 | "id3" => array(BinaryFileReader::FIXED, 3),
25 | "version" => array(BinaryFileReader::FIXED, 2),
26 | "flag" => array(BinaryFileReader::FIXED, 1),
27 | "sizeTag" => array(BinaryFileReader::FIXED, 4, BinaryFileReader::INT),
28 | ));
29 |
30 | $data = $this->fileReader->read();
31 |
32 | if( $data->id3 !== "ID3")
33 | {
34 | throw new \Exception("The MP3 file contains no valid ID3 Tags.");
35 | $this->validMp3 = FALSE;
36 | }
37 |
38 | }
39 |
40 | public function readAllTags()
41 | {
42 | assert( $this->validMp3 === TRUE);
43 |
44 | $bytesPos = 10; //From headers
45 |
46 | $this->fileReader->setMap(array(
47 | "frameId" => array(BinaryFileReader::FIXED, 4),
48 | "size" => array(BinaryFileReader::FIXED, 4, BinaryFileReader::INT),
49 | "flag" => array(BinaryFileReader::FIXED, 2),
50 | "body" => array(BinaryFileReader::SIZE_OF, "size"),
51 | ));
52 |
53 | $id3Tags = Id3Tags::getId3Tags();
54 |
55 | while (($file_data = $this->fileReader->read())) {
56 |
57 | if (!in_array($file_data->frameId, array_keys($id3Tags))) {
58 | break;
59 | }
60 |
61 | $body = $file_data->body;
62 |
63 | // If frame is a text frame then we have to consider
64 | // encoding as shown in spec section 4.2
65 | if( $file_data->frameId[0] === "T" )
66 | {
67 | // First character determines the encoding, 1 = ISO-8859-1, 0 = UTF - 16
68 | if( intval(bin2hex($body[0]), 16) === 1)
69 | $body = mb_convert_encoding(substr($body, 1), 'UTF-8', 'UTF-16'); // Convert UTF-16 to UTF-8 to compatible with current browsers
70 | }
71 |
72 | $this->id3Array[$file_data->frameId] = array(
73 | "fullTagName" => $id3Tags[$file_data->frameId],
74 | "position" => $bytesPos,
75 | "size" => $file_data->size,
76 | "body" => $body,
77 | );
78 |
79 | $bytesPos += 4 + 4 + 2 + $file_data->size;
80 | }
81 | return $this;
82 | }
83 |
84 | public function getId3Array()
85 | {
86 | return $this->id3Array;
87 | }
88 |
89 | public function getImage()
90 | {
91 | $fp = fopen('data://text/plain;base64,' . base64_encode($this->id3Array["APIC"]["body"]), 'rb'); //Create an artificial stream from Image data
92 |
93 | $fileReader = new BinaryFileReader($fp, array(
94 | "textEncoding" => array(BinaryFileReader::FIXED, 1),
95 | "mimeType" => array(BinaryFileReader::NULL_TERMINATED),
96 | "fileName" => array(BinaryFileReader::NULL_TERMINATED),
97 | "contentDesc" => array(BinaryFileReader::NULL_TERMINATED),
98 | "binaryData" => array(BinaryFileReader::EOF_TERMINATED)
99 | )
100 | );
101 |
102 | $imageData = $fileReader->read();
103 |
104 | return array($imageData->mimeType, $imageData->binaryData);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/PhpId3/BinaryFileReader.php:
--------------------------------------------------------------------------------
1 |
11 | * @example https://github.com/shubhamjain/PHP-ID3
12 | * @license MIT License
13 | */
14 | class BinaryFileReader
15 | {
16 | /**
17 | * size of block depends upon the variable defined in the next array element.
18 | */
19 | const SIZE_OF = 1;
20 |
21 | /**
22 | * Block is read until NULL is encountered.
23 | */
24 | const NULL_TERMINATED = 2;
25 |
26 | /**
27 | * Block is read until EOF is encountered.
28 | */
29 | const EOF_TERMINATED = 3;
30 |
31 | /**
32 | * Block size is fixed.
33 | */
34 | const FIXED = 4;
35 |
36 | /**
37 | * Datatypes to transform the read block
38 | */
39 | const INT = 5;
40 |
41 | const FLOAT = 6;
42 |
43 | /**
44 | * file handle to read data
45 | */
46 | private $fp;
47 |
48 | /**
49 | * Associative array of Varaibles and their info ( TYPE, SIZE, DATA_TYPE)
50 | * In special cases it can be an array to handle different types of block data lengths
51 | */
52 | private $map;
53 |
54 | public function __construct($fp, array $map)
55 | {
56 | $this->fp = $fp;
57 | $this->setMap($map);
58 | }
59 |
60 | public function setMap($map)
61 | {
62 | $this->map = $map;
63 |
64 | foreach ($this->map as $key => $size) {
65 | //Create property from keys of $map
66 | $this->$key = null;
67 | }
68 | }
69 |
70 | public function read()
71 | {
72 | if (feof($this->fp)) {
73 | return false;
74 | }
75 |
76 | foreach ($this->map as $key => $info) {
77 |
78 | $this->fillTag($info, $key);
79 |
80 | if (isset($info[2])) {
81 | $this->convertBinToNumeric($info[2], $key);
82 | }
83 | $this->$key = ltrim($this->$key, "\0x");
84 | }
85 | return $this;
86 | }
87 |
88 | private function nullTeminated($key)
89 | {
90 | while ((int) bin2hex(($ch = fgetc($this->fp))) !== 0) {
91 | $this->$key .= $ch;
92 | }
93 | }
94 |
95 | private function eofTerminated($key)
96 | {
97 | while (!feof($this->fp)) {
98 | $this->$key .= fgetc($this->fp);
99 | }
100 | }
101 |
102 | private function fillTag($tag, $key)
103 | {
104 | switch ($tag[0]) {
105 | case self::NULL_TERMINATED:
106 | $this->nullTeminated($key);
107 | break;
108 | case self::EOF_TERMINATED:
109 | $this->eofTerminated($key);
110 | break;
111 | case self::SIZE_OF:
112 | //If the variable is not an integer return false
113 | if (!( $tag[1] = $this->$tag[1] )) {
114 | return false;
115 | }
116 | default:
117 | //Read as string
118 | $this->$key = fread($this->fp, $tag[1]);
119 | break;
120 | }
121 | }
122 |
123 | private function convertBinToNumeric($value, $key)
124 | {
125 | switch ($value) {
126 | case self::INT:
127 | $this->$key = intval(bin2hex($this->$key), 16);
128 | break;
129 | case self::FLOAT:
130 | $this->$key = floatval(bin2hex($this->$key), 16);
131 | break;
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/PhpId3/Id3Tags.php:
--------------------------------------------------------------------------------
1 | "Audio encryption",
11 | "APIC" => "Attached picture",
12 | "COMM" => "Comments",
13 | "COMR" => "Commercial frame",
14 | "ENCR" => "Encryption method registration",
15 | "EQUA" => "Equalization",
16 | "ETCO" => "Event timing codes",
17 | "GEOB" => "General encapsulated object",
18 | "GRID" => "Group identification registration",
19 | "IPLS" => "Involved people list",
20 | "LINK" => "Linked information",
21 | "MCDI" => "Music CD identifier",
22 | "MLLT" => "MPEG location lookup table",
23 | "OWNE" => "Ownership frame",
24 | "PRIV" => "Private frame",
25 | "PCNT" => "Play counter",
26 | "POPM" => "Popularimeter",
27 | "POSS" => "Position synchronisation frame",
28 | "RBUF" => "Recommended buffer size",
29 | "RVAD" => "Relative volume adjustment",
30 | "RVRB" => "Reverb",
31 | "SYLT" => "Synchronized lyric/text",
32 | "SYTC" => "Synchronized tempo codes",
33 | "TALB" => "Album/Movie/Show title",
34 | "TBPM" => "BPM (beats per minute)",
35 | "TCOM" => "Composer",
36 | "TCON" => "Content type",
37 | "TCOP" => "Copyright message",
38 | "TDAT" => "Date",
39 | "TDLY" => "Playlist delay",
40 | "TENC" => "Encoded by",
41 | "TEXT" => "Lyricist/Text writer",
42 | "TFLT" => "File type",
43 | "TIME" => "Time",
44 | "TIT1" => "Content group description",
45 | "TIT2" => "Title/songname/content description",
46 | "TIT3" => "Subtitle/Description refinement",
47 | "TKEY" => "Initial key",
48 | "TLAN" => "Language(s)",
49 | "TLEN" => "Length",
50 | "TMED" => "Media type",
51 | "TOAL" => "Original album/movie/show title",
52 | "TOFN" => "Original filename",
53 | "TOLY" => "Original lyricist(s)/text writer(s)",
54 | "TOPE" => "Original artist(s)/performer(s)",
55 | "TORY" => "Original release year",
56 | "TOWN" => "File owner/licensee",
57 | "TPE1" => "Lead performer(s)/Soloist(s)",
58 | "TPE2" => "Band/orchestra/accompaniment",
59 | "TPE3" => "Conductor/performer refinement",
60 | "TPE4" => "Interpreted, remixed, or otherwise modified by",
61 | "TPOS" => "Part of a set",
62 | "TPUB" => "Publisher",
63 | "TRCK" => "Track number/Position in set",
64 | "TRDA" => "Recording dates",
65 | "TRSN" => "Internet radio station name",
66 | "TRSO" => "Internet radio station owner",
67 | "TSIZ" => "Size",
68 | "TSRC" => "ISRC (international standard recording code)",
69 | "TSSE" => "Software/Hardware and settings used for encoding",
70 | "TYER" => "Year",
71 | "TXXX" => "User defined text information frame",
72 | "UFID" => "Unique file identifier",
73 | "USER" => "Terms of use",
74 | "USLT" => "Unsychronized lyric/text transcription",
75 | "WCOM" => "Commercial information",
76 | "WCOP" => "Copyright/Legal information",
77 | "WOAF" => "Official audio file webpage",
78 | "WOAR" => "Official artist/performer webpage",
79 | "WOAS" => "Official audio source webpage",
80 | "WORS" => "Official internet radio station homepage",
81 | "WPAY" => "Payment",
82 | "WPUB" => "Publishers official webpage",
83 | "WXXX" => "User defined URL link frame",
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------