]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/james-heinrich/getid3/getid3/write.apetag.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / james-heinrich / getid3 / getid3 / write.apetag.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.apetag.php                                            //
12 // module for writing APE tags                                 //
13 // dependencies: module.tag.apetag.php                         //
14 //                                                            ///
15 /////////////////////////////////////////////////////////////////
16
17
18 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
19
20 class getid3_write_apetag
21 {
22
23         public $filename;
24         public $tag_data;
25         public $always_preserve_replaygain = true;    // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
26         public $warnings                   = array(); // any non-critical errors will be stored here
27         public $errors                     = array(); // any critical errors will be stored here
28
29         public function __construct() {
30                 return true;
31         }
32
33         public function WriteAPEtag() {
34                 // NOTE: All data passed to this function must be UTF-8 format
35
36                 $getID3 = new getID3;
37                 $ThisFileInfo = $getID3->analyze($this->filename);
38
39                 if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
40                         if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
41                                 // Current APE tag between Lyrics3 and ID3v1/EOF
42                                 // This break Lyrics3 functionality
43                                 if (!$this->DeleteAPEtag()) {
44                                         return false;
45                                 }
46                                 $ThisFileInfo = $getID3->analyze($this->filename);
47                         }
48                 }
49
50                 if ($this->always_preserve_replaygain) {
51                         $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
52                         foreach ($ReplayGainTagsToPreserve as $rg_key) {
53                                 if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
54                                         $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
55                                 }
56                         }
57                 }
58
59                 if ($APEtag = $this->GenerateAPEtag()) {
60                         if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
61                                 $oldignoreuserabort = ignore_user_abort(true);
62                                 flock($fp, LOCK_EX);
63
64                                 $PostAPEdataOffset = $ThisFileInfo['avdataend'];
65                                 if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
66                                         $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
67                                 }
68                                 if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
69                                         $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
70                                 }
71                                 fseek($fp, $PostAPEdataOffset);
72                                 $PostAPEdata = '';
73                                 if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
74                                         $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
75                                 }
76
77                                 fseek($fp, $PostAPEdataOffset);
78                                 if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
79                                         fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
80                                 }
81                                 ftruncate($fp, ftell($fp));
82                                 fwrite($fp, $APEtag, strlen($APEtag));
83                                 if (!empty($PostAPEdata)) {
84                                         fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
85                                 }
86                                 flock($fp, LOCK_UN);
87                                 fclose($fp);
88                                 ignore_user_abort($oldignoreuserabort);
89                                 return true;
90                         }
91                 }
92                 return false;
93         }
94
95         public function DeleteAPEtag() {
96                 $getID3 = new getID3;
97                 $ThisFileInfo = $getID3->analyze($this->filename);
98                 if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
99                         if (is_writable($this->filename) && is_file($this->filename) && ($fp = fopen($this->filename, 'a+b'))) {
100
101                                 flock($fp, LOCK_EX);
102                                 $oldignoreuserabort = ignore_user_abort(true);
103
104                                 fseek($fp, $ThisFileInfo['ape']['tag_offset_end']);
105                                 $DataAfterAPE = '';
106                                 if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
107                                         $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
108                                 }
109
110                                 ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
111                                 fseek($fp, $ThisFileInfo['ape']['tag_offset_start']);
112
113                                 if (!empty($DataAfterAPE)) {
114                                         fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
115                                 }
116
117                                 flock($fp, LOCK_UN);
118                                 fclose($fp);
119                                 ignore_user_abort($oldignoreuserabort);
120
121                                 return true;
122                         }
123                         return false;
124                 }
125                 return true;
126         }
127
128
129         public function GenerateAPEtag() {
130                 // NOTE: All data passed to this function must be UTF-8 format
131
132                 $items = array();
133                 if (!is_array($this->tag_data)) {
134                         return false;
135                 }
136                 foreach ($this->tag_data as $key => $arrayofvalues) {
137                         if (!is_array($arrayofvalues)) {
138                                 return false;
139                         }
140
141                         $valuestring = '';
142                         foreach ($arrayofvalues as $value) {
143                                 $valuestring .= str_replace("\x00", '', $value)."\x00";
144                         }
145                         $valuestring = rtrim($valuestring, "\x00");
146
147                         // Length of the assigned value in bytes
148                         $tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
149
150                         //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
151                         $tagitem .= "\x00\x00\x00\x00";
152
153                         $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
154                         $tagitem .= $valuestring;
155
156                         $items[] = $tagitem;
157
158                 }
159
160                 return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
161         }
162
163         public function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
164                 $tagdatalength = 0;
165                 foreach ($items as $itemdata) {
166                         $tagdatalength += strlen($itemdata);
167                 }
168
169                 $APEheader  = 'APETAGEX';
170                 $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
171                 $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
172                 $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
173                 $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
174                 $APEheader .= str_repeat("\x00", 8);
175
176                 return $APEheader;
177         }
178
179         public function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
180                 $APEtagFlags = array_fill(0, 4, 0);
181                 if ($header) {
182                         $APEtagFlags[0] |= 0x80; // Tag contains a header
183                 }
184                 if (!$footer) {
185                         $APEtagFlags[0] |= 0x40; // Tag contains no footer
186                 }
187                 if ($isheader) {
188                         $APEtagFlags[0] |= 0x20; // This is the header, not the footer
189                 }
190
191                 // 0: Item contains text information coded in UTF-8
192                 // 1: Item contains binary information °)
193                 // 2: Item is a locator of external stored information °°)
194                 // 3: reserved
195                 $APEtagFlags[3] |= ($encodingid << 1);
196
197                 if ($readonly) {
198                         $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
199                 }
200
201                 return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
202         }
203
204         public function CleanAPEtagItemKey($itemkey) {
205                 $itemkey = preg_replace("#[^\x20-\x7E]#i", '', $itemkey);
206
207                 // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
208                 switch (strtoupper($itemkey)) {
209                         case 'EAN/UPC':
210                         case 'ISBN':
211                         case 'LC':
212                         case 'ISRC':
213                                 $itemkey = strtoupper($itemkey);
214                                 break;
215
216                         default:
217                                 $itemkey = ucwords($itemkey);
218                                 break;
219                 }
220                 return $itemkey;
221
222         }
223
224 }