Support for decoding and encoding frames with unsynchronisation schema (ID3v2.4 only)
git-svn-id: http://php-reader.googlecode.com/svn/trunk@107 51a70ab9-7547-0410-9469-37e369ee0574
This commit is contained in:
@@ -43,7 +43,7 @@ require_once("ID3/Object.php");
|
||||
* A base class for all ID3v2 frames as described in the
|
||||
* {@link http://www.id3.org/id3v2.4.0-frames ID3v2 frames document}.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @package php-reader
|
||||
* @subpackage ID3
|
||||
* @author Sven Vollbehr <svollbehr@gmail.com>
|
||||
@@ -128,11 +128,11 @@ class ID3_Frame extends ID3_Object
|
||||
|
||||
/**
|
||||
* Raw content of the frame.
|
||||
*
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_data = "";
|
||||
|
||||
|
||||
/**
|
||||
* Constructs the class with given parameters and reads object related data
|
||||
* from the ID3v2 tag.
|
||||
@@ -144,15 +144,15 @@ class ID3_Frame extends ID3_Object
|
||||
public function __construct($reader = null, &$options = array())
|
||||
{
|
||||
parent::__construct($reader, $options);
|
||||
|
||||
|
||||
if ($reader === null) {
|
||||
$this->_identifier = substr(get_class($this), -4);
|
||||
} else {
|
||||
$this->_identifier = $this->_reader->readString8(4);
|
||||
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||
|
||||
/* ID3v2.3.0 Flags; convert to 2.4.0 format */
|
||||
/* ID3v2.3.0 size and flags; convert flags to 2.4.0 format */
|
||||
if ($this->getOption("version", 4) < 4) {
|
||||
$this->_size = $this->_reader->readUInt32BE();
|
||||
$flags = $this->_reader->readUInt16BE();
|
||||
if (($flags & 0x8000) == 0x8000)
|
||||
$this->_flags |= self::DISCARD_ON_TAGCHANGE;
|
||||
@@ -168,14 +168,26 @@ class ID3_Frame extends ID3_Object
|
||||
$this->_flags |= self::GROUPING_IDENTITY;
|
||||
}
|
||||
|
||||
/* ID3v2.4.0 Flags */
|
||||
else
|
||||
/* ID3v2.4.0 size and flags */
|
||||
else {
|
||||
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||
$this->_flags = $this->_reader->readUInt16BE();
|
||||
}
|
||||
|
||||
$dataLength = $this->_size;
|
||||
if ($this->hasFlag(self::DATA_LENGTH_INDICATOR)) {
|
||||
$dataLength = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||
$this->_size -= 4;
|
||||
}
|
||||
$this->_data = $this->_reader->read($this->_size);
|
||||
$this->_size = $dataLength;
|
||||
|
||||
if ($this->hasFlag(self::UNSYNCHRONISATION) ||
|
||||
$this->getOption("unsyncronisation", false) === true)
|
||||
$this->_data = $this->decodeUnsynchronisation($this->_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the frame identifier string.
|
||||
*
|
||||
@@ -227,14 +239,14 @@ class ID3_Frame extends ID3_Object
|
||||
/**
|
||||
* Sets the frame raw data.
|
||||
*
|
||||
* @return string
|
||||
* @param string $data
|
||||
*/
|
||||
protected function setData($data)
|
||||
{
|
||||
$this->_data = $data;
|
||||
$this->_size = strlen($data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the frame raw data.
|
||||
*
|
||||
@@ -263,8 +275,21 @@ class ID3_Frame extends ID3_Object
|
||||
else
|
||||
$flags = $this->_flags;
|
||||
|
||||
$size = $this->_size;
|
||||
if ($this->getOption("version", 4) < 4)
|
||||
$data = $this->_data;
|
||||
else {
|
||||
$data = $this->encodeUnsynchronisation($this->_data);
|
||||
if (($dataLength = strlen($data)) != $size) {
|
||||
$size = 4 + $dataLength;
|
||||
$data = Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) .
|
||||
$data;
|
||||
$flags |= self::DATA_LENGTH_INDICATOR | self::UNSYNCHRONISATION;
|
||||
$this->setOption("unsyncronisation", true);
|
||||
}
|
||||
}
|
||||
return Transform::toString8(substr($this->_identifier, 0, 4), 4) .
|
||||
Transform::toUInt32BE($this->encodeSynchsafe32($this->_size)) .
|
||||
Transform::toUInt16BE($flags) . $this->_data;
|
||||
Transform::toUInt32BE($this->encodeSynchsafe32($size)) .
|
||||
Transform::toUInt16BE($flags) . $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ abstract class ID3_Frame_AbstractText extends ID3_Frame
|
||||
case self::UTF16LE:
|
||||
$array = $this->_text;
|
||||
foreach ($array as &$text)
|
||||
$text = Transform::toString16($str);
|
||||
$text = Transform::toString16($text);
|
||||
$data .= Transform::toString16
|
||||
(implode("\0\0", $array), $this->_encoding == self::UTF16 ?
|
||||
Transform::MACHINE_ENDIAN_ORDER : Transform::LITTLE_ENDIAN_ORDER);
|
||||
|
||||
@@ -58,7 +58,6 @@ require_once("ID3/Timing.php");
|
||||
* a time-period is at the same time as the beat description occurs. There may
|
||||
* only be one SYTC frame in each tag.
|
||||
*
|
||||
* @todo The data could be parsed further; data samples needed
|
||||
* @package php-reader
|
||||
* @subpackage ID3
|
||||
* @author Sven Vollbehr <svollbehr@gmail.com>
|
||||
@@ -99,7 +98,7 @@ final class ID3_Frame_SYTC extends ID3_Frame
|
||||
$this->_format = Transform::fromUInt8($this->_data[$offset++]);
|
||||
while ($offset < strlen($this->_data)) {
|
||||
$tempo = Transform::fromUInt8($this->_data[$offset++]);
|
||||
if ($tempo == 0xFF)
|
||||
if ($tempo == 0xff)
|
||||
$tempo += Transform::fromUInt8($this->_data[$offset++]);
|
||||
$this->_events
|
||||
[Transform::fromUInt32BE(substr($this->_data, $offset, 4))] = $tempo;
|
||||
|
||||
@@ -89,7 +89,7 @@ final class ID3_Header extends ID3_Object
|
||||
*/
|
||||
public function __construct($reader = null, &$options = array())
|
||||
{
|
||||
parent::__construct($reader);
|
||||
parent::__construct($reader, $options);
|
||||
|
||||
if ($reader === null)
|
||||
return;
|
||||
@@ -98,8 +98,6 @@ final class ID3_Header extends ID3_Object
|
||||
$this->_reader->readInt8() + $this->_reader->readInt8() / 10;
|
||||
$this->_flags = $this->_reader->readInt8();
|
||||
$this->_size = $this->decodeSynchsafe32($this->_reader->readUInt32BE());
|
||||
|
||||
$this->setOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,7 +72,7 @@ abstract class ID3_Object
|
||||
public function __construct($reader = null, &$options = array())
|
||||
{
|
||||
$this->_reader = $reader;
|
||||
$this->_options = $options;
|
||||
$this->_options = &$options;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +101,7 @@ abstract class ID3_Object
|
||||
*
|
||||
* @param Array $options The options array.
|
||||
*/
|
||||
public function setOptions(&$options) { $this->_options = $options; }
|
||||
public function setOptions(&$options) { $this->_options = &$options; }
|
||||
|
||||
/**
|
||||
* Sets the given option the given value.
|
||||
@@ -168,6 +168,49 @@ abstract class ID3_Object
|
||||
($val & 0x7f0000) >> 2 | ($val & 0x7f000000) >> 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the unsynchronisation scheme to the given data string.
|
||||
*
|
||||
* Whenever a false synchronisation is found within the data, one zeroed byte
|
||||
* is inserted after the first false synchronisation byte. This has the side
|
||||
* effect that all 0xff00 combinations have to be altered, so they will not
|
||||
* be affected by the decoding process. Therefore all the 0xff00 combinations
|
||||
* have to be replaced with the 0xff0000 combination during the
|
||||
* unsynchronisation.
|
||||
*
|
||||
* @param string $data The input data.
|
||||
* @return string
|
||||
*/
|
||||
protected function encodeUnsynchronisation(&$data)
|
||||
{
|
||||
$result = "";
|
||||
for ($i = 0, $j = 0; $i < strlen($data) - 1; $i++)
|
||||
if (ord($data[$i]) == 0xff &&
|
||||
((($tmp = ord($data[$i + 1])) & 0xe0) == 0xe0 || $tmp == 0x0)) {
|
||||
$result .= substr($data, $j, $i + 1 - $j) . "\0";
|
||||
$j = $i + 1;
|
||||
}
|
||||
return $result . substr($data, $j);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the unsynchronisation scheme from the given data string.
|
||||
*
|
||||
* @see encodeUnsyncronisation
|
||||
* @param string $data The input data.
|
||||
* @return string
|
||||
*/
|
||||
protected function decodeUnsynchronisation(&$data)
|
||||
{
|
||||
$result = "";
|
||||
for ($i = 0, $j = 0; $i < strlen($data) - 1; $i++)
|
||||
if (ord($data[$i]) == 0xff && ord($data[$i + 1]) == 0x0) {
|
||||
$result .= substr($data, $j, $i + 1 - $j);
|
||||
$j = $i + 2;
|
||||
}
|
||||
return $result . substr($data, $j);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits UTF-16 formatted binary data up according to null terminators
|
||||
* residing in the string, up to a given limit.
|
||||
|
||||
@@ -108,22 +108,25 @@ final class ID3v1
|
||||
private $_reader;
|
||||
|
||||
/** @var string */
|
||||
private $_filename;
|
||||
private $_filename = false;
|
||||
|
||||
/**
|
||||
* Constructs the ID3v1 class with given file. The file is not mandatory
|
||||
* argument and may be omitted. A new tag can be written to a file also by
|
||||
* giving the filename to the {@link #write} method of this class.
|
||||
*
|
||||
* @param string $filename The path to the file.
|
||||
* @param string|Reader $filename The path to the file, file descriptor of an
|
||||
* opened file, or {@link Reader} instance.
|
||||
*/
|
||||
public function __construct($filename = false)
|
||||
{
|
||||
if (($this->_filename = $filename) !== false &&
|
||||
file_exists($filename) !== false)
|
||||
$this->_reader = new Reader($filename);
|
||||
else if ($filename instanceof Reader)
|
||||
if ($filename instanceof Reader)
|
||||
$this->_reader = &$filename;
|
||||
else if ((is_string($filename) && ($this->_filename = $filename) !== false &&
|
||||
file_exists($filename) !== false) ||
|
||||
(is_resource($filename) &&
|
||||
in_array(get_resource_type($filename), array("file", "stream"))))
|
||||
$this->_reader = new Reader($filename);
|
||||
else
|
||||
return;
|
||||
|
||||
@@ -288,9 +291,9 @@ final class ID3v1
|
||||
if ($filename === false && ($filename = $this->_filename) === false)
|
||||
throw new ID3_Exception("No file given to write the tag to");
|
||||
|
||||
if (($fd = fopen
|
||||
($filename, file_exists($filename) ? "r+b" : "wb")) === false)
|
||||
throw new ID3_Exception("Unable to open file for writing: " . $filename);
|
||||
if (($fd = fopen
|
||||
($filename, file_exists($filename) ? "r+b" : "wb")) === false)
|
||||
throw new ID3_Exception("Unable to open file for writing: " . $filename);
|
||||
|
||||
fseek($fd, $this->_reader !== false ? -128 : 0, SEEK_END);
|
||||
fwrite($fd, $this, 128);
|
||||
|
||||
127
src/ID3v2.php
127
src/ID3v2.php
@@ -3,7 +3,7 @@
|
||||
* PHP Reader Library
|
||||
*
|
||||
* Copyright (c) 2008 The PHP Reader Project Workgroup. All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
@@ -59,10 +59,11 @@ require_once("ID3/Frame.php");
|
||||
* need not be known to the software that encounters them. Each frame has an
|
||||
* unique and predefined identifier which allows software to skip unknown
|
||||
* frames.
|
||||
*
|
||||
*
|
||||
* @package php-reader
|
||||
* @subpackage ID3
|
||||
* @author Sven Vollbehr <svollbehr@gmail.com>
|
||||
* @author Ryan Butterfield <buttza@gmail.com>
|
||||
* @copyright Copyright (c) 2008 The PHP Reader Project Workgroup
|
||||
* @license http://code.google.com/p/php-reader/wiki/License New BSD License
|
||||
* @version $Rev$
|
||||
@@ -77,26 +78,26 @@ final class ID3v2
|
||||
|
||||
/** @var ID3_ExtendedHeader */
|
||||
private $_extendedHeader;
|
||||
|
||||
|
||||
/** @var ID3_Header */
|
||||
private $_footer;
|
||||
|
||||
|
||||
/** @var Array */
|
||||
private $_frames = array();
|
||||
|
||||
|
||||
/** @var string */
|
||||
private $_filename = false;
|
||||
|
||||
|
||||
/** @var Array */
|
||||
private $_options;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs the ID3v2 class with given file and options. The options array
|
||||
* may also be given as the only parameter.
|
||||
*
|
||||
* The following options are currently recognized:
|
||||
* o version -- The ID3v2 tag version to use in write operation. This option
|
||||
* is automatically set when a tag is read from a file and defaults to
|
||||
* is automatically set when a tag is read from a file and defaults to
|
||||
* version 4.0 for tag write.
|
||||
* o readonly -- Indicates that the tag is read from a temporary file or
|
||||
* another source it cannot be written back to. The tag can, however,
|
||||
@@ -105,9 +106,9 @@ final class ID3v2
|
||||
* @todo Only limited subset of flags are processed.
|
||||
* @todo Utilize the SEEK frame and search for a footer to find the tag
|
||||
* @todo Utilize the LINK frame to fetch frames from other sources
|
||||
* @param string $filename The path to the file, file descriptor of an opened
|
||||
* file, or {@link Reader} instance.
|
||||
* @param Array $options The options array.
|
||||
* @param string|Reader $filename The path to the file, file descriptor of an
|
||||
* opened file, or {@link Reader} instance.
|
||||
* @param Array $options The options array.
|
||||
*/
|
||||
public function __construct($filename = false, $options = array())
|
||||
{
|
||||
@@ -115,43 +116,50 @@ final class ID3v2
|
||||
$options = $filename;
|
||||
$filename = false;
|
||||
}
|
||||
|
||||
|
||||
$this->_options = &$options;
|
||||
if ($filename === false ||
|
||||
(is_string($filename) && file_exists($filename) === false) ||
|
||||
(is_resource($filename) && get_resource_type($filename) != "file")) {
|
||||
(is_resource($filename) &&
|
||||
in_array(get_resource_type($filename), array("file", "stream")))) {
|
||||
$this->_header = new ID3_Header(null, $options);
|
||||
} else {
|
||||
if (is_string($filename) && !isset($options["readonly"]))
|
||||
$this->_filename = $filename;
|
||||
if ($filename instanceof Reader)
|
||||
$this->_reader = $filename;
|
||||
$this->_reader = &$filename;
|
||||
else
|
||||
$this->_reader = new Reader($filename);
|
||||
if ($this->_reader->readString8(3) != "ID3")
|
||||
throw new ID3_Exception
|
||||
("File does not contain ID3v2 tag: " . $filename);
|
||||
throw new ID3_Exception("File does not contain ID3v2 tag");
|
||||
|
||||
$startOffset = $this->_reader->getOffset();
|
||||
|
||||
$this->_header = new ID3_Header($this->_reader, $options);
|
||||
if ($this->_header->getVersion() < 3 || $this->_header->getVersion() > 4)
|
||||
throw new ID3_Exception
|
||||
("File does not contain ID3v2 tag of supported version: " . $filename);
|
||||
("File does not contain ID3v2 tag of supported version");
|
||||
if ($this->_header->getVersion() < 4 &&
|
||||
$this->_header->hasFlag(ID3_Header::UNSYNCHRONISATION))
|
||||
throw new ID3_Exception
|
||||
("Unsynchronisation not supported for this version of ID3v2 tag");
|
||||
unset($this->_options["unsyncronisation"]);
|
||||
if ($this->_header->hasFlag(ID3_Header::UNSYNCHRONISATION))
|
||||
$this->_options["unsyncronisation"] = true;
|
||||
if ($this->_header->hasFlag(ID3_Header::EXTENDEDHEADER))
|
||||
$this->_extendedHeader =
|
||||
new ID3_ExtendedHeader($this->_reader, $options);
|
||||
if ($this->_header->hasFlag(ID3_Header::FOOTER))
|
||||
$this->_footer = &$this->_header; // skip footer, and rather copy header
|
||||
|
||||
|
||||
while (true) {
|
||||
$offset = $this->_reader->getOffset();
|
||||
|
||||
|
||||
// Jump off the loop if we reached the end of the tag
|
||||
if ($offset - $startOffset - 10 >= $this->_header->getSize() -
|
||||
($this->hasFooter() ? 10 : 0))
|
||||
break;
|
||||
|
||||
|
||||
// Jump off the loop if we reached the last frame
|
||||
if ($this->_reader->available() < 4 || Transform::fromUInt32BE
|
||||
($identifier = $this->_reader->read(4)) == 0)
|
||||
@@ -165,7 +173,7 @@ final class ID3v2
|
||||
$frame = new $classname($this->_reader, $options);
|
||||
else
|
||||
$frame = new ID3_Frame($this->_reader, $options);
|
||||
|
||||
|
||||
if (!isset($this->_frames[$frame->getIdentifier()]))
|
||||
$this->_frames[$frame->getIdentifier()] = array();
|
||||
$this->_frames[$frame->getIdentifier()][] = $frame;
|
||||
@@ -175,15 +183,15 @@ final class ID3v2
|
||||
|
||||
/**
|
||||
* Returns the header object.
|
||||
*
|
||||
*
|
||||
* @return ID3_Header
|
||||
*/
|
||||
public function getHeader() { return $this->_header; }
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether there is an extended header present in the tag. Returns
|
||||
* <var>true</var> if the header is present, <var>false</var> otherwise.
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasExtendedHeader()
|
||||
@@ -191,11 +199,11 @@ final class ID3v2
|
||||
if ($this->_header)
|
||||
return $this->_header->hasFlag(ID3_Header::EXTENDEDHEADER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the extended header object if present, or <var>false</var>
|
||||
* otherwise.
|
||||
*
|
||||
*
|
||||
* @return ID3_ExtendedHeader|false
|
||||
*/
|
||||
public function getExtendedHeader()
|
||||
@@ -219,27 +227,27 @@ final class ID3v2
|
||||
$this->_extendedHeader = $extendedHeader;
|
||||
} else throw new ID3_Exception("Invalid argument");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether there is a frame given as an argument defined in the tag.
|
||||
* Returns <var>true</var> if one ore more frames are present,
|
||||
* <var>false</var> otherwise.
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasFrame($identifier)
|
||||
{
|
||||
return isset($this->_frames[$identifier]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns all the frames the tag contains as an associate array. The frame
|
||||
* identifiers work as keys having an array of frames as associated value.
|
||||
*
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function getFrames() { return $this->_frames; }
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of frames matching the given identifier or an empty array
|
||||
* if no frames matched the identifier.
|
||||
@@ -251,7 +259,7 @@ final class ID3v2
|
||||
* Please note that one may also use the shorthand $obj->identifier to access
|
||||
* the first frame with the identifier given. Wildcards cannot be used with
|
||||
* the shorthand.
|
||||
*
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
public function getFramesByIdentifier($identifier)
|
||||
@@ -265,7 +273,7 @@ final class ID3v2
|
||||
$matches[] = $frame;
|
||||
return $matches;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new frame to the tag and returns it.
|
||||
*
|
||||
@@ -279,18 +287,18 @@ final class ID3v2
|
||||
$this->_frames[$frame->getIdentifier()] = array();
|
||||
return $this->_frames[$frame->getIdentifier()][] = $frame;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether there is a footer present in the tag. Returns
|
||||
* <var>true</var> if the footer is present, <var>false</var> otherwise.
|
||||
*
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasFooter()
|
||||
{
|
||||
return $this->_header->hasFlag(ID3_Header::FOOTER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the footer object if present, or <var>false</var> otherwise.
|
||||
*
|
||||
@@ -302,7 +310,7 @@ final class ID3v2
|
||||
return $this->_footer;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets whether the tag should have a footer defined.
|
||||
*
|
||||
@@ -319,13 +327,13 @@ final class ID3v2
|
||||
overridden upon re-write */
|
||||
if ($this->hasFooter())
|
||||
$this->_header->setSize($this->_header->getSize() + 10);
|
||||
|
||||
|
||||
$this->_header->setFlags
|
||||
($this->_header->getFlags() & ~ID3_Header::FOOTER);
|
||||
$this->_footer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the possibly altered ID3v2 tag back to the file where it was read.
|
||||
* If the class was constructed without a file name, one can be provided here
|
||||
@@ -341,11 +349,16 @@ final class ID3v2
|
||||
{
|
||||
if ($filename === false && ($filename = $this->_filename) === false)
|
||||
throw new ID3_Exception("No file given to write the tag to");
|
||||
|
||||
else if ($filename !== false && $this->_filename !== false &&
|
||||
realpath($filename) != realpath($this->_filename) &&
|
||||
!copy($this->_filename, $filename))
|
||||
throw new ID3_Exception("Unable to copy source to destination: " .
|
||||
realpath($this->_filename) . "->" . realpath($filename));
|
||||
|
||||
if (($fd = fopen
|
||||
($filename, file_exists($filename) ? "r+b" : "wb")) === false)
|
||||
throw new ID3_Exception("Unable to open file for writing: " . $filename);
|
||||
|
||||
|
||||
$oldTagSize = $this->_header->getSize();
|
||||
$tag = "" . $this;
|
||||
$tagSize = empty($this->_frames) ? 0 : strlen($tag);
|
||||
@@ -368,7 +381,7 @@ final class ID3v2
|
||||
|
||||
$this->_filename = $filename;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic function so that $obj->value will work. The method will attempt to
|
||||
* return the first frame that matches the identifier.
|
||||
@@ -393,7 +406,7 @@ final class ID3v2
|
||||
return $this->addFrame(new $classname());
|
||||
throw new ID3_Exception("Unknown frame/field: " . $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Magic function so that isset($obj->value) will work. This method checks
|
||||
* whether the frame matching the identifier exists.
|
||||
@@ -401,16 +414,19 @@ final class ID3v2
|
||||
* @param string $name The frame identifier.
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset($name) { return isset($this->_boxes[$name]); }
|
||||
|
||||
public function __isset($name)
|
||||
{
|
||||
return isset($this->_frames[strtoupper($name)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic function so that unset($obj->value) will work. This method removes
|
||||
* all the frames matching the identifier.
|
||||
*
|
||||
* @param string $name The frame identifier.
|
||||
*/
|
||||
public function __unset($name) { unset($this->_boxes[$name]); }
|
||||
|
||||
public function __unset($name) { unset($this->_frames[strtoupper($name)]); }
|
||||
|
||||
/**
|
||||
* Returns the tag raw data.
|
||||
*
|
||||
@@ -418,6 +434,8 @@ final class ID3v2
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
unset($this->_options["unsyncronisation"]);
|
||||
|
||||
$data = "";
|
||||
foreach ($this->_frames as $frames)
|
||||
foreach ($frames as $frame)
|
||||
@@ -426,6 +444,11 @@ final class ID3v2
|
||||
$datalen = strlen($data);
|
||||
$padlen = 0;
|
||||
|
||||
if (isset($this->_options["unsyncronisation"]) &&
|
||||
$this->_options["unsyncronisation"] === true)
|
||||
$this->_header->setFlags
|
||||
($this->_header->getFlags() | ID3_Header::UNSYNCHRONISATION);
|
||||
|
||||
/* The tag padding is calculated as follows. If the tag can be written in
|
||||
the space of the previous tag, the remaining space is used for padding.
|
||||
If there is no previous tag or the new tag is bigger than the space taken
|
||||
@@ -438,11 +461,11 @@ final class ID3v2
|
||||
else
|
||||
$padlen = ceil(log(0.2 * ($datalen / 1024 + 10), 10) * 1024);
|
||||
}
|
||||
|
||||
|
||||
/* ID3v2.4.0 CRC calculated w/ padding */
|
||||
if (!isset($this->_options["version"]) || $this->_options["version"] >= 4)
|
||||
$data = str_pad($data, $datalen + $padlen, "\0");
|
||||
|
||||
|
||||
if ($this->hasExtendedHeader()) {
|
||||
$this->_extendedHeader->setPadding($padlen);
|
||||
if ($this->_extendedHeader->hasFlag(ID3_ExtendedHeader::CRC32)) {
|
||||
@@ -453,13 +476,13 @@ final class ID3v2
|
||||
}
|
||||
$data = $this->getExtendedHeader() . $data;
|
||||
}
|
||||
|
||||
|
||||
/* ID3v2.3.0 CRC calculated w/o padding */
|
||||
if (isset($this->_options["version"]) && $this->_options["version"] < 4)
|
||||
$data = str_pad($data, $datalen + $padlen, "\0");
|
||||
|
||||
|
||||
$this->_header->setSize(strlen($data));
|
||||
|
||||
|
||||
return "ID3" . $this->_header . $data .
|
||||
($this->hasFooter() ? "3DI" . $this->getFooter() : "");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user