Пример #1
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     if (!Utils::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for APEtags because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     $id3v1tagsize = 128;
     $apetagheadersize = 32;
     $lyrics3tagsize = 10;
     if ($this->overrideendoffset == 0) {
         $this->fseek(0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
         $APEfooterID3v1 = $this->fread($id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
         //if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
         if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
             // APE tag found before ID3v1
             $info['ape']['tag_offset_end'] = $info['filesize'] - $id3v1tagsize;
             //} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
         } elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
             // APE tag found, no ID3v1
             $info['ape']['tag_offset_end'] = $info['filesize'];
         }
     } else {
         $this->fseek($this->overrideendoffset - $apetagheadersize);
         if ($this->fread(8) == 'APETAGEX') {
             $info['ape']['tag_offset_end'] = $this->overrideendoffset;
         }
     }
     if (!isset($info['ape']['tag_offset_end'])) {
         // APE tag not found
         unset($info['ape']);
         return false;
     }
     // shortcut
     $thisfile_ape =& $info['ape'];
     $this->fseek($thisfile_ape['tag_offset_end'] - $apetagheadersize);
     $APEfooterData = $this->fread(32);
     if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
         $info['error'][] = 'Error parsing APE footer at offset ' . $thisfile_ape['tag_offset_end'];
         return false;
     }
     if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
         $this->fseek($thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize);
         $thisfile_ape['tag_offset_start'] = $this->ftell();
         $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
     } else {
         $thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
         $this->fseek($thisfile_ape['tag_offset_start']);
         $APEtagData = $this->fread($thisfile_ape['footer']['raw']['tagsize']);
     }
     $info['avdataend'] = $thisfile_ape['tag_offset_start'];
     if (isset($info['id3v1']['tag_offset_start']) && $info['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end']) {
         $info['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
         unset($info['id3v1']);
         foreach ($info['warning'] as $key => $value) {
             if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
                 unset($info['warning'][$key]);
                 sort($info['warning']);
                 break;
             }
         }
     }
     $offset = 0;
     if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
         if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
             $offset += $apetagheadersize;
         } else {
             $info['error'][] = 'Error parsing APE header at offset ' . $thisfile_ape['tag_offset_start'];
             return false;
         }
     }
     // shortcut
     $info['replay_gain'] = array();
     $thisfile_replaygain =& $info['replay_gain'];
     for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
         $value_size = Utils::LittleEndian2Int(substr($APEtagData, $offset, 4));
         $offset += 4;
         $item_flags = Utils::LittleEndian2Int(substr($APEtagData, $offset, 4));
         $offset += 4;
         if (strstr(substr($APEtagData, $offset), "") === false) {
             $info['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #' . $i . ' and value. ItemKey starts ' . $offset . ' bytes into the APE tag, at file offset ' . ($thisfile_ape['tag_offset_start'] + $offset);
             return false;
         }
         $ItemKeyLength = strpos($APEtagData, "", $offset) - $offset;
         $item_key = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
         // shortcut
         $thisfile_ape['items'][$item_key] = array();
         $thisfile_ape_items_current =& $thisfile_ape['items'][$item_key];
         $thisfile_ape_items_current['offset'] = $thisfile_ape['tag_offset_start'] + $offset;
         $offset += $ItemKeyLength + 1;
         // skip 0x00 terminator
         $thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
         $offset += $value_size;
         $thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
         switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
             case 0:
                 // UTF-8
             // UTF-8
             case 2:
                 // Locator (URL, filename, etc), UTF-8 encoded
                 $thisfile_ape_items_current['data'] = explode("", $thisfile_ape_items_current['data']);
                 break;
             case 1:
                 // binary data
             // binary data
             default:
                 break;
         }
         switch (strtolower($item_key)) {
             // http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
             case 'replaygain_track_gain':
                 if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
                     $thisfile_replaygain['track']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                     // float casting will see "0,95" as zero!
                     $thisfile_replaygain['track']['originator'] = 'unspecified';
                 } else {
                     $info['warning'][] = 'MP3gainTrackGain value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'replaygain_track_peak':
                 if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
                     $thisfile_replaygain['track']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                     // float casting will see "0,95" as zero!
                     $thisfile_replaygain['track']['originator'] = 'unspecified';
                     if ($thisfile_replaygain['track']['peak'] <= 0) {
                         $info['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: ' . $thisfile_replaygain['track']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")';
                     }
                 } else {
                     $info['warning'][] = 'MP3gainTrackPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'replaygain_album_gain':
                 if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
                     $thisfile_replaygain['album']['adjustment'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                     // float casting will see "0,95" as zero!
                     $thisfile_replaygain['album']['originator'] = 'unspecified';
                 } else {
                     $info['warning'][] = 'MP3gainAlbumGain value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'replaygain_album_peak':
                 if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
                     $thisfile_replaygain['album']['peak'] = (double) str_replace(',', '.', $thisfile_ape_items_current['data'][0]);
                     // float casting will see "0,95" as zero!
                     $thisfile_replaygain['album']['originator'] = 'unspecified';
                     if ($thisfile_replaygain['album']['peak'] <= 0) {
                         $info['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: ' . $thisfile_replaygain['album']['peak'] . ' (original value = "' . $thisfile_ape_items_current['data'][0] . '")';
                     }
                 } else {
                     $info['warning'][] = 'MP3gainAlbumPeak value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'mp3gain_undo':
                 if (preg_match('#^[\\-\\+][0-9]{3},[\\-\\+][0-9]{3},[NW]$#', $thisfile_ape_items_current['data'][0])) {
                     list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
                     $thisfile_replaygain['mp3gain']['undo_left'] = intval($mp3gain_undo_left);
                     $thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
                     $thisfile_replaygain['mp3gain']['undo_wrap'] = $mp3gain_undo_wrap == 'Y' ? true : false;
                 } else {
                     $info['warning'][] = 'MP3gainUndo value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'mp3gain_minmax':
                 if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
                     list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
                     $thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
                     $thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
                 } else {
                     $info['warning'][] = 'MP3gainMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'mp3gain_album_minmax':
                 if (preg_match('#^[0-9]{3},[0-9]{3}$#', $thisfile_ape_items_current['data'][0])) {
                     list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
                     $thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
                     $thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
                 } else {
                     $info['warning'][] = 'MP3gainAlbumMinMax value in APEtag appears invalid: "' . $thisfile_ape_items_current['data'][0] . '"';
                 }
                 break;
             case 'tracknumber':
                 if (is_array($thisfile_ape_items_current['data'])) {
                     foreach ($thisfile_ape_items_current['data'] as $comment) {
                         $thisfile_ape['comments']['track'][] = $comment;
                     }
                 }
                 break;
             case 'cover art (artist)':
             case 'cover art (back)':
             case 'cover art (band logo)':
             case 'cover art (band)':
             case 'cover art (colored fish)':
             case 'cover art (composer)':
             case 'cover art (conductor)':
             case 'cover art (front)':
             case 'cover art (icon)':
             case 'cover art (illustration)':
             case 'cover art (lead)':
             case 'cover art (leaflet)':
             case 'cover art (lyricist)':
             case 'cover art (media)':
             case 'cover art (movie scene)':
             case 'cover art (other icon)':
             case 'cover art (other)':
             case 'cover art (performance)':
             case 'cover art (publisher logo)':
             case 'cover art (recording)':
             case 'cover art (studio)':
                 // list of possible cover arts from http://taglib-sharp.sourcearchive.com/documentation/2.0.3.0-2/Ape_2Tag_8cs-source.html
                 if (is_array($thisfile_ape_items_current['data'])) {
                     $info['warning'][] = 'APEtag "' . $item_key . '" should be flagged as Binary data, but was incorrectly flagged as UTF-8';
                     $thisfile_ape_items_current['data'] = implode("", $thisfile_ape_items_current['data']);
                 }
                 list($thisfile_ape_items_current['filename'], $thisfile_ape_items_current['data']) = explode("", $thisfile_ape_items_current['data'], 2);
                 $thisfile_ape_items_current['data_offset'] = $thisfile_ape_items_current['offset'] + strlen($thisfile_ape_items_current['filename'] . "");
                 $thisfile_ape_items_current['data_length'] = strlen($thisfile_ape_items_current['data']);
                 do {
                     $thisfile_ape_items_current['image_mime'] = '';
                     $imageinfo = array();
                     $imagechunkcheck = Utils::GetDataImageSize($thisfile_ape_items_current['data'], $imageinfo);
                     if ($imagechunkcheck === false || !isset($imagechunkcheck[2])) {
                         $info['warning'][] = 'APEtag "' . $item_key . '" contains invalid image data';
                         break;
                     }
                     $thisfile_ape_items_current['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
                     if ($this->inline_attachments === false) {
                         // skip entirely
                         unset($thisfile_ape_items_current['data']);
                         break;
                     }
                     if ($this->inline_attachments === true) {
                         // great
                     } elseif (is_int($this->inline_attachments)) {
                         if ($this->inline_attachments < $thisfile_ape_items_current['data_length']) {
                             // too big, skip
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' is too large to process inline (' . number_format($thisfile_ape_items_current['data_length']) . ' bytes)';
                             unset($thisfile_ape_items_current['data']);
                             break;
                         }
                     } elseif (is_string($this->inline_attachments)) {
                         $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
                         if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
                             // cannot write, skip
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)';
                             unset($thisfile_ape_items_current['data']);
                             break;
                         }
                     }
                     // if we get this far, must be OK
                     if (is_string($this->inline_attachments)) {
                         $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $thisfile_ape_items_current['data_offset'];
                         if (!file_exists($destination_filename) || is_writable($destination_filename)) {
                             file_put_contents($destination_filename, $thisfile_ape_items_current['data']);
                         } else {
                             $info['warning'][] = 'attachment at ' . $thisfile_ape_items_current['offset'] . ' cannot be saved to "' . $destination_filename . '" (not writable)';
                         }
                         $thisfile_ape_items_current['data_filename'] = $destination_filename;
                         unset($thisfile_ape_items_current['data']);
                     } else {
                         if (!isset($info['ape']['comments']['picture'])) {
                             $info['ape']['comments']['picture'] = array();
                         }
                         $comments_picture_data = array();
                         foreach (array('data', 'image_mime', 'image_width', 'image_height', 'imagetype', 'picturetype', 'description', 'datalength') as $picture_key) {
                             if (isset($thisfile_ape_items_current[$picture_key])) {
                                 $comments_picture_data[$picture_key] = $thisfile_ape_items_current[$picture_key];
                             }
                         }
                         $info['ape']['comments']['picture'][] = $comments_picture_data;
                         unset($comments_picture_data);
                     }
                 } while (false);
                 break;
             default:
                 if (is_array($thisfile_ape_items_current['data'])) {
                     foreach ($thisfile_ape_items_current['data'] as $comment) {
                         $thisfile_ape['comments'][strtolower($item_key)][] = $comment;
                     }
                 }
                 break;
         }
     }
     if (empty($thisfile_replaygain)) {
         unset($info['replay_gain']);
     }
     return true;
 }
Пример #2
0
 public function ASF_WMpicture(&$data)
 {
     //typedef struct _WMPicture{
     //  LPWSTR  pwszMIMEType;
     //  BYTE  bPictureType;
     //  LPWSTR  pwszDescription;
     //  DWORD  dwDataLen;
     //  BYTE*  pbData;
     //} WM_PICTURE;
     $WMpicture = array();
     $offset = 0;
     $WMpicture['image_type_id'] = Utils::LittleEndian2Int(substr($data, $offset, 1));
     $offset += 1;
     $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']);
     $WMpicture['image_size'] = Utils::LittleEndian2Int(substr($data, $offset, 4));
     $offset += 4;
     $WMpicture['image_mime'] = '';
     do {
         $next_byte_pair = substr($data, $offset, 2);
         $offset += 2;
         $WMpicture['image_mime'] .= $next_byte_pair;
     } while ($next_byte_pair !== "");
     $WMpicture['image_description'] = '';
     do {
         $next_byte_pair = substr($data, $offset, 2);
         $offset += 2;
         $WMpicture['image_description'] .= $next_byte_pair;
     } while ($next_byte_pair !== "");
     $WMpicture['dataoffset'] = $offset;
     $WMpicture['data'] = substr($data, $offset);
     $imageinfo = array();
     $WMpicture['image_mime'] = '';
     $imagechunkcheck = Utils::GetDataImageSize($WMpicture['data'], $imageinfo);
     unset($imageinfo);
     if (!empty($imagechunkcheck)) {
         $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
     }
     if (!isset($this->getid3->info['asf']['comments']['picture'])) {
         $this->getid3->info['asf']['comments']['picture'] = array();
     }
     $this->getid3->info['asf']['comments']['picture'][] = array('data' => $WMpicture['data'], 'image_mime' => $WMpicture['image_mime']);
     return $WMpicture;
 }