summaryrefslogtreecommitdiffstats
path: root/modules/id3/getid3/write.real.php
diff options
context:
space:
mode:
Diffstat (limited to 'modules/id3/getid3/write.real.php')
-rw-r--r--modules/id3/getid3/write.real.php251
1 files changed, 203 insertions, 48 deletions
diff --git a/modules/id3/getid3/write.real.php b/modules/id3/getid3/write.real.php
index 5ede28d1..1e0240cc 100644
--- a/modules/id3/getid3/write.real.php
+++ b/modules/id3/getid3/write.real.php
@@ -16,7 +16,7 @@
class getid3_write_real
{
var $filename;
- var $tag_data;
+ var $tag_data = array();
var $warnings = array(); // any non-critical errors will be stored here
var $errors = array(); // any critical errors will be stored here
var $paddedlength = 512; // minimum length of CONT tag in bytes
@@ -33,50 +33,98 @@ class getid3_write_real
// Initialize getID3 engine
$getID3 = new getID3;
$OldThisFileInfo = $getID3->analyze($this->filename);
- if (empty($OldThisFileInfo['chunks']) && !empty($OldThisFileInfo['old_ra_header'])) {
+ if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
$this->errors[] = 'Cannot write Real tags on old-style file format';
+ fclose($fp_source);
return false;
}
- $OldPROPinfo = false;
- $StartOfDATA = false;
- foreach ($OldThisFileInfo['chunks'] as $chunknumber => $chunkarray) {
- if ($chunkarray['name'] == 'PROP') {
- $OldPROPinfo = $chunkarray;
- } elseif ($chunkarray['name'] = 'DATA') {
- $StartOfDATA = $chunkarray['offset'];
- }
+ if (empty($OldThisFileInfo['real']['chunks'])) {
+ $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
+ fclose($fp_source);
+ return false;
+ }
+ foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+ $oldChunkInfo[$chunkarray['name']] = $chunkarray;
+ }
+ if (!empty($oldChunkInfo['CONT']['length'])) {
+ $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
}
- if (!empty($OldPROPinfo['length'])) {
- $this->paddedlength = max($OldPROPinfo['length'], $this->paddedlength);
+ $new_CONT_tag_data = $this->GenerateCONTchunk();
+ $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
+ $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
+
+ if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) {
+ fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
+ fwrite($fp_source, $new__RMF_tag_data);
+ } else {
+ $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
+ fclose($fp_source);
+ return false;
}
- $new_real_tag_data = GenerateRealTag();
+ if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) {
+ fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
+ fwrite($fp_source, $new_PROP_tag_data);
+ } else {
+ $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
+ fclose($fp_source);
+ return false;
+ }
- if (@$OldPROPinfo['length'] == $new_real_tag_data) {
+ if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) {
// new data length is same as old data length - just overwrite
- fseek($fp_source, $OldPROPinfo['offset'], SEEK_SET);
- fwrite($fp_source, $new_real_tag_data);
+ fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
+ fwrite($fp_source, $new_CONT_tag_data);
+ fclose($fp_source);
+ return true;
} else {
- if (empty($OldPROPinfo)) {
- // no existing PROP chunk
- $BeforeOffset = $StartOfDATA;
- $AfterOffset = $StartOfDATA;
+ if (empty($oldChunkInfo['CONT'])) {
+ // no existing CONT chunk
+ $BeforeOffset = $oldChunkInfo['DATA']['offset'];
+ $AfterOffset = $oldChunkInfo['DATA']['offset'];
} else {
// new data is longer than old data
- $BeforeOffset = $OldPROPinfo['offset'];
- $AfterOffset = $OldPROPinfo['offset'] + $OldPROPinfo['length'];
+ $BeforeOffset = $oldChunkInfo['CONT']['offset'];
+ $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
}
+ if ($tempfilename = tempnam('*', 'getID3')) {
+ ob_start();
+ if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+ rewind($fp_source);
+ fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+ fwrite($fp_temp, $new_CONT_tag_data);
+ fseek($fp_source, $AfterOffset, SEEK_SET);
+ while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+ fwrite($fp_temp, $buffer, strlen($buffer));
+ }
+ fclose($fp_temp);
+
+ if (copy($tempfilename, $this->filename)) {
+ unlink($tempfilename);
+ fclose($fp_source);
+ return true;
+ }
+ unlink($tempfilename);
+ $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+ } else {
+
+ $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+ }
+ ob_end_clean();
+ }
+ fclose($fp_source);
+ return false;
}
- fclose($fp_source);
- return true;
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
@@ -87,28 +135,88 @@ class getid3_write_real
return false;
}
- function GenerateRealTag() {
- $RealCONT = "\x00\x00"; // object version
+ function GenerateRMFchunk(&$chunks) {
+ $oldCONTexists = false;
+ foreach ($chunks as $key => $chunk) {
+ $chunkNameKeys[$chunk['name']] = $key;
+ if ($chunk['name'] == 'CONT') {
+ $oldCONTexists = true;
+ }
+ }
+ $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['title']), 4);
- $RealCONT .= @$this->tag_data['title'];
+ $RMFchunk = "\x00\x00"; // object version
+ $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
+ $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount, 4);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['artist']), 4);
- $RealCONT .= @$this->tag_data['artist'];
+ $RMFchunk = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
+ return $RMFchunk;
+ }
+
+ function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
+ $old_CONT_length = 0;
+ $old_DATA_offset = 0;
+ $old_INDX_offset = 0;
+ foreach ($chunks as $key => $chunk) {
+ $chunkNameKeys[$chunk['name']] = $key;
+ if ($chunk['name'] == 'CONT') {
+ $old_CONT_length = $chunk['length'];
+ } elseif ($chunk['name'] == 'DATA') {
+ if (!$old_DATA_offset) {
+ $old_DATA_offset = $chunk['offset'];
+ }
+ } elseif ($chunk['name'] == 'INDX') {
+ if (!$old_INDX_offset) {
+ $old_INDX_offset = $chunk['offset'];
+ }
+ }
+ }
+ $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['copyright']), 4);
- $RealCONT .= @$this->tag_data['copyright'];
+ $PROPchunk = "\x00\x00"; // object version
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'], 4);
+ $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta), 4);
+ $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta), 4);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'], 2);
+ $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'], 2);
- $RealCONT .= BigEndian2String(strlen(@$this->tag_data['comment']), 4);
- $RealCONT .= @$this->tag_data['comment'];
+ $PROPchunk = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
+ return $PROPchunk;
+ }
- if ($this->paddedlength > (strlen($RealCONT) + 8)) {
- $RealCONT .= str_repeat("\x00", $this->paddedlength - strlen($RealCONT) - 8);
+ function GenerateCONTchunk() {
+ foreach ($this->tag_data as $key => $value) {
+ // limit each value to 0xFFFF bytes
+ $this->tag_data[$key] = substr($value, 0, 65535);
}
- $RealCONT = 'CONT'.BigEndian2String(strlen($RealCONT) + 8, 4).$RealCONT; // CONT chunk identifier + chunk length
+ $CONTchunk = "\x00\x00"; // object version
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2);
+ $CONTchunk .= @$this->tag_data['title'];
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
+ $CONTchunk .= @$this->tag_data['artist'];
+
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
+ $CONTchunk .= @$this->tag_data['copyright'];
- return $RealCONT;
+ $CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
+ $CONTchunk .= @$this->tag_data['comment'];
+
+ if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
+ $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
+ }
+
+ $CONTchunk = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
+
+ return $CONTchunk;
}
function RemoveReal() {
@@ -116,22 +224,69 @@ class getid3_write_real
if (is_writeable($this->filename)) {
if ($fp_source = @fopen($this->filename, 'r+b')) {
-return false;
- //fseek($fp_source, -128, SEEK_END);
- //if (fread($fp_source, 3) == 'TAG') {
- // ftruncate($fp_source, filesize($this->filename) - 128);
- //} else {
- // // no real tag to begin with - do nothing
- //}
+ // Initialize getID3 engine
+ $getID3 = new getID3;
+ $OldThisFileInfo = $getID3->analyze($this->filename);
+ if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+ $this->errors[] = 'Cannot remove Real tags from old-style file format';
+ fclose($fp_source);
+ return false;
+ }
+
+ if (empty($OldThisFileInfo['real']['chunks'])) {
+ $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
+ fclose($fp_source);
+ return false;
+ }
+ foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+ $oldChunkInfo[$chunkarray['name']] = $chunkarray;
+ }
+
+ if (empty($oldChunkInfo['CONT'])) {
+ // no existing CONT chunk
+ fclose($fp_source);
+ return true;
+ }
+
+ $BeforeOffset = $oldChunkInfo['CONT']['offset'];
+ $AfterOffset = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+ if ($tempfilename = tempnam('*', 'getID3')) {
+ ob_start();
+ if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+ rewind($fp_source);
+ fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+ fseek($fp_source, $AfterOffset, SEEK_SET);
+ while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+ fwrite($fp_temp, $buffer, strlen($buffer));
+ }
+ fclose($fp_temp);
+
+ if (copy($tempfilename, $this->filename)) {
+ unlink($tempfilename);
+ fclose($fp_source);
+ return true;
+ }
+ unlink($tempfilename);
+ $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+ } else {
+
+ $this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+ }
+ ob_end_clean();
+ }
fclose($fp_source);
- return true;
+ return false;
+
} else {
$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+ return false;
}
- } else {
- $this->errors[] = $this->filename.' is not writeable';
}
+ $this->errors[] = 'File is not writeable: '.$this->filename;
return false;
}