A php extension which wraps TagLib.
At first I wanted to make a full-fledged, 1:1 extension to the entire TagLib API for php. Upon fully exploring my options in accomplishing such a thing, I found that it's too ambitious and not really necessary for what I need at this time.
swig can probably accomplish it, but would involve a lot of tweaking via custom .cpp and .i files anyway so if you came here looking for that, sorry, but look into swig.
Instead, this is going to use the taglib functions in C++ and expose useful methods packaged in a class to php for specific audio formats.
Previously, I had written scripts in ruby and perl (both of which have taglib wrappers with lots of features exposed and are available right now by the way) to accomplish this. I am basing much of this on those scripts.
At this time I am planning to only support MP3, OGG, and FLAC.
All I need from taglib is to read and write tags from audio files, preferably as stupid-simple and self-contained as possible.
- gcc, cmake, etc
- taglib
- php
- get taglib and compile
- get php source (or
apt-get source php5
) for your current php version (optionally compile a new version of php from source) cd /path/to/php-source-code/ext/
git clone [email protected]:generaltso/taglib-php
cd taglib-php
- run
./build.sh
sudo cp modules/taglib.so /path/to/extension_dir
(seeextension_dir
in yourphp.ini
)- add
extension=taglib.so
to your php.ini
Just a container for these:
For use with get/set ID3v2 functions in this extension:
see also TagLib::ID3v2::AttachedPictureFrame::Type in attachedpictureframe.h
TagLib::APIC_OTHER = 0x00
TagLib::APIC_FILEICON = 0x01
TagLib::APIC_OTHERFILEICON = 0x02
TagLib::APIC_FRONTCOVER = 0x03
TagLib::APIC_BACKCOVER = 0x04
TagLib::APIC_LEAFLETPAGE = 0x05
TagLib::APIC_MEDIA = 0x06
TagLib::APIC_LEADARTIST = 0x07
TagLib::APIC_ARTIST = 0x08
TagLib::APIC_CONDUCTOR = 0x09
TagLib::APIC_BAND = 0x0A
TagLib::APIC_COMPOSER = 0x0B
TagLib::APIC_LYRICIST = 0x0C
TagLib::APIC_RECORDINGLOCATION = 0x0D
TagLib::APIC_DURINGRECORDING = 0x0E
TagLib::APIC_DURINGPERFORMANCE = 0x0F
TagLib::APIC_MOVIESCREENCAPTURE = 0x10
TagLib::APIC_COLOUREDFISH = 0x11
TagLib::APIC_ILLUSTRATION = 0x12
TagLib::APIC_BANDLOGO = 0x13
TagLib::APIC_PUBLISHERLOGO = 0x14
class TagLibFLAC {
public __construct( string $filename )
public array getAudioProperties( void )
public bool hasID3v1( void )
public bool|array getID3v1( void )
public bool|array setID3v1( array $frames )
public bool hasID3v2( void )
public bool|array getID3v2( void )
public bool setID3v2( array $frames )
public bool hasXiphComment( void )
public array getXiphComment( void )
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = FALSE ])
public bool stripTags( void )
}
Attempts to open the file path provided by $filename
.
public __construct( string $filename )
filename
- the file to open, must be a valid FLAC file and php must have read and write permissions for the file.
Throws Exception
on error.
Returns a new TagLibFLAC
object on success or NULL
on failure.
// example usage
try {
$t = new TagLibFLAC('file.flac');
echo "File is open and ready for reading/writing tags!";
} catch(Exception $e) {
echo "Something happened.\n", $e->getMessage();
}
Get information about the FLAC file.
public array getAudioProperties( void )
None
An array with the following keys and possible values:
length
-int
duration of audio in secondsbitrate
-int
bitrate of audio in kilobits per second (kbps)sampleRate
-int
sample rate of audio in Hzchannels
-int
number of audio channels (mono = 1, stereo = 2, ...)sampleWidth
-int
bits per sample of audiosampleFrames
-int
number of frames in audio
// example usage
$t = new TagLibFLAC('file.flac');
print_r($t->getAudioProperties());
/**
* Array
* (
* [length] => 1,
* [bitrate] => 107,
* [sampleRate] => 44100,
* [channels] => 2,
* [sampleWidth] => 16,
* [sampleFrames] => 44100
* )
*/
Check whether file on disk has ID3v1 tag.
public bool hasID3v1( void )
None
TRUE
if file has an ID3v1 tag, FALSE
otherwise.
// example usage
$t = new TagLibFLAC('file.flac');
if($t->hasID3v1()) {
// do stuff
}
If the file on disk has an ID3v1 tag, get the ID3v1 tag as an associative array.
public bool|array getID3v1( void )
None
Returns an associative array of string
frameIDs as keys and their string
values.
Returns FALSE
if file does not have an ID3v1 tag.
Also returns FALSE
on failure.
See also id3.org/ID3v1 and wikipedia for more information about ID3v1.
// example usage
$t = new TagLibFLAC('file_with_id3v1_tag.flac');
print_r($t->getID3v1());
/**
* Array
* (
* [ALBUM] => album,
* [ARTIST] => artist,
* [COMMENT] => comment,
* [DATE] => 2000,
* [GENRE] => New Age,
* [TITLE] => title,
* [TRACKNUMBER] => 99
* )
*/
Write ID3v1 tag fields to file.
public bool|array setID3v1( array $frames[, bool $overwrite_existing_tags = TRUE ] )
$frames
is an associative array
of string
values with UPPERCASE string
frame IDs as keys e.g.:
$frames = ['ALBUM' => 'Abbey Road', 'ARTIST' => 'The Beatles'];
$overwrite_existing_tags
- if
TRUE
(default), tags already present in the file with frame IDs matching the keys of$frames
will be overwritten with the values passed in. - if
FALSE
, tags already present will not be overwritten and will be returned by this function.
TagLib will truncate values greater than 28-30 bytes. See also id3.org/ID3v1 and wikipedia for more information about ID3v1.
Additionally, this function will issue E_WARNING
errors and return FALSE
if $frames
is any other variable type than the one described here.
In other words, this function will not do any type conversion but instead issue PHP errors and return FALSE
if it encounters any unexpected type.
So for something like TRACKNUMBER, ALWAYS convert to string:
$tracknumber = 99;
$frames['TRACKNUMBER'] = sprintf("%d", $tracknumber);
NEVER:
$tracknumber = 99;
$frames['TRACKNUMBER'] = $tracknumber; // will cause errors!
Returns TRUE
on success.
Returns any values from $frames
which could not be set as an associative array
of string
values.
Returns FALSE
on failure.
// example usage
$t = new TagLibFLAC('file.flac');
$frames = ['ALBUM' => 'Abbey Road', 'ARTIST' => 'The Beatles'];
$ret = $t->setID3v1($frames);
if($ret === true) {
echo "Tag set successfully!";
} elseif(is_array($ret)) {
echo "Failed to set these values:\n", print_r($ret,1);
/**
* Array
* (
* [ALBUM] => 'Abbey Road'
* )
*/
} else {
echo "Something happened."
}
Check whether file on disk has ID3v2 tag.
public bool hasID3v2( void )
None
TRUE
if file has an ID3v2 tag, FALSE
otherwise.
// example usage
$t = new TagLibFLAC('file.flac');
if($t->hasID3v2()) {
// do stuff
}
If the file on disk has an ID3v2 tag, get the ID3v2 tag as an associative array.
public bool|array getID3v2( void )
None
Returns an associative array of string
frameIDs as keys and their string
values.
Returns FALSE
if file does not have an ID3v2 tag.
Also returns FALSE
on failure.
See also id3.org/ID3v2 and wikipedia for more information about ID3v2.
public bool setID3v2( void )
None
Check whether file on disk has a XiphComment.
public bool hasXiphComment( void )
None
TRUE
if the file has a XiphComment, FALSE
otherwise.
FALSE
on failure
// example usage
$t = new TagLibFLAC('file.flac');
if($t->hasXiphComment()) {
// do stuff
}
words
public bool|array getXiphComment( void )
// example usage
words
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = FALSE ])
// example usage
words
public bool stripTags( void )
// example usage
class TagLibMPEG {
public __construct( string $filename )
public array getAudioProperties( void )
public bool hasID3v1( void )
public bool|array getID3v1( void )
public bool|array setID3v1( array $frames )
public bool hasID3v2( void )
public bool|array getID3v2( void )
public bool setID3v2( array $frames )
public bool stripTags( void )
}
Attempts to open the file path provided by $filename
.
public __construct( string $filename )
filename
- the file to open, must be a valid MP3 file and php must have read and write permissions for the file.
May work just fine with other MPEG types (.mpeg, .mp2, etc) but this is untested.
Throws Exception
on error.
Returns a new TagLibMPEG
object on success, NULL
on failure.
// example usage
try {
$t = new TagLibMPEG('file.mp3');
echo "File is open and ready for reading/writing tags!";
} catch(Exception $e) {
echo "Something happened.\n", $e->getMessage();
}
Get information about the MP3 file.
public array getAudioProperties( void )
None
An array with the following keys and possible values:
length
-int
duration of audio in secondsbitrate
-int
bitrate of audio in kilobits per second (kbps)sampleRate
-int
samplerate of audio in Hzchannels
-int
number of audio channels (mono = 1, stereo = 2, ...)version
-string
one of the following possible values:"MPEG Version 1"
"MPEG Version 2"
"MPEG Version 2.5"
"Unknown"
channelMode
-string
one of the following possible values:"Mono"
"Dual Mono"
"Stereo"
"Unknown"
layer
-int
, e.g. MP3 is MPEG layer 3protectionEnabled
-bool
isCopyrighted
-bool
isOriginal
-bool
// example usage
$t = new TagLibMPEG('file.mp3');
print_r($t->getAudioProperties());
/**
* Array
* (
* [length] => 1234,
* [bitrate] => 320,
* [sampleRate] => 44100,
* [channels] => 2,
* [version] => MPEG Version 1,
* [channelMode] => Stereo,
* [layer] => 3
* [protectionEnabled] =>
* [isCopyrighted] =>
* [isOriginal] => 1
* )
*/
Check whether file on disk has ID3v1 tag.
public bool hasID3v1( void )
None
TRUE
if file has an ID3v1 tag, FALSE
otherwise.
// example usage
$t = new TagLibMPEG('file.mp3');
if($t->hasID3v1()) {
// do stuff
}
If the file on disk has an ID3v1 tag, get the ID3v1 tag as an associative array.
public bool|array getID3v1( void )
None
Returns an associative array of string
frameIDs as keys and their string
values.
Returns FALSE
if file does not have an ID3v1 tag.
Also returns FALSE
on failure.
See also id3.org/ID3v1 and wikipedia for more information about ID3v1.
// example usage
$t = new TagLibMPEG('file_with_id3v1_tag.mp3');
print_r($t->getID3v1());
/**
* Array
* (
* [ALBUM] => album,
* [ARTIST] => artist,
* [COMMENT] => comment,
* [DATE] => 2000,
* [GENRE] => New Age,
* [TITLE] => title,
* [TRACKNUMBER] => 99
* )
*/
Write ID3v1 tag fields to file.
public bool|array setID3v1( array $frames[, bool $overwrite_existing_tags = TRUE ] )
$frames
is an associative array
of string
values with UPPERCASE string
frame IDs as keys e.g.:
$frames = ['ALBUM' => 'Abbey Road', 'ARTIST' => 'The Beatles'];
$overwrite_existing_tags
- if
TRUE
(default), tags already present in the file with frame IDs matching the keys of$frames
will be overwritten with the values passed in. - if
FALSE
, tags already present will not be overwritten and will be returned by this function.
TagLib will truncate values greater than 28-30 bytes. See also id3.org/ID3v1 and wikipedia for more information about ID3v1.
Additionally, this function will issue E_WARNING
errors and return FALSE
if $frames
is any other variable type than the one described here.
In other words, this function will not do any type conversion but instead issue PHP errors and return FALSE
if it encounters any unexpected type.
So for something like TRACKNUMBER, ALWAYS convert to string:
$tracknumber = 99;
$frames['TRACKNUMBER'] = sprintf("%d", $tracknumber);
NEVER:
$tracknumber = 99;
$frames['TRACKNUMBER'] = $tracknumber; // will cause errors!
Returns TRUE
on success.
Returns any values from $frames
which could not be set as an associative array
of string
values.
Returns FALSE
on failure.
// example usage
$t = new TagLibMPEG('file.mp3');
$frames = ['ALBUM' => 'Abbey Road', 'ARTIST' => 'The Beatles'];
$ret = $t->setID3v1($frames);
if($ret === true) {
echo "Tag set successfully!";
} elseif(is_array($ret)) {
echo "Failed to set these values:\n", print_r($ret,1);
/**
* Array
* (
* [ALBUM] => 'Abbey Road'
* )
*/
} else {
echo "Something happened."
}
Check whether file on disk has ID3v2 tag.
public bool hasID3v2( void )
None
TRUE
if file has an ID3v2 tag, FALSE
otherwise.
// example usage
$t = new TagLibMPEG('file.mp3');
if($t->hasID3v2()) {
// do stuff
}
If the file on disk has an ID3v2 tag, get the ID3v2 tag as an array.
public bool|array getID3v2( void )
None
Returns an array with the following structure:
[
0 => [
"frameID" => "TPE1",
"data" => "Artist Name"
],
//1 => ...
]
See id3.org for possible frameIDs.
It is this way because an ID3v2 tag can contain multiple frames with the same frameID.
Although I would urge you not to treat this unfortunate fact as a feature.
Additionally, certain ID3v2 frames contain additional data, such as APIC (Attached Picture Frame aka cover art aka an image). See example usage.
Returns FALSE
if file does not have an ID3v2 tag.
Returns FALSE
on failure.
// example usage
$t = new TagLibMPEG('file.mp3');
$id3v2 = $t->getID3v2();
print_r($id3v2);
/**
* Array(
* 0 => Array(
* [frameID] => TPE1,
* [data] => Artist Name
* ),
* 1 => Array(
* [frameID] => TIT2,
* [data" => Track Title
* ),
* 2 => Array(
* [frameID] => APIC,
* [data] => (base64 encoded data that will likely flood your screen),
* [mime] => image/jpeg,
* [type] => 3
* [desc] => Optional Description of Image
* )
* )
*/
if(!is_array($id3v2)) return;
// you can do something like this to work with this data:
$tags = [];
foreach($id3v2 as $frame) {
$id = $frame['frameID'];
$data = $frame['data'];
$human_readable_id = '';
$human_readable_data = '';
switch($id) {
case 'APIC':
$human_readable_id = 'Picture';
// write the file to disk...
// get filetype by reading $frame['mime']
$ext = 'unknown';
switch($frame['mime']) {
case 'image/jpeg':
$ext = 'jpg';
break;
case 'image/png':
$ext = 'png';
break;
// most album art will be png or jpg
// it is possible that any other image format might be here tho
// in that case add more cases.
default:
throw new Exception('this shouldn\'t happen');
}
// optionally get picture type if you care
switch($frame['type']) {
case TagLib::APIC_COLOUREDFISH:
echo "<°))))><";
break;
// add the other APIC_* constants here if you want
default:
echo "(boring image)";
}
// write the data to a file
$filename = "album_art$ext";
file_put_contents($filename, base64_decode($data));
$human_readable_data = $filename
break;
// currently APIC is the only different case, structurally speaking
// but more may prove necessary in future
case 'TPE1':
$human_readable_id = 'Artist';
$human_readable_data = $data;
break;
case 'TIT2':
$human_readable_id = 'Title';
$human_readable_data = $data;
break;
// etc...
}
$tags[$human_readable_id] = $human_readable_data;
}
print_r($tags);
/**
* (actually useful tag info hopefully)
*/
See also Predefined Constants for possible Attached Picture Frame Types.
And again, see id3.org for possible frameIDs.
words
public bool setID3v2( array $frames )
// example usage
Clear all tag information from file.
public bool stripTags( void )
None
Returns TRUE
on success, FALSE
on failure.
// example usage
$t = new TagLibMPEG('file.mp3');
// say for argument's sake this file has both ID3v1 and ID3v2 tags.
echo $t->hasID3v1() ? 'true' : 'false'; // true
echo $t->hasID3v2() ? 'true' : 'false'; // true
$t->stripTags();
// now it has neither
echo $t->hasID3v1() ? 'true' : 'false'; // false
echo $t->hasID3v2() ? 'true' : 'false'; // false
TagLib provides ways to remove individual tags without affecting the other(s) but this causSegmentation fault
class TagLibOGG {
public __construct( string $filename[, int $type = TagLibOGG::VORBIS ])
public array getAudioProperties( void )
public bool hasXiphComment( void )
public array getXiphComment( void )
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = FALSE ])
public bool stripTags( void )
}
TagLibOGG::VORBIS = 1
TagLibOGG::OPUS = 2
TagLibOGG::FLAC = 3
TagLibOGG::SPEEX = 4 // don't use this one
Attempts to open the specified file path.
public __construct( string $filename[, int $codec = TagLibOGG::VORBIS ])
filename
- the file to open, must be a valid OGG file of codec
and php must have read and write permissions for the file.
codec
- int
codec used in the OGG container
Most OGGs are vorbis, which is also the default for $codec
, so you probably don't need to worry about this
But OGG is actually a container which supports multiple codecs. If your OGG is a different codec, you must specify it in the constructor with one of the Predefined Constants.
Except speex because speex is deprecated.
Throws Exception
on error.
Returns a new TagLibOGG
object on success or NULL
on failure.
// example usage
try {
$t = new TagLibOGG('file.ogg');
echo "File is open and ready for reading/writing tags!";
} catch(Exception $e) {
echo "Something happened.\n", $e->getMessage();
}
Get Information about the OGG.
public array getAudioProperties( void )
None
An array with the following keys and possible values, depending on the codec of the OGG:
- Common to all codecs:
length
-int
duration of audio in secondsbitrate
-int
bitrate of audio in kilobits per second (kbps)sampleRate
-int
samplerate of audio in Hzchannels
-int
number of audio channels (mono = 1, stereo = 2, ...)
TagLibOGG::VORBIS
only:
vorbisVersion
-int
Returns the Vorbis version, currently "0" (as specified by the spec).bitrateMaximum
-int
maximum bitratebitrateNominal
-int
nominal bitratebitrateMinimum
-int
minimum bitrate
TagLibOGG::OPUS
only:
opusVersion
-int
Returns the Opus version, in the range 0...255.inputSampleRate
-int
The Opus codec supports decoding at multiple sample rates, there is no single sample rate of the encoded stream. This returns the sample rate of the original audio stream.
TagLibOGG::FLAC
only:
sampleWidth
-int
bits per sample of audiosampleFrames
-int
number of frames in audio
// vorbis
$v = new TagLibOGG('vorbis.ogg', TagLibOGG::VORBIS);
print_r($v->getAudioProperties());
/**
* Array(
* [length] => 1,
* [bitrate] => 120,
* [sampleRate] => 44100,
* [channels] => 1,
* [vorbisVersion] => 0,
* [bitrateMaximum] => 0,
* [bitrateNominal] => 120000,
* [bitrateMinimum] => 0
* )
*/
// opus
$o = new TagLibOGG('opus.ogg', TagLibOGG::OPUS);
print_r($o->getAudioProperties());
/**
* Array(
* [length] => 1,
* [bitrate] => 120,
* [sampleRate] => 44100,
* [channels] => 1,
* [opusVersion] => 2,
* [inputSampleRate] => 44100
* )
*/
// flac
$f = new TagLibOGG('flac.ogg', TagLibOGG::FLAC);
print_r($f->getAudioProperties());
/**
* Array(
* [length] => 1,
* [bitrate] => 120,
* [sampleRate] => 44100,
* [channels] => 1,
* [sampleWidth] => 16,
* [sampleFrames] => 44100
* )
*/
Check whether file on disk has a XiphComment.
public bool hasXiphComment( void )
None
TRUE
if the file has a XiphComment, FALSE
otherwise.
FALSE
on failure
// example usage
$t = new TagLibOGG('file.ogg');
if($t->hasXiphComment()) {
// do stuff
}
Get the XiphComment as an associative array.
public bool|array getXiphComment( void )
None
// example usage
words
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = FALSE ])
// example usage
Clear XiphComment from file.
public bool stripTags( void )
None
Returns TRUE
on success, FALSE
on failure.
// example usage
$t = new TagLibOGG('file.ogg');
// say for argument's sake this file has a XiphComment.
echo $t->hasXiphComment() ? 'true' : 'false'; // true
$t->stripTags();
// now it doesn't
echo $t->hasXiphComment() ? 'true' : 'false'; // false