author | ymh <ymh.work@gmail.com> |
Mon, 08 Sep 2025 19:44:41 +0200 | |
changeset 23 | 417f20492bf7 |
parent 21 | 48c4eec2b7e6 |
permissions | -rw-r--r-- |
0 | 1 |
<?php |
16 | 2 |
|
0 | 3 |
///////////////////////////////////////////////////////////////// |
4 |
/// getID3() by James Heinrich <info@getid3.org> // |
|
16 | 5 |
// available at https://github.com/JamesHeinrich/getID3 // |
6 |
// or https://www.getid3.org // |
|
7 |
// or http://getid3.sourceforge.net // |
|
8 |
// see readme.txt for more details // |
|
0 | 9 |
///////////////////////////////////////////////////////////////// |
10 |
// // |
|
11 |
// module.tag.id3v1.php // |
|
12 |
// module for analyzing ID3v1 tags // |
|
13 |
// dependencies: NONE // |
|
14 |
// /// |
|
15 |
///////////////////////////////////////////////////////////////// |
|
16 |
||
16 | 17 |
if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers |
18 |
exit; |
|
19 |
} |
|
0 | 20 |
|
21 |
class getid3_id3v1 extends getid3_handler |
|
22 |
{ |
|
16 | 23 |
/** |
24 |
* @return bool |
|
25 |
*/ |
|
0 | 26 |
public function Analyze() { |
27 |
$info = &$this->getid3->info; |
|
28 |
||
29 |
if (!getid3_lib::intValueSupported($info['filesize'])) { |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
30 |
$this->warning('Unable to check for ID3v1 because file is larger than '.round(PHP_INT_MAX / 1073741824).'GB'); |
0 | 31 |
return false; |
32 |
} |
|
33 |
||
19 | 34 |
if($info['filesize'] < 256) { |
35 |
$this->fseek(-128, SEEK_END); |
|
36 |
$preid3v1 = ''; |
|
37 |
$id3v1tag = $this->fread(128); |
|
38 |
} else { |
|
39 |
$this->fseek(-256, SEEK_END); |
|
40 |
$preid3v1 = $this->fread(128); |
|
41 |
$id3v1tag = $this->fread(128); |
|
42 |
} |
|
43 |
||
0 | 44 |
|
45 |
if (substr($id3v1tag, 0, 3) == 'TAG') { |
|
46 |
||
47 |
$info['avdataend'] = $info['filesize'] - 128; |
|
48 |
||
19 | 49 |
$ParsedID3v1 = array(); |
0 | 50 |
$ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30)); |
51 |
$ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30)); |
|
52 |
$ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30)); |
|
53 |
$ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4)); |
|
54 |
$ParsedID3v1['comment'] = substr($id3v1tag, 97, 30); // can't remove nulls yet, track detection depends on them |
|
55 |
$ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1)); |
|
56 |
||
57 |
// If second-last byte of comment field is null and last byte of comment field is non-null |
|
58 |
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number |
|
16 | 59 |
if (($id3v1tag[125] === "\x00") && ($id3v1tag[126] !== "\x00")) { |
60 |
$ParsedID3v1['track_number'] = ord(substr($ParsedID3v1['comment'], 29, 1)); |
|
61 |
$ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28); |
|
0 | 62 |
} |
63 |
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']); |
|
64 |
||
65 |
$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']); |
|
66 |
if (!empty($ParsedID3v1['genre'])) { |
|
67 |
unset($ParsedID3v1['genreid']); |
|
68 |
} |
|
21
48c4eec2b7e6
Add CLAUDE.md documentation and sync WordPress core files
ymh <ymh.work@gmail.com>
parents:
19
diff
changeset
|
69 |
if (empty($ParsedID3v1['genre']) || ($ParsedID3v1['genre'] == 'Unknown')) { |
0 | 70 |
unset($ParsedID3v1['genre']); |
71 |
} |
|
72 |
||
73 |
foreach ($ParsedID3v1 as $key => $value) { |
|
74 |
$ParsedID3v1['comments'][$key][0] = $value; |
|
75 |
} |
|
16 | 76 |
$ID3v1encoding = $this->getid3->encoding_id3v1; |
77 |
if ($this->getid3->encoding_id3v1_autodetect) { |
|
78 |
// ID3v1 encoding detection hack START |
|
79 |
// ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets |
|
80 |
// Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess |
|
81 |
foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) { |
|
82 |
foreach ($valuearray as $key => $value) { |
|
83 |
if (preg_match('#^[\\x00-\\x40\\x80-\\xFF]+$#', $value) && !ctype_digit((string) $value)) { // check for strings with only characters above chr(128) and punctuation/numbers, but not just numeric strings (e.g. track numbers or years) |
|
84 |
foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) { |
|
85 |
if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) { |
|
86 |
$ID3v1encoding = $id3v1_bad_encoding; |
|
87 |
$this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key); |
|
88 |
break 3; |
|
89 |
} elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) { |
|
90 |
$ID3v1encoding = $id3v1_bad_encoding; |
|
91 |
$this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key); |
|
92 |
break 3; |
|
93 |
} |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
94 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
95 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
96 |
} |
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
97 |
} |
16 | 98 |
// ID3v1 encoding detection hack END |
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
99 |
} |
0 | 100 |
|
101 |
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces |
|
102 |
$GoodFormatID3v1tag = $this->GenerateID3v1Tag( |
|
103 |
$ParsedID3v1['title'], |
|
104 |
$ParsedID3v1['artist'], |
|
105 |
$ParsedID3v1['album'], |
|
106 |
$ParsedID3v1['year'], |
|
107 |
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false), |
|
108 |
$ParsedID3v1['comment'], |
|
16 | 109 |
(!empty($ParsedID3v1['track_number']) ? $ParsedID3v1['track_number'] : '')); |
0 | 110 |
$ParsedID3v1['padding_valid'] = true; |
111 |
if ($id3v1tag !== $GoodFormatID3v1tag) { |
|
112 |
$ParsedID3v1['padding_valid'] = false; |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
113 |
$this->warning('Some ID3v1 fields do not use NULL characters for padding'); |
0 | 114 |
} |
115 |
||
116 |
$ParsedID3v1['tag_offset_end'] = $info['filesize']; |
|
117 |
$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128; |
|
118 |
||
119 |
$info['id3v1'] = $ParsedID3v1; |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
120 |
$info['id3v1']['encoding'] = $ID3v1encoding; |
0 | 121 |
} |
122 |
||
123 |
if (substr($preid3v1, 0, 3) == 'TAG') { |
|
124 |
// The way iTunes handles tags is, well, brain-damaged. |
|
125 |
// It completely ignores v1 if ID3v2 is present. |
|
126 |
// This goes as far as adding a new v1 tag *even if there already is one* |
|
127 |
||
128 |
// A suspected double-ID3v1 tag has been detected, but it could be that |
|
129 |
// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag |
|
130 |
if (substr($preid3v1, 96, 8) == 'APETAGEX') { |
|
131 |
// an APE tag footer was found before the last ID3v1, assume false "TAG" synch |
|
132 |
} elseif (substr($preid3v1, 119, 6) == 'LYRICS') { |
|
133 |
// a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch |
|
134 |
} else { |
|
135 |
// APE and Lyrics3 footers not found - assume double ID3v1 |
|
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
136 |
$this->warning('Duplicate ID3v1 tag detected - this has been known to happen with iTunes'); |
0 | 137 |
$info['avdataend'] -= 128; |
138 |
} |
|
139 |
} |
|
140 |
||
141 |
return true; |
|
142 |
} |
|
143 |
||
16 | 144 |
/** |
145 |
* @param string $str |
|
146 |
* |
|
147 |
* @return string |
|
148 |
*/ |
|
0 | 149 |
public static function cutfield($str) { |
150 |
return trim(substr($str, 0, strcspn($str, "\x00"))); |
|
151 |
} |
|
152 |
||
16 | 153 |
/** |
154 |
* @param bool $allowSCMPXextended |
|
155 |
* |
|
156 |
* @return string[] |
|
157 |
*/ |
|
0 | 158 |
public static function ArrayOfGenres($allowSCMPXextended=false) { |
159 |
static $GenreLookup = array( |
|
160 |
0 => 'Blues', |
|
161 |
1 => 'Classic Rock', |
|
162 |
2 => 'Country', |
|
163 |
3 => 'Dance', |
|
164 |
4 => 'Disco', |
|
165 |
5 => 'Funk', |
|
166 |
6 => 'Grunge', |
|
167 |
7 => 'Hip-Hop', |
|
168 |
8 => 'Jazz', |
|
169 |
9 => 'Metal', |
|
170 |
10 => 'New Age', |
|
171 |
11 => 'Oldies', |
|
172 |
12 => 'Other', |
|
173 |
13 => 'Pop', |
|
174 |
14 => 'R&B', |
|
175 |
15 => 'Rap', |
|
176 |
16 => 'Reggae', |
|
177 |
17 => 'Rock', |
|
178 |
18 => 'Techno', |
|
179 |
19 => 'Industrial', |
|
180 |
20 => 'Alternative', |
|
181 |
21 => 'Ska', |
|
182 |
22 => 'Death Metal', |
|
183 |
23 => 'Pranks', |
|
184 |
24 => 'Soundtrack', |
|
185 |
25 => 'Euro-Techno', |
|
186 |
26 => 'Ambient', |
|
187 |
27 => 'Trip-Hop', |
|
188 |
28 => 'Vocal', |
|
189 |
29 => 'Jazz+Funk', |
|
190 |
30 => 'Fusion', |
|
191 |
31 => 'Trance', |
|
192 |
32 => 'Classical', |
|
193 |
33 => 'Instrumental', |
|
194 |
34 => 'Acid', |
|
195 |
35 => 'House', |
|
196 |
36 => 'Game', |
|
197 |
37 => 'Sound Clip', |
|
198 |
38 => 'Gospel', |
|
199 |
39 => 'Noise', |
|
200 |
40 => 'Alt. Rock', |
|
201 |
41 => 'Bass', |
|
202 |
42 => 'Soul', |
|
203 |
43 => 'Punk', |
|
204 |
44 => 'Space', |
|
205 |
45 => 'Meditative', |
|
206 |
46 => 'Instrumental Pop', |
|
207 |
47 => 'Instrumental Rock', |
|
208 |
48 => 'Ethnic', |
|
209 |
49 => 'Gothic', |
|
210 |
50 => 'Darkwave', |
|
211 |
51 => 'Techno-Industrial', |
|
212 |
52 => 'Electronic', |
|
213 |
53 => 'Pop-Folk', |
|
214 |
54 => 'Eurodance', |
|
215 |
55 => 'Dream', |
|
216 |
56 => 'Southern Rock', |
|
217 |
57 => 'Comedy', |
|
218 |
58 => 'Cult', |
|
219 |
59 => 'Gangsta Rap', |
|
220 |
60 => 'Top 40', |
|
221 |
61 => 'Christian Rap', |
|
222 |
62 => 'Pop/Funk', |
|
223 |
63 => 'Jungle', |
|
224 |
64 => 'Native American', |
|
225 |
65 => 'Cabaret', |
|
226 |
66 => 'New Wave', |
|
227 |
67 => 'Psychedelic', |
|
228 |
68 => 'Rave', |
|
229 |
69 => 'Showtunes', |
|
230 |
70 => 'Trailer', |
|
231 |
71 => 'Lo-Fi', |
|
232 |
72 => 'Tribal', |
|
233 |
73 => 'Acid Punk', |
|
234 |
74 => 'Acid Jazz', |
|
235 |
75 => 'Polka', |
|
236 |
76 => 'Retro', |
|
237 |
77 => 'Musical', |
|
238 |
78 => 'Rock & Roll', |
|
239 |
79 => 'Hard Rock', |
|
240 |
80 => 'Folk', |
|
241 |
81 => 'Folk/Rock', |
|
242 |
82 => 'National Folk', |
|
243 |
83 => 'Swing', |
|
244 |
84 => 'Fast-Fusion', |
|
245 |
85 => 'Bebob', |
|
246 |
86 => 'Latin', |
|
247 |
87 => 'Revival', |
|
248 |
88 => 'Celtic', |
|
249 |
89 => 'Bluegrass', |
|
250 |
90 => 'Avantgarde', |
|
251 |
91 => 'Gothic Rock', |
|
252 |
92 => 'Progressive Rock', |
|
253 |
93 => 'Psychedelic Rock', |
|
254 |
94 => 'Symphonic Rock', |
|
255 |
95 => 'Slow Rock', |
|
256 |
96 => 'Big Band', |
|
257 |
97 => 'Chorus', |
|
258 |
98 => 'Easy Listening', |
|
259 |
99 => 'Acoustic', |
|
260 |
100 => 'Humour', |
|
261 |
101 => 'Speech', |
|
262 |
102 => 'Chanson', |
|
263 |
103 => 'Opera', |
|
264 |
104 => 'Chamber Music', |
|
265 |
105 => 'Sonata', |
|
266 |
106 => 'Symphony', |
|
267 |
107 => 'Booty Bass', |
|
268 |
108 => 'Primus', |
|
269 |
109 => 'Porn Groove', |
|
270 |
110 => 'Satire', |
|
271 |
111 => 'Slow Jam', |
|
272 |
112 => 'Club', |
|
273 |
113 => 'Tango', |
|
274 |
114 => 'Samba', |
|
275 |
115 => 'Folklore', |
|
276 |
116 => 'Ballad', |
|
277 |
117 => 'Power Ballad', |
|
278 |
118 => 'Rhythmic Soul', |
|
279 |
119 => 'Freestyle', |
|
280 |
120 => 'Duet', |
|
281 |
121 => 'Punk Rock', |
|
282 |
122 => 'Drum Solo', |
|
283 |
123 => 'A Cappella', |
|
284 |
124 => 'Euro-House', |
|
285 |
125 => 'Dance Hall', |
|
286 |
126 => 'Goa', |
|
287 |
127 => 'Drum & Bass', |
|
288 |
128 => 'Club-House', |
|
289 |
129 => 'Hardcore', |
|
290 |
130 => 'Terror', |
|
291 |
131 => 'Indie', |
|
292 |
132 => 'BritPop', |
|
293 |
133 => 'Negerpunk', |
|
294 |
134 => 'Polsk Punk', |
|
295 |
135 => 'Beat', |
|
296 |
136 => 'Christian Gangsta Rap', |
|
297 |
137 => 'Heavy Metal', |
|
298 |
138 => 'Black Metal', |
|
299 |
139 => 'Crossover', |
|
300 |
140 => 'Contemporary Christian', |
|
301 |
141 => 'Christian Rock', |
|
302 |
142 => 'Merengue', |
|
303 |
143 => 'Salsa', |
|
304 |
144 => 'Thrash Metal', |
|
305 |
145 => 'Anime', |
|
306 |
146 => 'JPop', |
|
307 |
147 => 'Synthpop', |
|
19 | 308 |
148 => 'Abstract', |
309 |
149 => 'Art Rock', |
|
310 |
150 => 'Baroque', |
|
311 |
151 => 'Bhangra', |
|
312 |
152 => 'Big Beat', |
|
313 |
153 => 'Breakbeat', |
|
314 |
154 => 'Chillout', |
|
315 |
155 => 'Downtempo', |
|
316 |
156 => 'Dub', |
|
317 |
157 => 'EBM', |
|
318 |
158 => 'Eclectic', |
|
319 |
159 => 'Electro', |
|
320 |
160 => 'Electroclash', |
|
321 |
161 => 'Emo', |
|
322 |
162 => 'Experimental', |
|
323 |
163 => 'Garage', |
|
324 |
164 => 'Global', |
|
325 |
165 => 'IDM', |
|
326 |
166 => 'Illbient', |
|
327 |
167 => 'Industro-Goth', |
|
328 |
168 => 'Jam Band', |
|
329 |
169 => 'Krautrock', |
|
330 |
170 => 'Leftfield', |
|
331 |
171 => 'Lounge', |
|
332 |
172 => 'Math Rock', |
|
333 |
173 => 'New Romantic', |
|
334 |
174 => 'Nu-Breakz', |
|
335 |
175 => 'Post-Punk', |
|
336 |
176 => 'Post-Rock', |
|
337 |
177 => 'Psytrance', |
|
338 |
178 => 'Shoegaze', |
|
339 |
179 => 'Space Rock', |
|
340 |
180 => 'Trop Rock', |
|
341 |
181 => 'World Music', |
|
342 |
182 => 'Neoclassical', |
|
343 |
183 => 'Audiobook', |
|
344 |
184 => 'Audio Theatre', |
|
345 |
185 => 'Neue Deutsche Welle', |
|
346 |
186 => 'Podcast', |
|
347 |
187 => 'Indie-Rock', |
|
348 |
188 => 'G-Funk', |
|
349 |
189 => 'Dubstep', |
|
350 |
190 => 'Garage Rock', |
|
351 |
191 => 'Psybient', |
|
0 | 352 |
|
353 |
255 => 'Unknown', |
|
354 |
||
355 |
'CR' => 'Cover', |
|
356 |
'RX' => 'Remix' |
|
357 |
); |
|
358 |
||
359 |
static $GenreLookupSCMPX = array(); |
|
360 |
if ($allowSCMPXextended && empty($GenreLookupSCMPX)) { |
|
361 |
$GenreLookupSCMPX = $GenreLookup; |
|
362 |
// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended |
|
363 |
// Extended ID3v1 genres invented by SCMPX |
|
364 |
// Note that 255 "Japanese Anime" conflicts with standard "Unknown" |
|
365 |
$GenreLookupSCMPX[240] = 'Sacred'; |
|
366 |
$GenreLookupSCMPX[241] = 'Northern Europe'; |
|
367 |
$GenreLookupSCMPX[242] = 'Irish & Scottish'; |
|
368 |
$GenreLookupSCMPX[243] = 'Scotland'; |
|
369 |
$GenreLookupSCMPX[244] = 'Ethnic Europe'; |
|
370 |
$GenreLookupSCMPX[245] = 'Enka'; |
|
371 |
$GenreLookupSCMPX[246] = 'Children\'s Song'; |
|
372 |
$GenreLookupSCMPX[247] = 'Japanese Sky'; |
|
373 |
$GenreLookupSCMPX[248] = 'Japanese Heavy Rock'; |
|
374 |
$GenreLookupSCMPX[249] = 'Japanese Doom Rock'; |
|
375 |
$GenreLookupSCMPX[250] = 'Japanese J-POP'; |
|
376 |
$GenreLookupSCMPX[251] = 'Japanese Seiyu'; |
|
377 |
$GenreLookupSCMPX[252] = 'Japanese Ambient Techno'; |
|
378 |
$GenreLookupSCMPX[253] = 'Japanese Moemoe'; |
|
379 |
$GenreLookupSCMPX[254] = 'Japanese Tokusatsu'; |
|
380 |
//$GenreLookupSCMPX[255] = 'Japanese Anime'; |
|
381 |
} |
|
382 |
||
383 |
return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup); |
|
384 |
} |
|
385 |
||
16 | 386 |
/** |
387 |
* @param string $genreid |
|
388 |
* @param bool $allowSCMPXextended |
|
389 |
* |
|
390 |
* @return string|false |
|
391 |
*/ |
|
0 | 392 |
public static function LookupGenreName($genreid, $allowSCMPXextended=true) { |
393 |
switch ($genreid) { |
|
394 |
case 'RX': |
|
395 |
case 'CR': |
|
396 |
break; |
|
397 |
default: |
|
398 |
if (!is_numeric($genreid)) { |
|
399 |
return false; |
|
400 |
} |
|
401 |
$genreid = intval($genreid); // to handle 3 or '3' or '03' |
|
402 |
break; |
|
403 |
} |
|
404 |
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended); |
|
405 |
return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false); |
|
406 |
} |
|
407 |
||
16 | 408 |
/** |
409 |
* @param string $genre |
|
410 |
* @param bool $allowSCMPXextended |
|
411 |
* |
|
412 |
* @return string|false |
|
413 |
*/ |
|
0 | 414 |
public static function LookupGenreID($genre, $allowSCMPXextended=false) { |
415 |
$GenreLookup = self::ArrayOfGenres($allowSCMPXextended); |
|
416 |
$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre)); |
|
417 |
foreach ($GenreLookup as $key => $value) { |
|
418 |
if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) { |
|
419 |
return $key; |
|
420 |
} |
|
421 |
} |
|
422 |
return false; |
|
423 |
} |
|
424 |
||
16 | 425 |
/** |
426 |
* @param string $OriginalGenre |
|
427 |
* |
|
428 |
* @return string|false |
|
429 |
*/ |
|
0 | 430 |
public static function StandardiseID3v1GenreName($OriginalGenre) { |
431 |
if (($GenreID = self::LookupGenreID($OriginalGenre)) !== false) { |
|
432 |
return self::LookupGenreName($GenreID); |
|
433 |
} |
|
434 |
return $OriginalGenre; |
|
435 |
} |
|
436 |
||
16 | 437 |
/** |
438 |
* @param string $title |
|
439 |
* @param string $artist |
|
440 |
* @param string $album |
|
441 |
* @param string $year |
|
442 |
* @param int $genreid |
|
443 |
* @param string $comment |
|
444 |
* @param int|string $track |
|
445 |
* |
|
446 |
* @return string |
|
447 |
*/ |
|
0 | 448 |
public static function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') { |
449 |
$ID3v1Tag = 'TAG'; |
|
450 |
$ID3v1Tag .= str_pad(trim(substr($title, 0, 30)), 30, "\x00", STR_PAD_RIGHT); |
|
451 |
$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT); |
|
452 |
$ID3v1Tag .= str_pad(trim(substr($album, 0, 30)), 30, "\x00", STR_PAD_RIGHT); |
|
453 |
$ID3v1Tag .= str_pad(trim(substr($year, 0, 4)), 4, "\x00", STR_PAD_LEFT); |
|
454 |
if (!empty($track) && ($track > 0) && ($track <= 255)) { |
|
455 |
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT); |
|
456 |
$ID3v1Tag .= "\x00"; |
|
457 |
if (gettype($track) == 'string') { |
|
458 |
$track = (int) $track; |
|
459 |
} |
|
460 |
$ID3v1Tag .= chr($track); |
|
461 |
} else { |
|
462 |
$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT); |
|
463 |
} |
|
464 |
if (($genreid < 0) || ($genreid > 147)) { |
|
465 |
$genreid = 255; // 'unknown' genre |
|
466 |
} |
|
467 |
switch (gettype($genreid)) { |
|
468 |
case 'string': |
|
469 |
case 'integer': |
|
470 |
$ID3v1Tag .= chr(intval($genreid)); |
|
471 |
break; |
|
472 |
default: |
|
473 |
$ID3v1Tag .= chr(255); // 'unknown' genre |
|
474 |
break; |
|
475 |
} |
|
476 |
||
477 |
return $ID3v1Tag; |
|
478 |
} |
|
479 |
||
7
cf61fcea0001
resynchronize code repo with production
ymh <ymh.work@gmail.com>
parents:
5
diff
changeset
|
480 |
} |