[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ID3/ -> module.tag.id3v1.php (source)

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


Generated: Sat Jul 4 01:00:03 2020 Cross-referenced by PHPXref 0.7.1