]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/james-heinrich/getid3/getid3/write.real.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / james-heinrich / getid3 / getid3 / write.real.php
1 <?php
2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org>               //
4 //  available at http://getid3.sourceforge.net                 //
5 //            or http://www.getid3.org                         //
6 //          also https://github.com/JamesHeinrich/getID3       //
7 /////////////////////////////////////////////////////////////////
8 // See readme.txt for more details                             //
9 /////////////////////////////////////////////////////////////////
10 //                                                             //
11 // write.real.php                                              //
12 // module for writing RealAudio/RealVideo tags                 //
13 // dependencies: module.tag.real.php                           //
14 //                                                            ///
15 /////////////////////////////////////////////////////////////////
16
17 class getid3_write_real
18 {
19         public $filename;
20         public $tag_data          = array();
21         public $fread_buffer_size = 32768;   // read buffer size in bytes
22         public $warnings          = array(); // any non-critical errors will be stored here
23         public $errors            = array(); // any critical errors will be stored here
24         public $paddedlength      = 512;     // minimum length of CONT tag in bytes
25
26         public function __construct() {
27                 return true;
28         }
29
30         public function WriteReal() {
31                 // File MUST be writeable - CHMOD(646) at least
32                 if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
33
34                         // Initialize getID3 engine
35                         $getID3 = new getID3;
36                         $OldThisFileInfo = $getID3->analyze($this->filename);
37                         if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
38                                 $this->errors[] = 'Cannot write Real tags on old-style file format';
39                                 fclose($fp_source);
40                                 return false;
41                         }
42
43                         if (empty($OldThisFileInfo['real']['chunks'])) {
44                                 $this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
45                                 fclose($fp_source);
46                                 return false;
47                         }
48                         foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
49                                 $oldChunkInfo[$chunkarray['name']] = $chunkarray;
50                         }
51                         if (!empty($oldChunkInfo['CONT']['length'])) {
52                                 $this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
53                         }
54
55                         $new_CONT_tag_data = $this->GenerateCONTchunk();
56                         $new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
57                         $new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
58
59                         if (isset($oldChunkInfo['.RMF']['length']) && ($oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data))) {
60                                 fseek($fp_source, $oldChunkInfo['.RMF']['offset']);
61                                 fwrite($fp_source, $new__RMF_tag_data);
62                         } else {
63                                 $this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
64                                 fclose($fp_source);
65                                 return false;
66                         }
67
68                         if (isset($oldChunkInfo['PROP']['length']) && ($oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data))) {
69                                 fseek($fp_source, $oldChunkInfo['PROP']['offset']);
70                                 fwrite($fp_source, $new_PROP_tag_data);
71                         } else {
72                                 $this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
73                                 fclose($fp_source);
74                                 return false;
75                         }
76
77                         if (isset($oldChunkInfo['CONT']['length']) && ($oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data))) {
78
79                                 // new data length is same as old data length - just overwrite
80                                 fseek($fp_source, $oldChunkInfo['CONT']['offset']);
81                                 fwrite($fp_source, $new_CONT_tag_data);
82                                 fclose($fp_source);
83                                 return true;
84
85                         } else {
86
87                                 if (empty($oldChunkInfo['CONT'])) {
88                                         // no existing CONT chunk
89                                         $BeforeOffset = $oldChunkInfo['DATA']['offset'];
90                                         $AfterOffset  = $oldChunkInfo['DATA']['offset'];
91                                 } else {
92                                         // new data is longer than old data
93                                         $BeforeOffset = $oldChunkInfo['CONT']['offset'];
94                                         $AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
95                                 }
96                                 if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
97                                         if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
98
99                                                 rewind($fp_source);
100                                                 fwrite($fp_temp, fread($fp_source, $BeforeOffset));
101                                                 fwrite($fp_temp, $new_CONT_tag_data);
102                                                 fseek($fp_source, $AfterOffset);
103                                                 while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
104                                                         fwrite($fp_temp, $buffer, strlen($buffer));
105                                                 }
106                                                 fclose($fp_temp);
107
108                                                 if (copy($tempfilename, $this->filename)) {
109                                                         unlink($tempfilename);
110                                                         fclose($fp_source);
111                                                         return true;
112                                                 }
113                                                 unlink($tempfilename);
114                                                 $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
115
116                                         } else {
117                                                 $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
118                                         }
119                                 }
120                                 fclose($fp_source);
121                                 return false;
122
123                         }
124
125                 }
126                 $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
127                 return false;
128         }
129
130         public function GenerateRMFchunk(&$chunks) {
131                 $oldCONTexists = false;
132                 foreach ($chunks as $key => $chunk) {
133                         $chunkNameKeys[$chunk['name']] = $key;
134                         if ($chunk['name'] == 'CONT') {
135                                 $oldCONTexists = true;
136                         }
137                 }
138                 $newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
139
140                 $RMFchunk  = "\x00\x00"; // object version
141                 $RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
142                 $RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount,                                4);
143
144                 $RMFchunk  = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
145                 return $RMFchunk;
146         }
147
148         public function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
149                 $old_CONT_length = 0;
150                 $old_DATA_offset = 0;
151                 $old_INDX_offset = 0;
152                 foreach ($chunks as $key => $chunk) {
153                         $chunkNameKeys[$chunk['name']] = $key;
154                         if ($chunk['name'] == 'CONT') {
155                                 $old_CONT_length = $chunk['length'];
156                         } elseif ($chunk['name'] == 'DATA') {
157                                 if (!$old_DATA_offset) {
158                                         $old_DATA_offset = $chunk['offset'];
159                                 }
160                         } elseif ($chunk['name'] == 'INDX') {
161                                 if (!$old_INDX_offset) {
162                                         $old_INDX_offset = $chunk['offset'];
163                                 }
164                         }
165                 }
166                 $CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
167
168                 $PROPchunk  = "\x00\x00"; // object version
169                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'],    4);
170                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'],    4);
171                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
172                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
173                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'],     4);
174                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'],        4);
175                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'],         4);
176                 $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta),              4);
177                 $PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta),              4);
178                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'],     2);
179                 $PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'],       2);
180
181                 $PROPchunk  = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
182                 return $PROPchunk;
183         }
184
185         public function GenerateCONTchunk() {
186                 foreach ($this->tag_data as $key => $value) {
187                         // limit each value to 0xFFFF bytes
188                         $this->tag_data[$key] = substr($value, 0, 65535);
189                 }
190
191                 $CONTchunk  = "\x00\x00"; // object version
192
193                 $CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['title'])     ? strlen($this->tag_data['title'])     : 0), 2);
194                 $CONTchunk .= (!empty($this->tag_data['title'])     ? strlen($this->tag_data['title'])     : '');
195
196                 $CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['artist'])    ? strlen($this->tag_data['artist'])    : 0), 2);
197                 $CONTchunk .= (!empty($this->tag_data['artist'])    ? strlen($this->tag_data['artist'])    : '');
198
199                 $CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0), 2);
200                 $CONTchunk .= (!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '');
201
202                 $CONTchunk .= getid3_lib::BigEndian2String((!empty($this->tag_data['comment'])   ? strlen($this->tag_data['comment'])   : 0), 2);
203                 $CONTchunk .= (!empty($this->tag_data['comment'])   ? strlen($this->tag_data['comment'])   : '');
204
205                 if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
206                         $CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
207                 }
208
209                 $CONTchunk  = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
210
211                 return $CONTchunk;
212         }
213
214         public function RemoveReal() {
215                 // File MUST be writeable - CHMOD(646) at least
216                 if (is_writeable($this->filename) && is_file($this->filename) && ($fp_source = fopen($this->filename, 'r+b'))) {
217
218                         // Initialize getID3 engine
219                         $getID3 = new getID3;
220                         $OldThisFileInfo = $getID3->analyze($this->filename);
221                         if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
222                                 $this->errors[] = 'Cannot remove Real tags from old-style file format';
223                                 fclose($fp_source);
224                                 return false;
225                         }
226
227                         if (empty($OldThisFileInfo['real']['chunks'])) {
228                                 $this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
229                                 fclose($fp_source);
230                                 return false;
231                         }
232                         foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
233                                 $oldChunkInfo[$chunkarray['name']] = $chunkarray;
234                         }
235
236                         if (empty($oldChunkInfo['CONT'])) {
237                                 // no existing CONT chunk
238                                 fclose($fp_source);
239                                 return true;
240                         }
241
242                         $BeforeOffset = $oldChunkInfo['CONT']['offset'];
243                         $AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
244                         if ($tempfilename = tempnam(GETID3_TEMP_DIR, 'getID3')) {
245                                 if (is_writable($tempfilename) && is_file($tempfilename) && ($fp_temp = fopen($tempfilename, 'wb'))) {
246
247                                         rewind($fp_source);
248                                         fwrite($fp_temp, fread($fp_source, $BeforeOffset));
249                                         fseek($fp_source, $AfterOffset);
250                                         while ($buffer = fread($fp_source, $this->fread_buffer_size)) {
251                                                 fwrite($fp_temp, $buffer, strlen($buffer));
252                                         }
253                                         fclose($fp_temp);
254
255                                         if (copy($tempfilename, $this->filename)) {
256                                                 unlink($tempfilename);
257                                                 fclose($fp_source);
258                                                 return true;
259                                         }
260                                         unlink($tempfilename);
261                                         $this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.')';
262
263                                 } else {
264                                         $this->errors[] = 'Could not fopen("'.$tempfilename.'", "wb")';
265                                 }
266                         }
267                         fclose($fp_source);
268                         return false;
269                 }
270                 $this->errors[] = 'Could not fopen("'.$this->filename.'", "r+b")';
271                 return false;
272         }
273
274 }