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, similar to Perl's Audio::TagLib. swig can probably accomplish such a thing, but is too ambitious a task and not really necessary for what I need at this time.
This extension aims to provide a unified, quasi-lowlevel interface to audio file metadata tagging:
- for MP3, OGG, and FLAC files only
- getters and setters for the actual tags in a file
- using their actual field names rather than any abstractions.
- common, consistent parameters and naming in the interface whereever possible
- for example, all embedded picture data is required to be set and returned as base64 encoded data
This extension is meant to be used to:
- extract all metadata from an audio file in a single operation
- remove all metadata from an audio file * in a single operation
- write a complete set of metadata to an audio file in a single operation
The user is free to implement abstractions on top of this extension on their own, in PHP.
* See On ID3v2 and FLAC...
linux only at the moment (not tested on BSD or OSX, or anything else for that matter)
PHP5 only at the moment
no support for PHP7 planned yet (different extension API)
no support for HHVM planned yet (way different extension API)
no support for windows planned ever
this is very much a proof-of-concept, work-in-progress, no-warranty-guaranteed, use-at-your-risk, here-be-dragons, no-good-very-bad, you-might-not-need, considered-harmful, disclaimer-of-damages-notwithstanding, etc, etc
don't use this if you're not ready to break fast and move stuff.
If you know C++ and/or have familiarity with the TagLib API and/or the PHP Extension API in addition to knowing PHP please consider contributing :3
- gcc, g++, cmake, etc
- taglib
- php
- any prerequisites thereof
- get taglib v.1.9.1 and compile
- please see the README and installation instructions included with their release.
- get the source code for your current php version by either:
apt-get source php5
- official php source downloads
-
extract the files
-
cd /path/to/php-source-code/ext/
-
git clone git@github.com:dayvonjersen/taglib-php
-
cd taglib-php
-
edit
build.sh
and change the following text: -
/usr/local/include/taglib
to where the taglib.h
files maybe found.- (if you don't know, something like
sudo find / -name "tstringlist.h"
might help) sudo find /usr -name "tstringlist.h"
might be faster
- (if you don't know, something like
-
/usr/local/lib/php/modules
to where your php extension directory is- see
extension_dir
in yourphp.ini
php -i
from CLI and<?php phpinfo(); ?>
from a web script might also be really useful.
- see
-
run
./build.sh --no-tests
- this script will attempt to copy the built module to the extension directory you already specified, if you get a password prompt that's because it's doing exactly this:
-
sudo cp -R modules/* /path/to/extension_dir
-
add
extension=taglib.so
to your php.ini -
be sure to restart apache2 (for mod_php) or restart php5-fpm if you're using that.
From taglib-php
directory
$ cd test
$ php -f taglib-php-tests.php | less -r
Please note that all audio files included with the test suite are 1 second clips of silence.
It may be advantageous to test this extension against actual audio files of a typical length with actual tags.
# apt-get install flac vorbis-tools sox libsox-dev libsox-fmt-all libsox-fmt-mp3 mp3info id3v2 oggz-tools
(names of packages in debian, might be different for your platform, don't know where the source to all of these individually are)
Then, from taglib-php/test
directory
$ cp /path/to/some.mp3 testfiles/
$ cp /path/to/some.ogg testfiles/
$ # and so on..., then:
$ php -f generate-taglib-php-tests.json.php > taglib-php-tests.json
If contributing back, please don't include any of your own audio files or a modified taglib-php-tests.json
file with your pull request...
From taglib-php/test
:
$ cd testcases
$ ls
Pick a file at random and prepare for eyebleed.
The methods provided by this extension will issue PHP errors of level E_WARNING
and return FALSE
by default if they encounter an error.
It is therefore recommended that you have error_reporting
visible in your development environment in order to view the error messages during development, e.g:
error_reporting(E_ALL|~E_STRICT);
ini_set('display_errors', 1);
The one exception to this being class constructors, which will throw Exception
*
It is therefore recommended that you wrap class constructors in a try
-catch
block, e.g:
try {
$t = new TagLibMPEG($path_to_mp3);
} catch(Exception $e) {
echo "Whoops! That wasn't a valid file!\n", $e->getMessage();
}
* because this is the only way I found to return NULL
from a class constructor in a PHP extension. (RETURN_NULL
does nothing as far as I can tell...)
Also, please be aware that there is the very real possiblity of Segmentation fault errors occuring.
It is a high priority of mine to fix these programming errors (which are entirely my fault) but they may be hard to detect for the casual PHP user who has come to expect not running into segfaults during development.
Segmentation faults will bring down the entire running PHP process immediately regardless of any preventative measures taken.
The only indication that a Segmentation fault has occurred will be in the error log for your webserver*, or from stderr if you're using php-cli.
* maybe php-fpm has its own error log as well idk...
Please file a bug report for any Segmentation faults you come across thank you very much.
- If the extension works for you except that you come across this in some PHP code you wrote, please include in your report a complete PHP script which will reliably (as in, always) reproduce the Segmentation fault.
- If you can't load the extension, or the Segmentation fault occurs outside PHP userspace, a description will suffice.
You should be able to debug cli php scripts using gdb (GNU Debugger) because the build script provided should include debugging symbols.
$ gdb php
(gdb) set args -f /path/to/some.php
(gdb) run
...
(gdb) bt
Or something.
If you have OCD about overly bloated binaries, you can use strip
Really just a container for the Predefined Constants.
NOTE: The other classes in this extension: TagLibFLAC, TagLibMPEG, and TagLibOGG do not inherit from, extend from, implement anything from or in any other way relate to this class. All of these classes exposed to the PHP userspace are distinct, independent entities.
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
Convenience method to turn the integers into their respective strings.
public static getPictureTypeAsString(int $type)
type
- one of the above Predefined Constants
A string
representation of the associated constant, with each word Capitalized.
Returns FALSE
if $type
is not an int
or is not in the range 0..20
.
// example usage
echo TagLib::getPictureTypeAsString(TagLib::APIC_FRONTCOVER); // "Front Cover"
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 ) // disabled, always returns false
public bool hasID3v2( void )
public bool|array getID3v2( void )
//public bool setID3v2( array $frames ) // disabled, always returns false
public bool hasXiphComment( void )
public array getXiphComment( void )
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = TRUE ])
public bool hasPicture( void )
public array getPictures( void )
public bool setPicture( array $arr )
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
* )
*/
TagLibFLAC::setID3v1() DISABLED
See also On ID3v2 and FLAC...
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.
TagLibFLAC::setID3v2() DISABLED
See also On ID3v2 and FLAC...
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
}
If the file on disk has a XiphComment, gets it as an associative array.
public bool|array getXiphComment( void )
None
Returns an associative array of string
field names as keys and their string
values.
Returns FALSE
if file does not have a XiphComment
Also returns FALSE
on failure.
See also wiki.xiph.org/VorbisComment and xiph.org/flac/format.html for more information about FLAC metadata tags.
// example usage
$t = new TagLibFLAC('file.flac');
print_r($t->getXiphComment());
/*
* Array
* (
* [ALBUM] => album,
* [ARTIST] => artist,
* [COMMENT] => comment,
* [DATE] => 2000,
* [GENRE] => New Age,
* [TITLE] => title,
* [TRACKNUMBER] => 99
* )
*/
Writes new entries to the XiphComment in the file on disk.
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = TRUE ])
newProperties
- associativearray
argument ofstring
field names as keys andstring
new valuesovewrite_existing_tags
-boolean
whether to overwrite (TRUE
) or append to (FALSE
) existing tags.
I can't stress enough that $overwrite_existing_tags = FALSE
is a bad idea.
Returns TRUE
on success, FALSE
on failure.
// example usage
$t = new TagLibFLAC('file.flac');
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => something
* )
*/
$t->setXiphComment(['TITLE' => 'new title'])
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title
* )
*/
//
// $overwrite_existing_tags = FALSE is a bad idea, here's what it would do:
//
unset($t);
$t = new TagLibFLAC('file.flac');
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title
* )
*/
$t->setXiphComment(['TITLE' => 'new title'], false)
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title new title
* )
*/
// see? absolutely useless.
Check if the file on disk has a METADATA_BLOCK_PICTURE.
public bool hasPicture( void )
None
Returns TRUE
if there is a
// example usage
Please note the "s" in getPictures (it's plural not singular)
Returns a numerically indexed array of all METADATA_BLOCK_PICTUREs in the file on disk.
public array getPictures( void )
None
If the file has no pictures, it returns an empty array ([]
).
Otherwise it's a numerically indexed array of associative arrays with the following values:
data
-string
base64 encoded data of the picturemime
-string
mimetype of the picturetype
-int
one of theAPIC_*
constants, see Predefined Constantsdesc
-string
short description (think "caption") of the picture
$t = new TagLibFLAC('file.flac');
if($t->hasPicture()) {
$pictures = $t->getPictures();
foreach($pictures as $index => $picture) {
$ext = '';
switch($picture['mime']) {
case 'image/jpeg':
case 'image/jpg':
$ext = '.jpg';
break;
case 'image/png':
$ext = '.png';
break;
// add more mimetype support here, for TIFF or GIF or whatever
// most images will be JPEG or PNG however
default:
echo "Unsupported mimetype {$picture['mime']}!!\n";
}
$filename = "picture_$index$ext";
file_put_contents($filename, base64_decode($picture['data']));
// should find your image in e.g. picture_0.jpg
echo "Wrote ", TagLib::getPictureTypeAsString($picture['type']), " image to ", $filename, "\n";
}
}
Writes a METADATA_BLOCK_PICTURE to the file on disk.
public bool setPicture( array $arr )
arr
- an associative array
with the following fields:
data
required -string
base64 encoded image data to writemime
required -string
mimetype of associated imagetype
(optional) -int
one of theAPIC_*
constants, see Predefined Constantsdesc
(optional) -string
short description (think "caption") of the picture
Returns TRUE
on success, FALSE
on failure.
// example usage
$t = new TagLibFLAC('file.flac');
$success = $t->setPicture([
'data' => base64_encode(file_get_contents('my_image.jpg')),
'mime' => 'image/jpg',
'type' => TagLib::APIC_FRONTCOVER,
'desc' => 'sunset on my rooftop last summer'
]);
if($success) {
echo "Wrote image to file successfully.\n";
} else {
echo "Something happened.\n";
}
Attempts to remove all metadata tags from file.
See also On ID3v2 and FLAC...
public bool stripTags( void )
None
Returns TRUE
on success, FALSE
on failure.
May return true and FAIL to remove tags from file, see also On ID3v2 and FLAC...
// example usage
$t = TagLibFLAC('file.flac');
if( $t->hasXiphComment() && !($t->hasID3v1() || $t->hasID3v2()) )
$t->stripTags();
assert($t->hasXiphComment() === false);
} else {
// ¯\_(ツ)_/¯
}
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.
Writes new ID3v2 frames to file.
public bool setID3v2( array $frames[, bool $overwrite_existing_tags = TRUE ] )
-
frames
- associativearray
of new frames withstring
frameID for keys and different values depending on the frameID: -
APIC
(Attached Picture Frame) - => an associativearray
with the following fields:mime
required -string
mimetype of associated imagetype
(optional) -int
one of theAPIC_*
constants, see Predefined Constantsdesc
(optional) -string
short description (think "caption") of the picture
data
-string
base64 encoded image data to write
file
-string
path to image file on disk to write
-
TXXX
(User Text Identification Frame) - => an associativearray
with the following fields:desc
required -string
Description of the Frame itselftext
required -string
contents of the frame
-
WXXX
(User URL Link Frame) => an associativearray
with the following fields:desc
required -string
link texttext
required -string
URL
-
OWNE
(Ownership Frame) => an associativearray
with the following fields:date
required -string
date inYYYYMMDD
formatpaid
required -string
a price, including currency symbol e.g.$0.99
seller
required -string
name of seller
-
PRIV
(Private Frame) => an associativearray
with the following fields:owner
required -string
usually an email addressdata
required -string
whatever
-
UFID
(Unique File Identifier Frame) => an associativearray
with the following fields:owner
required -string
URL to some music identification databaseid
required -string
id in that database corresponding to the sound in this file
-
Additionally, the following frameIDs are explicitly not allowed:
- AENC, COMR, ENCR, EQUA, ETCO, GEOB, GRID, IPLS, LINK, MCDI, MLLT, PCNT, POPM, POSS, RBUF, RVAD, RVRB, SYLT, SYTC, USER
-
All others not listed above => a
string
value. -
overwrite_existing_tags
-boolean
- if
TRUE
, will remove tags with matching frameIDs provided in$frames
from file - if
FALSE
, will add new frames regardless of any previously set values.
- if
See also id3.org for massive migranes all possible frameIDs defined by that specification.
Returns TRUE
on success.
Returns FALSE
and issues E_WARNING
on failure.
// example usage
$t = new TagLibMPEG('file.mp3');
$t->setID3v2(['TPE1' => 'an artist']);
print_r($t->getID3v2());
/**
* Array(
* 0 => Array(
* [frameID] => TPE1
* [data] => an artist
* )
* )
*/
After making this extension I thoroughly hate ID3v2, everything about it and everything it represents.
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 = TRUE ])
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
}
If the file on disk has a XiphComment, gets it as an associative array.
public bool|array getXiphComment( void )
None
Returns an associative array of string
field names as keys and their string
values.
Returns FALSE
if file does not have a XiphComment
Also returns FALSE
on failure.
See also wiki.xiph.org/VorbisComment.
// example usage
$t = new TagLibOGG('file.ogg');
print_r($t->getXiphComment());
/*
* Array
* (
* [ALBUM] => album,
* [ARTIST] => artist,
* [COMMENT] => comment,
* [DATE] => 2000,
* [GENRE] => New Age,
* [TITLE] => title,
* [TRACKNUMBER] => 99
* )
*/
Writes new entries to the XiphComment in the file on disk.
public bool setXiphComment( array $newProperties[, bool $overwrite_existing_tags = TRUE ])
newProperties
- associativearray
argument ofstring
field names as keys andstring
new valuesovewrite_existing_tags
-boolean
whether to overwrite (TRUE
) or append to (FALSE
) existing tags.
I can't stress enough that $overwrite_existing_tags = FALSE
is a bad idea.
Returns TRUE
on success, FALSE
on failure.
// example usage
$t = new TagLibOGG('file.ogg');
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => something
* )
*/
$t->setXiphComment(['TITLE' => 'new title'])
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title
* )
*/
//
// $overwrite_existing_tags = FALSE is a bad idea, here's what it would do:
//
unset($t);
$t = new TagLibOGG('file.ogg');
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title
* )
*/
$t->setXiphComment(['TITLE' => 'new title'], false)
print_r($t->getXiphComment());
/**
* Array(
* [TITLE] => new title new title
* )
*/
// see? absolutely useless.
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
FLACs are not meant to have ID3v2 tags [citation needed] but they can anyway. TagLib does a great job of reading information from FLACs with any combination of ID3v1, ID3v2, and XiphComments, in addition to any METADATA_BLOCK(s) found in the file.
The issues are:
-
Save the file. This will primarily save the XiphComment, but will also keep any old ID3-tags up to date. If the file has no XiphComment, one will be constructed from the ID3-tags.
What this means is that a FLAC file with an ID3v1 or ID3v2 must have a XiphComment.
So if a FLAC comes to you with an ID3v1 or ID3v2 tag you can never fully remove it and, as a side-effect of save()
, the zombie tags will copy themselves into the XiphComment.
My advice to users of this extension in order to get around this sticky situation is to:
-
Read all relevant data from ID3 tags using
TagLibFLAC::getID3v1()
andTagLibFLAC::getID3v2()
first. -
Remove ID3 tags from FLAC files using external tools such as
id3v2
- which is available in debian with
apt-get install id3v2
- on sourceForge
- archive of id3v2.org
- which is available in debian with
-
Never write new ID3 tags to FLAC files
- which is why TagLibFLAC::setID3v1() and TagLibFLAC::setID3v2() are disabled, only ever returning
FALSE
.
- which is why TagLibFLAC::setID3v1() and TagLibFLAC::setID3v2() are disabled, only ever returning
This is why the tests are spurting out a bunch of tl;dr instead of "OK" atm. Speaking of tl;dr:
tl;dr FLAC files with ID3 tags will cause issues
MP3 bitrates returned by TagLibMPEG::getAudioProperties()
in the "bitrate"
field are known to be inaccurate.
OGG bitrates returned by TagLibOGG::getAudioProperties()
in the "bitrate"
field are also inaccurate, use "bitrateNominal"
which is in bits per second and not the typical kbps...
On the other hand, FLAC bitrates returned by TagLibFLAC::getAudioProperties()
in the "bitrate"
field are more accurate than any other tool or media player I've come across including soxi
and even metaflac
.
This is due to the fact that in order to get an accurate reading for the bitrate
bitrate = filesize in bytes * 8 / length in seconds
Metadata must not be included with the filesize. For audio files containing embedded images the discrepancy can be substantial.