New library for extracting mp3 album art. The old one was hopelessly broken and unsupported. Requires composer install or update.

This commit is contained in:
Mike Macgirvin 2023-02-11 10:02:56 +11:00
parent bed9fb86b8
commit 0163fd23c9
15 changed files with 56 additions and 506 deletions

View file

@ -2,11 +2,7 @@
namespace Code\Thumbs;
require_once('library/php-id3/PhpId3/Id3TagsReader.php');
require_once('library/php-id3/PhpId3/BinaryFileReader.php');
require_once('library/php-id3/PhpId3/Id3Tags.php');
use PhpId3\Id3TagsReader;
use wapmorgan\Mp3Info\Mp3Info;
class Mp3audio
{
@ -19,19 +15,12 @@ class Mp3audio
public function Thumb($attach, $preview_style, $height = 300, $width = 300)
{
$fh = @fopen(dbunescbin($attach['content']), 'rb');
if ($fh === false) {
$audio = new Mp3Info($attach['content'], true);
if (! $audio->hasCover) {
return;
}
$id3 = new Id3TagsReader($fh);
$id3->readAllTags();
$image = $id3->getImage();
if (is_array($image)) {
$photo = $image[1];
}
fclose($fh);
$photo = $audio->getCover();
if ($photo) {
$image = imagecreatefromstring($photo);

View file

@ -39,7 +39,8 @@
"ext-json": "*",
"symfony/yaml": "^5.4",
"symfony/uid": "^5.4",
"symfony/mailer": "^5.4"
"symfony/mailer": "^5.4",
"wapmorgan/mp3info": "^0.0.8"
},
"require-dev" : {
"phpunit/phpunit" : "@stable",

51
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "a27d00681fe935f0f31de7359c0d24b4",
"content-hash": "47010e9f19209fb51bf8324c91a3da4b",
"packages": [
{
"name": "blueimp/jquery-file-upload",
@ -3465,6 +3465,55 @@
"source": "https://github.com/voku/stop-words/tree/master"
},
"time": "2018-11-23T01:37:27+00:00"
},
{
"name": "wapmorgan/mp3info",
"version": "0.0.8",
"source": {
"type": "git",
"url": "https://github.com/wapmorgan/Mp3Info.git",
"reference": "13d1c142a75ca5bf63dd29d706c9a93d8436dbcb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wapmorgan/Mp3Info/zipball/13d1c142a75ca5bf63dd29d706c9a93d8436dbcb",
"reference": "13d1c142a75ca5bf63dd29d706c9a93d8436dbcb",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.4.0"
},
"require-dev": {
"wapmorgan/terminal-info": "dev-master"
},
"bin": [
"bin/mp3scan"
],
"type": "library",
"autoload": {
"psr-4": {
"wapmorgan\\Mp3Info\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"description": "The fastest php library to extract mp3 tags & meta information.",
"keywords": [
"audio",
"id3",
"id3v1",
"id3v2",
"mp3",
"mpeg"
],
"support": {
"issues": "https://github.com/wapmorgan/Mp3Info/issues",
"source": "https://github.com/wapmorgan/Mp3Info/tree/0.0.8"
},
"time": "2022-06-15T23:39:26+00:00"
}
],
"packages-dev": [

View file

@ -1,2 +0,0 @@
/vendor/
/nbproject/private/

View file

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2013 Shubham Jain
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -1,134 +0,0 @@
<?php
namespace PhpId3;
/**
* A simple class to read variable byte length binary data.
* This is basically is a better replacement for unpack() function
* which creates a very large associative array.
*
* @author Shubham Jain <shubham.jain.1@gmail.com>
* @example https://github.com/shubhamjain/PHP-ID3
* @license MIT License
*/
class BinaryFileReader
{
/**
* size of block depends upon the variable defined in the next array element.
*/
const SIZE_OF = 1;
/**
* Block is read until NULL is encountered.
*/
const NULL_TERMINATED = 2;
/**
* Block is read until EOF is encountered.
*/
const EOF_TERMINATED = 3;
/**
* Block size is fixed.
*/
const FIXED = 4;
/**
* Datatypes to transform the read block
*/
const INT = 5;
const FLOAT = 6;
/**
* file handle to read data
*/
private $fp;
/**
* Associative array of Varaibles and their info ( TYPE, SIZE, DATA_TYPE)
* In special cases it can be an array to handle different types of block data lengths
*/
private $map;
public function __construct($fp, array $map)
{
$this->fp = $fp;
$this->setMap($map);
}
public function setMap($map)
{
$this->map = $map;
foreach ($this->map as $key => $size) {
//Create property from keys of $map
$this->$key = null;
}
}
public function read()
{
if (feof($this->fp)) {
return false;
}
foreach ($this->map as $key => $info) {
$this->fillTag($info, $key);
if (isset($info[2])) {
$this->convertBinToNumeric($info[2], $key);
}
$this->$key = ltrim($this->$key, "\0x");
}
return $this;
}
private function nullTeminated($key)
{
while ((int) bin2hex(($ch = fgetc($this->fp))) !== 0) {
$this->$key .= $ch;
}
}
private function eofTerminated($key)
{
while (!feof($this->fp)) {
$this->$key .= fgetc($this->fp);
}
}
private function fillTag($tag, $key)
{
switch ($tag[0]) {
case self::NULL_TERMINATED:
$this->nullTeminated($key);
break;
case self::EOF_TERMINATED:
$this->eofTerminated($key);
break;
case self::SIZE_OF:
//If the variable is not an integer return false
if (!( $tag[1] = $this->$tag[1] )) {
return false;
}
default:
//Read as string
$this->$key = fread($this->fp, $tag[1]);
break;
}
}
private function convertBinToNumeric($value, $key)
{
switch ($value) {
case self::INT:
$this->$key = intval(bin2hex($this->$key), 16);
break;
case self::FLOAT:
$this->$key = floatval(bin2hex($this->$key), 16);
break;
}
}
}

View file

@ -1,86 +0,0 @@
<?php
namespace PhpId3;
class Id3Tags
{
public static function getId3Tags()
{
return array(
"AENC" => "Audio encryption",
"APIC" => "Attached picture",
"COMM" => "Comments",
"COMR" => "Commercial frame",
"ENCR" => "Encryption method registration",
"EQUA" => "Equalization",
"ETCO" => "Event timing codes",
"GEOB" => "General encapsulated object",
"GRID" => "Group identification registration",
"IPLS" => "Involved people list",
"LINK" => "Linked information",
"MCDI" => "Music CD identifier",
"MLLT" => "MPEG location lookup table",
"OWNE" => "Ownership frame",
"PRIV" => "Private frame",
"PCNT" => "Play counter",
"POPM" => "Popularimeter",
"POSS" => "Position synchronisation frame",
"RBUF" => "Recommended buffer size",
"RVAD" => "Relative volume adjustment",
"RVRB" => "Reverb",
"SYLT" => "Synchronized lyric/text",
"SYTC" => "Synchronized tempo codes",
"TALB" => "Album/Movie/Show title",
"TBPM" => "BPM (beats per minute)",
"TCOM" => "Composer",
"TCON" => "Content type",
"TCOP" => "Copyright message",
"TDAT" => "Date",
"TDLY" => "Playlist delay",
"TENC" => "Encoded by",
"TEXT" => "Lyricist/Text writer",
"TFLT" => "File type",
"TIME" => "Time",
"TIT1" => "Content group description",
"TIT2" => "Title/songname/content description",
"TIT3" => "Subtitle/Description refinement",
"TKEY" => "Initial key",
"TLAN" => "Language(s)",
"TLEN" => "Length",
"TMED" => "Media type",
"TOAL" => "Original album/movie/show title",
"TOFN" => "Original filename",
"TOLY" => "Original lyricist(s)/text writer(s)",
"TOPE" => "Original artist(s)/performer(s)",
"TORY" => "Original release year",
"TOWN" => "File owner/licensee",
"TPE1" => "Lead performer(s)/Soloist(s)",
"TPE2" => "Band/orchestra/accompaniment",
"TPE3" => "Conductor/performer refinement",
"TPE4" => "Interpreted, remixed, or otherwise modified by",
"TPOS" => "Part of a set",
"TPUB" => "Publisher",
"TRCK" => "Track number/Position in set",
"TRDA" => "Recording dates",
"TRSN" => "Internet radio station name",
"TRSO" => "Internet radio station owner",
"TSIZ" => "Size",
"TSRC" => "ISRC (international standard recording code)",
"TSSE" => "Software/Hardware and settings used for encoding",
"TYER" => "Year",
"TXXX" => "User defined text information frame",
"UFID" => "Unique file identifier",
"USER" => "Terms of use",
"USLT" => "Unsychronized lyric/text transcription",
"WCOM" => "Commercial information",
"WCOP" => "Copyright/Legal information",
"WOAF" => "Official audio file webpage",
"WOAR" => "Official artist/performer webpage",
"WOAS" => "Official audio source webpage",
"WORS" => "Official internet radio station homepage",
"WPAY" => "Payment",
"WPUB" => "Publishers official webpage",
"WXXX" => "User defined URL link frame",
);
}
}

View file

@ -1,107 +0,0 @@
<?php
namespace PhpId3;
use Exception;
use PhpId3\Id3Tags;
use PhpId3\BinaryFileReader;
/**
* Read ID3Tags and thumbnails.
*
* @author Shubham Jain <shubham.jain.1@gmail.com>
* @license MIT License
*/
class Id3TagsReader
{
private $fileReader;
private $id3Array;
private $validMp3 = TRUE;
public function __construct($fileHandle)
{
$this->fileReader = new BinaryFileReader($fileHandle, array(
"id3" => array(BinaryFileReader::FIXED, 3),
"version" => array(BinaryFileReader::FIXED, 2),
"flag" => array(BinaryFileReader::FIXED, 1),
"sizeTag" => array(BinaryFileReader::FIXED, 4, BinaryFileReader::INT),
));
$data = $this->fileReader->read();
if( $data->id3 !== "ID3")
{
throw new Exception("The MP3 file contains no valid ID3 Tags.");
$this->validMp3 = FALSE;
}
}
public function readAllTags()
{
assert( $this->validMp3 === TRUE);
$bytesPos = 10; //From headers
$this->fileReader->setMap(array(
"frameId" => array(BinaryFileReader::FIXED, 4),
"size" => array(BinaryFileReader::FIXED, 4, BinaryFileReader::INT),
"flag" => array(BinaryFileReader::FIXED, 2),
"body" => array(BinaryFileReader::SIZE_OF, "size"),
));
$id3Tags = Id3Tags::getId3Tags();
while (($file_data = $this->fileReader->read())) {
if (!in_array($file_data->frameId, array_keys($id3Tags))) {
break;
}
$body = $file_data->body;
// If frame is a text frame then we have to consider
// encoding as shown in spec section 4.2
if( $file_data->frameId[0] === "T" )
{
// First character determines the encoding, 1 = ISO-8859-1, 0 = UTF - 16
if( intval(bin2hex($body[0]), 16) === 1)
$body = mb_convert_encoding(substr($body, 1), 'UTF-8', 'UTF-16'); // Convert UTF-16 to UTF-8 to compatible with current browsers
}
$this->id3Array[$file_data->frameId] = array(
"fullTagName" => $id3Tags[$file_data->frameId],
"position" => $bytesPos,
"size" => $file_data->size,
"body" => $body,
);
$bytesPos += 4 + 4 + 2 + $file_data->size;
}
return $this;
}
public function getId3Array()
{
return $this->id3Array;
}
public function getImage()
{
$fp = fopen('data://text/plain;base64,' . base64_encode($this->id3Array["APIC"]["body"]), 'rb'); //Create an artificial stream from Image data
$fileReader = new BinaryFileReader($fp, array(
"textEncoding" => array(BinaryFileReader::FIXED, 1),
"mimeType" => array(BinaryFileReader::NULL_TERMINATED),
"fileName" => array(BinaryFileReader::NULL_TERMINATED),
"contentDesc" => array(BinaryFileReader::NULL_TERMINATED),
"binaryData" => array(BinaryFileReader::EOF_TERMINATED)
)
);
$imageData = $fileReader->read();
return array($imageData->mimeType, $imageData->binaryData);
}
}

View file

@ -1,55 +0,0 @@
#PHP-ID3
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).
To read binary data more effectively, I have created a sclass, [BinaryFileReader](https://gist.github.com/shubhamjain/5964350), which reads data in named chunks.
##How to Install
Into your composer.json
```json
{
"require" : {
"shubhamjain/php-id3": "dev-master"
}
}
```
##How to Use
You will first need to include the autoload.php generated by composer and then you can use the classes in PhpId3 namespace.
```php
<?php
require 'vendor/autoload.php';
//...
use PhpId3\Id3TagsReader;
//...
$id3 = new Id3TagsReader(fopen("Exodus - 06 - Piranha.mp3", "rb"));
$id3->readAllTags(); //Calling this is necesarry before others
foreach($id3->getId3Array() as $key => $value) {
if( $key !== "APIC" ) { //Skip Image data
echo $value["FullTagName"] . ": " . $value["Body"] . "<br />";
}
}
list($mimeType, $image) = $id3->getImage();
file_put_contents("thumb.jpeg", $image ); //Note the image type depends upon MimeType
//...
```
##LICENSE
See ``LICENSE`` for more informations
##Feedback
If you used this project or liked it or have any doubt about the source, send your valuable thoughts at <shubham.jain.1@gmail.com>.

View file

@ -1,43 +0,0 @@
<?php
namespace PhpId3\Tests;
use PhpId3\Id3TagsReader;
use PHPUnit_Framework_TestCase;
class GenerateCvCommandTest extends PHPUnit_Framework_TestCase
{
private $id3;
private $mp3File = "/TestFiles/Exodus - 06 - Piranha.mp3";
private $albumCover = "/TestFiles/thumb.jpeg";
protected function setUp()
{
$this->id3 = new Id3TagsReader(fopen(__DIR__ . $this->mp3File, "rb"));
$this->id3->readAllTags();
}
public function testGetImage()
{
$image = $this->id3->getImage();
$this->assertEquals("mage/jpg", $image[0]);
$this->assertEquals(file_get_contents(__DIR__ . $this->albumCover), $image[1]);
}
public function testGetId3TagsArray()
{
$id3Tags = $this->id3->getId3Array();
$this->assertEquals($id3Tags["TIT2"]["body"], 'Piranha');
$this->assertEquals($id3Tags["TRCK"]["body"], '6');
$this->assertEquals($id3Tags["TCON"]["body"], 'Heavy Metal');
$this->assertEquals($id3Tags["TALB"]["body"], 'Bonded by Blood');
$this->assertEquals($id3Tags["TYER"]["body"], '1985');
$this->assertEquals($id3Tags["TPE1"]["body"], 'Exodus');
$this->assertEquals($id3Tags["TLEN"]["body"], '228963');
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,7 +0,0 @@
<?php
$file = __DIR__ . '/../vendor/autoload.php';
if (!file_exists($file)) {
throw new RuntimeException('Install dependencies to run test suite.');
}
require_once $file;

View file

@ -1,17 +0,0 @@
{
"name": "shubhamjain/php-id3",
"description": "A MP3 ID3 tags reader in native PHP",
"license": "MIT",
"authors": [
{
"name": "Shubham Jain",
"email": "shubham.jain.1@gmail.com"
}
],
"autoload": {
"psr-0": { "PhpId3" : "" }
},
"require": {
"php": ">=5.3.3"
}
}

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="./Tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="PhpId3 test suite">
<directory suffix="Test.php">./Tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./Resources</directory>
<directory>./Tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>