/plugins/podcast/trunk/mp3info.php

https://github.com/somefool/habari-extras · PHP · 275 lines · 230 code · 37 blank · 8 comment · 37 complexity · 262537b6575ae765b8083a5b83be7aa9 MD5 · raw file

  1. <?php
  2. require_once( 'mp3frameheader.php' );
  3. class MP3Info
  4. {
  5. private $file_name;
  6. private $size; // size of the file in bytes
  7. private $validity;
  8. private $content = '';
  9. // MP3 frame information. Many of these are not yet used.
  10. private $num_frames;
  11. private $duration; // in seconds
  12. private $mpeg_version;
  13. private $mpeg_layer;
  14. private $has_CRC;
  15. private $bitrate; // average
  16. private $samplerate;
  17. private $samples_per_frame;
  18. private $channel_mode;
  19. private $mode_extension;
  20. private $emphasis;
  21. private $is_copyrighted;
  22. private $is_original;
  23. private $is_private;
  24. function __construct( $file_name )
  25. {
  26. $this->file_name = $file_name;
  27. }
  28. public function open()
  29. {
  30. $pos = 0;
  31. $frame_bitrate = 0;
  32. $total_bitrate = 0; // total frames bit rate (used to calc. average)
  33. $frame_mark = 0xE0;
  34. $framesize = 0;
  35. $hdr = NULL;
  36. $content = '';
  37. $tmp = $this->get_file( $this->file_name );
  38. if( ! $tmp ) {
  39. return FALSE;
  40. }
  41. $fh = @fopen( $tmp, 'rb' );
  42. if( ! $fh ) {
  43. return FALSE;
  44. }
  45. $this->size = filesize( $tmp );
  46. $ch = fgetc( $fh );
  47. while( $pos < $this->size ) {
  48. // first byte of frame header
  49. if ( ord( $ch ) == 0xFF ) {
  50. $ch = fgetc( $fh );
  51. // second byte of frame header
  52. if ( $this->num_frames == 0 && ( ord( $ch ) & $frame_mark ) == $frame_mark ) {
  53. fseek( $fh, $pos );
  54. $hdr = new MP3FrameHeader( fread( $fh, 4 ) );
  55. $frame_bitrate = $hdr->bitrate;
  56. if( $frame_bitrate != -1 ) {
  57. $this->num_frames++;
  58. $frame_mark = ord( $ch );
  59. $total_bitrate += $frame_bitrate;
  60. // Get the version
  61. $this->mpeg_version = $hdr->version;
  62. // Get the layers
  63. $this->mpeg_layer = $hdr->layer;
  64. // Get samples per frame
  65. $this->samples_per_frame = $hdr->samples_per_frame;
  66. // Get samplerate
  67. $this->samplerate = $hdr->samplerate;
  68. $this->has_CRC = $hdr->has_CRC;
  69. $this->channel_mode = $hdr->channel_mode;
  70. $this->mode_extension = $hdr->mode_ext;
  71. $this->emphasis = $hdr->emphasis;
  72. $this->is_copyrighted = $hdr->copyright;
  73. $this->is_original = $hdr->original;
  74. $this->is_private = $hdr->is_private;
  75. if( $this->mpeg_layer == MP3FrameHeader::MPEG_LAYER_1 ) {
  76. $framesize = ( 12 * $frame_bitrate / $this->samplerate + $hdr->is_padded ) * 4;
  77. }
  78. else {
  79. $framesize = 144 * $frame_bitrate / $this->samplerate + $hdr->is_padded;
  80. }
  81. $pos += $framesize - 2;
  82. }
  83. }
  84. else if ( ( ord( $ch ) & $frame_mark ) == $frame_mark ) {
  85. fseek( $fh, $pos );
  86. $hdr = new MP3FrameHeader( fread( $fh, 4 ) );
  87. $frame_bitrate = $hdr->bitrate;
  88. if( $frame_bitrate != -1 ) {
  89. $this->num_frames++;
  90. $total_bitrate += $frame_bitrate;
  91. if( $this->mpeg_layer == MP3FrameHeader::MPEG_LAYER_1 ) {
  92. $framesize = ( 12 * $frame_bitrate / $this->samplerate + $hdr->is_padded ) * 4;
  93. }
  94. else {
  95. $framesize = 144 * $frame_bitrate / $this->samplerate + $hdr->is_padded;
  96. }
  97. $pos += $framesize - 2;
  98. }
  99. }
  100. }
  101. $pos++;
  102. if( $pos < $this->size ) {
  103. fseek( $fh, $pos, SEEK_SET );
  104. $ch = fgetc( $fh );
  105. }
  106. }
  107. // if at least one frame was read, the MP3 is considered valid
  108. if ( $this->num_frames > 0 ) {
  109. $this->bitrate = (int)($total_bitrate / $this->num_frames ); // average the bitrate
  110. $this->duration = ($this->num_frames * $this->samples_per_frame / $this->samplerate);
  111. }
  112. else {
  113. $this->bitrate = 0;
  114. $this->duration = 0;
  115. }
  116. if( ! $this->is_local_file( $this->file_name ) ) {
  117. unlink( $tmp );
  118. }
  119. return TRUE;
  120. }
  121. public function format_minutes_seconds( $seconds )
  122. {
  123. $min = (int)$seconds / 60;
  124. $sec = $seconds % 60;
  125. $str = sprintf( "%d:%02d", $min, $sec );
  126. return $str;
  127. }
  128. public function get_size()
  129. {
  130. return $this->size;
  131. }
  132. public function get_frame_count()
  133. {
  134. return $this->num_frames;
  135. }
  136. public function get_duration()
  137. {
  138. return $this->duration;
  139. }
  140. public function get_mpeg_version()
  141. {
  142. return $this->mpeg_version;
  143. }
  144. public function get_mpeg_layer()
  145. {
  146. return $this->mpeg_layer;
  147. }
  148. public function has_CRC()
  149. {
  150. return $this->has_CRC;
  151. }
  152. public function get_bitrate()
  153. {
  154. return $this->bitrate;
  155. }
  156. public function get_samplerate()
  157. {
  158. return $this->samplerate;
  159. }
  160. public function get_channel_mode()
  161. {
  162. return $this->channel_mode;
  163. }
  164. public function get_emphasis()
  165. {
  166. return $this->emphasis;
  167. }
  168. public function is_copyrighted()
  169. {
  170. return $this->copyrighted;
  171. }
  172. public function is_original()
  173. {
  174. return $this->original;
  175. }
  176. protected function get_file( $file_name )
  177. {
  178. if( $this->is_local_file( $file_name ) ) {
  179. EventLog::log('local file');
  180. return $this->get_local( $file_name );
  181. }
  182. else if( ini_get( 'allow_url_fopen' ) ) {
  183. return $this->get_with_streams( $file_name );
  184. }
  185. else if( function_exists( 'curl_init' ) && ! ( ini_get( 'safe_mode' ) && ini_get( 'open_basedir' ) ) ) {
  186. return $this->get_with_curl( $file_name );
  187. }
  188. }
  189. protected function is_local_file( $file_name = '' )
  190. {
  191. $parsed = InputFilter::parse_url( $file_name );
  192. $parsed_home = InputFilter::parse_url( Site::get_url( 'habari' ) );
  193. return $parsed['host'] == $parsed_home['host'];
  194. }
  195. protected function get_local( $file_name = '' )
  196. {
  197. $parsed = InputFilter::parse_url( $file_name );
  198. return HABARI_PATH . $parsed['path'];
  199. }
  200. protected function get_with_streams( $file_name = '' )
  201. {
  202. $tmp = tempnam( '/user/cache', 'RR' );
  203. $temph = fopen( $tmp, 'wb' );
  204. $fp = fopen( $file_name, 'rb' );
  205. stream_copy_to_stream( $fp, $temph );
  206. fclose( $temph );
  207. fclose( $fp );
  208. return $tmp;
  209. }
  210. protected function get_with_curl( $file_name = '' )
  211. {
  212. $headers = array();
  213. $timeout = 180;
  214. $method = 'GET';
  215. $tmp = tempnam( '/user/cache', 'RR' );
  216. $headers[] = "User-Agent: Habari";
  217. $ch = curl_init();
  218. curl_setopt( $ch, CURLOPT_URL, $file_name ); // The URL.
  219. curl_setopt( $ch, CURLOPT_MAXREDIRS, 5 ); // Maximum number of redirections to follow.
  220. curl_setopt( $ch, CURLOPT_CRLF, true ); // Convert UNIX newlines to \r\n.
  221. curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); // Follow 302's and the like.
  222. curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
  223. curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
  224. curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers ); // headers to send
  225. $th = fopen( $tmp, 'wb' );
  226. curl_setopt( $ch, CURLOPT_FILE, $th );
  227. $success = curl_exec( $ch );
  228. fclose( $th );
  229. if( ! $success || curl_errno($ch) != 0 || curl_getinfo( $ch, CURLINFO_HTTP_CODE ) !== 200 ) {
  230. curl_close( $ch );
  231. unlink( $tmp );
  232. return FALSE;
  233. }
  234. curl_close( $ch );
  235. return $tmp;
  236. }
  237. }
  238. ?>