Ignore:
Timestamp:
Jul 7, 2009, 10:27:37 PM (15 years ago)
Author:
vdigital
Message:

Change: getid3 upgraded to -> 1.7.9

File:
1 edited

Legend:

Unmodified
Added
Removed
  • extensions/charlies_content/getid3/getid3/write.apetag.php

    r3318 r3544  
    11<?php
    2 // +----------------------------------------------------------------------+
    3 // | PHP version 5                                                        |
    4 // +----------------------------------------------------------------------+
    5 // | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
    6 // +----------------------------------------------------------------------+
    7 // | This source file is subject to version 2 of the GPL license,         |
    8 // | that is bundled with this package in the file license.txt and is     |
    9 // | available through the world-wide-web at the following url:           |
    10 // | http://www.gnu.org/copyleft/gpl.html                                 |
    11 // +----------------------------------------------------------------------+
    12 // | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
    13 // +----------------------------------------------------------------------+
    14 // | Authors: James Heinrich <infoØgetid3*org>                            |
    15 // |          Allan Hansen <ahØartemis*dk>                                |
    16 // +----------------------------------------------------------------------+
    17 // | write.apetag.php                                                     |
    18 // | writing module for ape tags                                          |
    19 // | dependencies: module.tag.apetag.php                                  |
    20 // | dependencies: module.tag.id3v1.php                                   |
    21 // | dependencies: module.tag.lyrics3.php                                 |
    22 // +----------------------------------------------------------------------+
    23 //
    24 // $Id$
    25 
    26 
    27 class getid3_write_apetag extends getid3_handler_write
     2/////////////////////////////////////////////////////////////////
     3/// getID3() by James Heinrich <info@getid3.org>               //
     4//  available at http://getid3.sourceforge.net                 //
     5//            or http://www.getid3.org                         //
     6/////////////////////////////////////////////////////////////////
     7// See readme.txt for more details                             //
     8/////////////////////////////////////////////////////////////////
     9//                                                             //
     10// write.apetag.php                                            //
     11// module for writing APE tags                                 //
     12// dependencies: module.tag.apetag.php                         //
     13//                                                            ///
     14/////////////////////////////////////////////////////////////////
     15
     16
     17getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
     18
     19class getid3_write_apetag
    2820{
    29    
    30     public $comments;
    31    
    32 
    33     public function read() {
    34        
    35         $engine = new getid3;
    36         $engine->filename = $this->filename;
    37         $engine->fp = fopen($this->filename, 'rb');
    38         $engine->include_module('tag.apetag');
    39        
    40         $tag = new getid3_apetag($engine);
    41         $tag->Analyze();
    42 
    43         if (!isset($engine->info['ape']['comments'])) {
    44             return;
    45         }
    46        
    47         $this->comments = $engine->info['ape']['comments'];
    48        
    49         // convert single element arrays to string
    50         foreach ($this->comments as $key => $value) {
    51             if (sizeof($value) == 1) {
    52                 $this->comments[$key] = $value[0];
    53             }
    54         }
    55            
    56         return true;
    57     }
    58 
    59 
    60     public function write() {
    61        
    62         // remove existing apetag
    63         $this->remove();
    64        
    65         $engine = new getid3;
    66         $engine->filename = $this->filename;
    67         $engine->fp = fopen($this->filename, 'rb');
    68         $engine->include_module('tag.id3v1');
    69         $engine->include_module('tag.lyrics3');
    70        
    71         $tag = new getid3_id3v1($engine);
    72         $tag->Analyze();
    73        
    74         $tag = new getid3_lyrics3($engine);
    75         $tag->Analyze();
    76 
    77         $apetag = $this->generate_tag();
    78            
    79         if (!$fp = @fopen($this->filename, 'a+b')) {
    80             throw new getid3_exception('Could not open a+b: ' . $this->filename);
    81         }
    82 
    83         // init: audio ends at eof
    84         $post_audio_offset = filesize($this->filename);
    85        
    86         // lyrics3 tag present
    87         if (@$engine->info['lyrics3']['tag_offset_start']) {
    88            
    89             // audio ends before lyrics3 tag
    90             $post_audio_offset = @$engine->info['lyrics3']['tag_offset_start'];
    91         }
    92 
    93         // id3v1 tag present
    94         elseif (@$engine->info['id3v1']['tag_offset_start']) {
    95            
    96             // audio ends before id3v1 tag
    97             $post_audio_offset = $engine->info['id3v1']['tag_offset_start'];
    98         }
    99 
    100         // seek to end of audio data
    101         fseek($fp, $post_audio_offset, SEEK_SET);
    102 
    103         // save data after audio data
    104         $post_audio_data = '';
    105         if (filesize($this->filename) > $post_audio_offset) {
    106             $post_audio_data = fread($fp, filesize($this->filename) - $post_audio_offset);
    107         }
    108 
    109         // truncate file before start of new apetag
    110         fseek($fp, $post_audio_offset, SEEK_SET);
    111         ftruncate($fp, ftell($fp));
    112        
    113         // write new apetag
    114         fwrite($fp, $apetag, strlen($apetag));
    115        
    116         // rewrite data after audio
    117         if (!empty($post_audio_data)) {
    118             fwrite($fp, $post_audio_data, strlen($post_audio_data));
    119         }
    120        
    121         fclose($fp);
    122         clearstatcache();
    123        
    124         return true;
    125     }
    126 
    127 
    128     public function remove() {
    129        
    130         $engine = new getid3;
    131         $engine->filename = $this->filename;
    132         $engine->fp = fopen($this->filename, 'rb');
    133         $engine->include_module('tag.apetag');
    134 
    135         $tag = new getid3_apetag($engine);
    136         $tag->Analyze();
    137 
    138         if (isset($engine->info['ape']['tag_offset_start']) && isset($engine->info['ape']['tag_offset_end'])) {
    139            
    140             if (!$fp = @fopen($this->filename, 'a+b')) {
    141                 throw new getid3_exception('Could not open a+b: ' . $this->filename);
    142             }
    143 
    144             // get data after apetag
    145             if (filesize($this->filename) > $engine->info['ape']['tag_offset_end']) {
    146                 fseek($fp, $engine->info['ape']['tag_offset_end'], SEEK_SET);
    147                 $data_after_ape = fread($fp, filesize($this->filename) - $engine->info['ape']['tag_offset_end']);
    148             }
    149 
    150             // truncate file before start of apetag
    151             ftruncate($fp, $engine->info['ape']['tag_offset_start']);
    152 
    153             // rewrite data after apetag
    154             if (isset($data_after_ape)) {
    155                 fseek($fp, $engine->info['ape']['tag_offset_start'], SEEK_SET);
    156                 fwrite($fp, $data_after_ape, strlen($data_after_ape));
    157             }
    158            
    159             fclose($fp);
    160             clearstatcache();
    161         }
    162        
    163         // success when removing non-existant tag
    164         return true;
    165     }
    166 
    167 
    168     protected function generate_tag() {
    169        
    170         // NOTE: All data passed to this function must be UTF-8 format
    171 
    172         $items = array();
    173         if (!is_array($this->comments)) {
    174             throw new getid3_exception('Cannot write empty tag, use remove() instead.');
    175         }
    176        
    177         foreach ($this->comments as $key => $values) {
    178            
    179             // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
    180             // A case-insensitive vobiscomment field name that may consist of ASCII 0x20 through 0x7E.
    181             // ASCII 0x41 through 0x5A  inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through 0x7A inclusive (a-z).
    182             if (preg_match("/[^\x20-\x7E]/", $key)) {
    183                 throw new getid3_exception('Field name "' . $key . '" contains invalid character(s).');
    184             }
    185            
    186             $key = strtolower($key);
    187            
    188             // convert single value comment to array
    189             if (!is_array($values)) {
    190                 $values = array ($values);
    191             }
    192 
    193             $value_array = array ();
    194             foreach ($values as $value) {
    195                 $value_array[] = str_replace("\x00", '', $value);
    196             }
    197             $value_string = implode("\x00", $value_array);
    198 
    199             // length of the assigned value in bytes
    200             $tag_item  = getid3_lib::LittleEndian2String(strlen($value_string), 4);
    201            
    202             $tag_item .= "\x00\x00\x00\x00" . $key . "\x00" . $value_string;
    203 
    204             $items[] = $tag_item;
    205         }
    206 
    207         return $this->generate_header_footer($items, true) . implode('', $items) . $this->generate_header_footer($items, false);
    208     }
    209    
    210 
    211     protected function generate_header_footer(&$items, $is_header=false) {
    212        
    213         $comments_length = 0;
    214         foreach ($items as $item_data) {
    215             $comments_length += strlen($item_data);
    216         }
    217 
    218         $header  = 'APETAGEX';
    219         $header .= getid3_lib::LittleEndian2String(2000, 4);
    220         $header .= getid3_lib::LittleEndian2String(32 + $comments_length, 4);
    221         $header .= getid3_lib::LittleEndian2String(count($items), 4);
    222         $header .= $this->generate_flags(true, true, $is_header, 0, false);
    223         $header .= str_repeat("\x00", 8);
    224 
    225         return $header;
    226     }
    227    
    228 
    229     protected function generate_flags($header=true, $footer=true, $is_header=false, $encoding_id=0, $read_only=false) {
    230        
    231         $flags = array_fill(0, 4, 0);
    232        
    233         // Tag contains a header
    234         if ($header) {
    235             $flags[0] |= 0x80;
    236         }
    237        
    238         // Tag contains no footer
    239         if (!$footer) {
    240             $flags[0] |= 0x40;
    241         }
    242        
    243         // This is the header, not the footer
    244         if ($is_header) {
    245             $flags[0] |= 0x20;
    246         }
    247 
    248         // 0: Item contains text information coded in UTF-8
    249         // 1: Item contains binary information °)
    250         // 2: Item is a locator of external stored information °°)
    251         // 3: reserved
    252         $flags[3] |= ($encoding_id << 1);
    253 
    254         // Tag or Item is Read Only
    255         if ($read_only) {
    256             $flags[3] |= 0x01;
    257         }
    258 
    259         return chr($flags[3]).chr($flags[2]).chr($flags[1]).chr($flags[0]);
    260     }
    261    
     21
     22        var $filename;
     23        var $tag_data;
     24        var $always_preserve_replaygain = true;  // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
     25        var $warnings = array();                 // any non-critical errors will be stored here
     26        var $errors   = array();                 // any critical errors will be stored here
     27
     28        function getid3_write_apetag() {
     29                return true;
     30        }
     31
     32        function WriteAPEtag() {
     33                // NOTE: All data passed to this function must be UTF-8 format
     34
     35                $getID3 = new getID3;
     36                $ThisFileInfo = $getID3->analyze($this->filename);
     37
     38                if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
     39                        if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
     40                                // Current APE tag between Lyrics3 and ID3v1/EOF
     41                                // This break Lyrics3 functionality
     42                                if (!$this->DeleteAPEtag()) {
     43                                        return false;
     44                                }
     45                                $ThisFileInfo = $getID3->analyze($this->filename);
     46                        }
     47                }
     48
     49                if ($this->always_preserve_replaygain) {
     50                        $ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
     51                        foreach ($ReplayGainTagsToPreserve as $rg_key) {
     52                                if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
     53                                        $this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
     54                                }
     55                        }
     56                }
     57
     58                if ($APEtag = $this->GenerateAPEtag()) {
     59                        if ($fp = @fopen($this->filename, 'a+b')) {
     60                                $oldignoreuserabort = ignore_user_abort(true);
     61                                flock($fp, LOCK_EX);
     62
     63                                $PostAPEdataOffset = $ThisFileInfo['avdataend'];
     64                                if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
     65                                        $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
     66                                }
     67                                if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
     68                                        $PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
     69                                }
     70                                fseek($fp, $PostAPEdataOffset, SEEK_SET);
     71                                $PostAPEdata = '';
     72                                if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
     73                                        $PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
     74                                }
     75
     76                                fseek($fp, $PostAPEdataOffset, SEEK_SET);
     77                                if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
     78                                        fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
     79                                }
     80                                ftruncate($fp, ftell($fp));
     81                                fwrite($fp, $APEtag, strlen($APEtag));
     82                                if (!empty($PostAPEdata)) {
     83                                        fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
     84                                }
     85                                flock($fp, LOCK_UN);
     86                                fclose($fp);
     87                                ignore_user_abort($oldignoreuserabort);
     88                                return true;
     89
     90                        }
     91                        return false;
     92                }
     93                return false;
     94        }
     95
     96        function DeleteAPEtag() {
     97                $getID3 = new getID3;
     98                $ThisFileInfo = $getID3->analyze($this->filename);
     99                if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
     100                        if ($fp = @fopen($this->filename, 'a+b')) {
     101
     102                                flock($fp, LOCK_EX);
     103                                $oldignoreuserabort = ignore_user_abort(true);
     104
     105                                fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
     106                                $DataAfterAPE = '';
     107                                if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
     108                                        $DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
     109                                }
     110
     111                                ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
     112                                fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
     113
     114                                if (!empty($DataAfterAPE)) {
     115                                        fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
     116                                }
     117
     118                                flock($fp, LOCK_UN);
     119                                fclose($fp);
     120                                ignore_user_abort($oldignoreuserabort);
     121
     122                                return true;
     123
     124                        }
     125                        return false;
     126                }
     127                return true;
     128        }
     129
     130
     131        function GenerateAPEtag() {
     132                // NOTE: All data passed to this function must be UTF-8 format
     133
     134                $items = array();
     135                if (!is_array($this->tag_data)) {
     136                        return false;
     137                }
     138                foreach ($this->tag_data as $key => $arrayofvalues) {
     139                        if (!is_array($arrayofvalues)) {
     140                                return false;
     141                        }
     142
     143                        $valuestring = '';
     144                        foreach ($arrayofvalues as $value) {
     145                                $valuestring .= str_replace("\x00", '', $value)."\x00";
     146                        }
     147                        $valuestring = rtrim($valuestring, "\x00");
     148
     149                        // Length of the assigned value in bytes
     150                        $tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
     151
     152                        //$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
     153                        $tagitem .= "\x00\x00\x00\x00";
     154
     155                        $tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
     156                        $tagitem .= $valuestring;
     157
     158                        $items[] = $tagitem;
     159
     160                }
     161
     162                return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
     163        }
     164
     165        function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
     166                $tagdatalength = 0;
     167                foreach ($items as $itemdata) {
     168                        $tagdatalength += strlen($itemdata);
     169                }
     170
     171                $APEheader  = 'APETAGEX';
     172                $APEheader .= getid3_lib::LittleEndian2String(2000, 4);
     173                $APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
     174                $APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
     175                $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
     176                $APEheader .= str_repeat("\x00", 8);
     177
     178                return $APEheader;
     179        }
     180
     181        function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
     182                $APEtagFlags = array_fill(0, 4, 0);
     183                if ($header) {
     184                        $APEtagFlags[0] |= 0x80; // Tag contains a header
     185                }
     186                if (!$footer) {
     187                        $APEtagFlags[0] |= 0x40; // Tag contains no footer
     188                }
     189                if ($isheader) {
     190                        $APEtagFlags[0] |= 0x20; // This is the header, not the footer
     191                }
     192
     193                // 0: Item contains text information coded in UTF-8
     194                // 1: Item contains binary information °)
     195                // 2: Item is a locator of external stored information °°)
     196                // 3: reserved
     197                $APEtagFlags[3] |= ($encodingid << 1);
     198
     199                if ($readonly) {
     200                        $APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
     201                }
     202
     203                return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
     204        }
     205
     206        function CleanAPEtagItemKey($itemkey) {
     207                $itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
     208
     209                // http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
     210                switch (strtoupper($itemkey)) {
     211                        case 'EAN/UPC':
     212                        case 'ISBN':
     213                        case 'LC':
     214                        case 'ISRC':
     215                                $itemkey = strtoupper($itemkey);
     216                                break;
     217
     218                        default:
     219                                $itemkey = ucwords($itemkey);
     220                                break;
     221                }
     222                return $itemkey;
     223
     224        }
     225
    262226}
    263227
Note: See TracChangeset for help on using the changeset viewer.