r25467 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r25466‎ | r25467 | r25468 >
Date:04:05, 4 September 2007
Author:tstarling
Status:old
Tags:
Comment:
* Implemented thumbtime and noplayer parameters as planned.
* Added pretty icons from Crystal Project, licensed under LGPL.
* Fixed audio display, now looks kind of OK in both gallery and vertically constrained environments.
* Replaced the rapidly growing parameter list for wgOggPlayer.init() with an associative array.
* Switched from <br/> to <div> all over the place because it seemed like a good idea at the time. Printable display is now even more broken than before as a result.

The extension is now ready for beta testing.
Modified paths:
  • /trunk/extensions/OggHandler/OggHandler.i18n.php (modified) (history)
  • /trunk/extensions/OggHandler/OggHandler.php (modified) (history)
  • /trunk/extensions/OggHandler/OggHandler_body.php (modified) (history)
  • /trunk/extensions/OggHandler/OggPlayer.js (modified) (history)
  • /trunk/extensions/OggHandler/README (modified) (history)
  • /trunk/extensions/OggHandler/info.png (added) (history)
  • /trunk/extensions/OggHandler/pause.png (added) (history)
  • /trunk/extensions/OggHandler/play.png (added) (history)
  • /trunk/extensions/OggHandler/stop.png (added) (history)

Diff [purge]

Index: trunk/extensions/OggHandler/OggHandler.php
@@ -22,6 +22,7 @@
2323 $wgFFmpegLocation = 'ffmpeg';
2424 $wgExtensionMessagesFiles['OggHandler'] = "$oggDir/OggHandler.i18n.php";
2525 $wgParserOutputHooks['OggHandler'] = array( 'OggHandler', 'outputHook' );
 26+$wgHooks['LanguageGetMagic'][] = 'OggHandler::registerMagicWords';
2627
2728 // Filename or URL path to the Cortado Java player applet.
2829 //
Index: trunk/extensions/OggHandler/pause.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/OggHandler/pause.png
___________________________________________________________________
Name: svn:mime-type
2930 + image/png
Index: trunk/extensions/OggHandler/OggPlayer.js
@@ -13,21 +13,24 @@
1414 // Configuration from MW
1515 'msg': {},
1616 'cortadoUrl' : '',
17 - 'smallFileUrl' : '',
 17+ 'extPathUrl' : '',
1818 'showPlayerSelect': true,
1919 'controlsHeightGuess': 20,
2020
21 - // Main entry point: initialise a video player
22 - // Player will be created as a child of the given ID
23 - // There may be multiple players in a document
24 - 'init': function ( player, id, videoUrl, width, height, length, linkUrl ) {
25 - elt = document.getElementById( id );
 21+ /**
 22+ * Main entry point: initialise a video player
 23+ * Player will be created as a child of the given ID
 24+ * There may be multiple players in a document.
 25+ * Parameters are: id, videoUrl, width, height, length, linkUrl
 26+ */
 27+ 'init': function ( player, params ) {
 28+ elt = document.getElementById( params.id );
2629
2730 // Save still image HTML
28 - if ( !(id in this.savedThumbs) ) {
 31+ if ( !(params.id in this.savedThumbs) ) {
2932 var thumb = document.createDocumentFragment();
3033 thumb.appendChild( elt.cloneNode( true ) );
31 - this.savedThumbs[id] = thumb;
 34+ this.savedThumbs[params.id] = thumb;
3235 }
3336
3437 this.detect( elt );
@@ -62,40 +65,41 @@
6366
6467 switch ( player ) {
6568 case 'videoElement':
66 - this.embedVideoElement( elt, videoUrl, width, height, length );
 69+ this.embedVideoElement( elt, params );
6770 break;
6871 case 'oggPlugin':
69 - this.embedOggPlugin( elt, videoUrl, width, height, length );
 72+ this.embedOggPlugin( elt, params );
7073 break;
7174 case 'vlc-mozilla':
72 - this.embedVlcPlugin( elt, videoUrl, width, height, length );
 75+ this.embedVlcPlugin( elt, params );
7376 break;
7477 case 'vlc-activex':
75 - this.embedVlcActiveX( elt, videoUrl, width, height, length );
 78+ this.embedVlcActiveX( elt, params );
7679 break;
7780 case 'cortado':
78 - this.embedCortado( elt, videoUrl, width, height, length );
 81+ this.embedCortado( elt, params );
7982 break;
8083 case 'quicktime-mozilla':
81 - this.embedQuicktimePlugin( elt, videoUrl, width, height, length );
 84+ this.embedQuicktimePlugin( elt, params );
8285 break;
8386 case 'thumbnail':
84 - if ( id in this.savedThumbs ) {
85 - elt.appendChild( this.savedThumbs[id].cloneNode( true ) );
 87+ if ( params.id in this.savedThumbs ) {
 88+ elt.appendChild( this.savedThumbs[params.id].cloneNode( true ) );
8689 } else {
87 - elt.appendChild( document.createTextNode( 'Missing saved thumbnail for ' + id ) );
 90+ elt.appendChild( document.createTextNode( 'Missing saved thumbnail for ' + params.id ) );
8891 }
8992 break;
9093 default:
91 - elt.innerHTML = this.msg['ogg-no-player'] + '<br/>';
 94+ elt.innerHTML = '<div>' + this.msg['ogg-no-player'] + '</div>';
9295 player = 'none';
9396 }
9497 if ( player != 'thumbnail' ) {
95 - var optionsBox = this.makeOptionsBox( player, id, videoUrl, width, height, length, linkUrl );
96 - var optionsLink = this.makeOptionsLink( id );
97 - elt.appendChild( document.createElement( 'br' ) );
98 - elt.appendChild( optionsBox );
99 - elt.appendChild( optionsLink );
 98+ var optionsBox = this.makeOptionsBox( player, params );
 99+ var optionsLink = this.makeOptionsLink( params.id );
 100+ var div = document.createElement( 'div' );
 101+ div.appendChild( optionsBox );
 102+ div.appendChild( optionsLink );
 103+ elt.appendChild( div );
100104 }
101105 },
102106
@@ -197,22 +201,22 @@
198202 }
199203 },
200204
201 - 'makeOptionsBox' : function ( selectedPlayer, id, videoUrl, width, height, length, linkUrl ) {
 205+ 'makeOptionsBox' : function ( selectedPlayer, params ) {
202206 var div, p, a, ul, li, button;
203207
204208 div = document.createElement( 'div' );
205 - div.style.cssText = "width: " + ( width - 10 ) + "px; display: none;";
 209+ div.style.cssText = "width: " + ( params.width - 10 ) + "px; display: none;";
206210 div.className = 'ogg-player-options';
207 - div.id = id + '_options_box';
 211+ div.id = params.id + '_options_box';
208212 div.align = 'center';
209213
210214 ul = document.createElement( 'ul' );
211215
212216 // Description page link
213 - if ( linkUrl ) {
 217+ if ( params.linkUrl ) {
214218 li = document.createElement( 'li' );
215219 a = document.createElement( 'a' );
216 - a.href = linkUrl;
 220+ a.href = params.linkUrl;
217221 a.appendChild( document.createTextNode( this.msg['ogg-desc-link'] ) );
218222 li.appendChild( a );
219223 ul.appendChild( li );
@@ -221,7 +225,7 @@
222226 // Download link
223227 li = document.createElement( 'li' );
224228 a = document.createElement( 'a' );
225 - a.href = videoUrl;
 229+ a.href = params.videoUrl;
226230 a.appendChild( document.createTextNode( this.msg['ogg-download'] ) );
227231 li.appendChild( a );
228232 ul.appendChild( li );
@@ -236,15 +240,21 @@
237241 // Make player list
238242 ul = document.createElement( 'ul' );
239243 for ( var i = 0; i < this.players.length + 1; i++ ) {
240 - var player;
 244+ var player, playerMsg;
241245 if ( i == this.players.length ) {
242246 player = 'thumbnail';
 247+ if ( params.isVideo ) {
 248+ playerMsg = 'ogg-player-thumbnail';
 249+ } else {
 250+ playerMsg = 'ogg-player-soundthumb';
 251+ }
243252 } else {
244253 player = this.players[i];
245254 // Skip unsupported players
246255 if ( ! this.clientSupports[player] ) {
247256 continue;
248257 }
 258+ playerMsg = 'ogg-player-' + player;
249259 }
250260
251261 // Make list item
@@ -252,13 +262,13 @@
253263 if ( player == selectedPlayer ) {
254264 var strong = document.createElement( 'strong' );
255265 strong.appendChild( document.createTextNode(
256 - this.msg['ogg-player-' + player] + ' ' + this.msg['ogg-player-selected'] ) );
 266+ this.msg[playerMsg] + ' ' + this.msg['ogg-player-selected'] ) );
257267 li.appendChild( strong );
258268 } else {
259269 a = document.createElement( 'a' );
260270 a.href = 'javascript:void("' + player + '")';
261 - a.onclick = this.makePlayerFunction( player, id, videoUrl, width, height, length, linkUrl );
262 - a.appendChild( document.createTextNode( this.msg['ogg-player-' + player] ) );
 271+ a.onclick = this.makePlayerFunction( player, params );
 272+ a.appendChild( document.createTextNode( this.msg[playerMsg] ) );
263273 li.appendChild( a );
264274 }
265275 ul.appendChild( li );
@@ -269,7 +279,7 @@
270280 div2.style.cssText = 'text-align: center;';
271281 button = document.createElement( 'button' );
272282 button.appendChild( document.createTextNode( this.msg['ogg-dismiss'] ) );
273 - button.onclick = this.makeDismissFunction( id );
 283+ button.onclick = this.makeDismissFunction( params.id );
274284 div2.appendChild( button );
275285 div.appendChild( div2 );
276286
@@ -319,165 +329,172 @@
320330 }
321331 },
322332
323 - 'makePlayerFunction' : function ( player, id, videoUrl, width, height, length, linkUrl ) {
 333+ 'makePlayerFunction' : function ( player, params ) {
324334 var this_ = this;
325335 return function () {
326336 if ( player != 'thumbnail' ) {
327337 document.cookie = "ogg_player=" + player;
328338 }
329 - this_.init( player, id, videoUrl, width, height, length, linkUrl );
 339+ this_.init( player, params );
330340 };
331341 },
332342
333 - 'newButton': function ( caption, callback ) {
 343+ 'newButton': function ( caption, image, callback ) {
334344 var elt = document.createElement('input');
335 - elt.type = 'button';
336 - elt.value = this.msg[caption];
 345+ elt.type = 'image';
 346+ elt.src = this.extPathUrl + '/' + image;
 347+ elt.alt = elt.value = elt.title = this.msg[caption];
337348 elt.onclick = callback;
338349 return elt;
339350 },
340351
341352 'newPlayButton': function ( videoElt ) {
342 - return this.newButton( 'ogg-play', function () { videoElt.play(); } );
 353+ return this.newButton( 'ogg-play', 'play.png', function () { videoElt.play(); } );
343354 },
344355
345356 'newPauseButton': function ( videoElt ) {
346 - return this.newButton( 'ogg-pause', function () { videoElt.pause(); } );
 357+ return this.newButton( 'ogg-pause', 'pause.png', function () { videoElt.pause(); } );
347358 },
348359
349360 'newStopButton': function ( videoElt ) {
350 - return this.newButton( 'ogg-stop', function () { videoElt.stop(); } );
 361+ return this.newButton( 'ogg-stop', 'stop.png', function () { videoElt.stop(); } );
351362 },
352363
353 - 'embedVideoElement': function ( elt, videoUrl, width, height, length ) {
 364+ 'embedVideoElement': function ( elt, params ) {
354365 var videoElt = document.createElement('video');
355 - videoElt.setAttribute( 'width', width );
356 - videoElt.setAttribute( 'height', height + this.controlsHeightGuess );
357 - videoElt.setAttribute( 'src', videoUrl );
 366+ videoElt.setAttribute( 'width', params.width );
 367+ videoElt.setAttribute( 'height', params.height + this.controlsHeightGuess );
 368+ videoElt.setAttribute( 'src', params.videoUrl );
358369 videoElt.setAttribute( 'autoplay', '1' );
359370 videoElt.setAttribute( 'controls', '1' );
360 - elt.appendChild( videoElt );
 371+ var div = document.createElement( 'div' );
 372+ div.appendChild( videoElt );
 373+ elt.appendChild( div );
361374
362375 // Try to detect implementations that don't support controls
363376 // This works for the Opera test build
364377 if ( !videoElt.controls ) {
365 - elt.appendChild( document.createElement( 'br' ) );
366 - elt.appendChild( this.newPlayButton( videoElt ) );
367 - elt.appendChild( this.newPauseButton( videoElt ) );
368 - elt.appendChild( this.newStopButton( videoElt ) );
 378+ div = document.createElement( 'div' );
 379+ div.appendChild( this.newPlayButton( videoElt ) );
 380+ div.appendChild( this.newPauseButton( videoElt ) );
 381+ div.appendChild( this.newStopButton( videoElt ) );
 382+ elt.appendChild( div );
369383 //videoElt.play();
370384 }
371385 },
372386
373 - 'embedOggPlugin': function ( elt, videoUrl, width, height, length ) {
 387+ 'embedOggPlugin': function ( elt, params ) {
374388 var id = elt.id + "_obj";
375389 elt.innerHTML +=
376 - "<object id=" + this.hq( id ) +
 390+ "<div><object id=" + this.hq( id ) +
377391 " type='application/ogg'" +
378 - " width=" + this.hq( width ) +
379 - " height=" + this.hq( height + this.controlsHeightGuess ) +
380 - " data=" + this.hq( videoUrl ) + "></object>";
 392+ " width=" + this.hq( params.width ) +
 393+ " height=" + this.hq( params.height + this.controlsHeightGuess ) +
 394+ " data=" + this.hq( params.videoUrl ) + "></object></div>";
381395 },
382396
383 - 'embedVlcPlugin' : function ( elt, videoUrl, width, height, length ) {
 397+ 'embedVlcPlugin' : function ( elt, params ) {
384398 var id = elt.id + "_obj";
385399 elt.innerHTML +=
386 - "<object id=" + this.hq( id ) +
 400+ "<div><object id=" + this.hq( id ) +
387401 " type='application/x-vlc-plugin'" +
388 - " width=" + this.hq( width ) +
389 - " height=" + this.hq( height ) +
390 - " data=" + this.hq( videoUrl ) + "></object>";
 402+ " width=" + this.hq( params.width ) +
 403+ " height=" + this.hq( params.height ) +
 404+ " data=" + this.hq( params.videoUrl ) + "></object></div>";
391405
392406 var videoElt = document.getElementById( id );
393 - elt.appendChild( document.createElement( 'br' ) );
 407+ var div = document.createElement( 'div' );
394408 // TODO: seek bar
395 - elt.appendChild( this.newPlayButton( videoElt ) );
396 - elt.appendChild( this.newPauseButton( videoElt ) );
397 - elt.appendChild( this.newStopButton( videoElt ) );
 409+ div.appendChild( this.newPlayButton( videoElt ) );
 410+ div.appendChild( this.newPauseButton( videoElt ) );
 411+ div.appendChild( this.newStopButton( videoElt ) );
 412+ elt.appendChild( div );
398413 },
399414
400 - 'embedVlcActiveX' : function ( elt, videoUrl, width, height, length ) {
 415+ 'embedVlcActiveX' : function ( elt, params ) {
401416 var id = elt.id + "_obj";
402417
403418 var html =
404 - '<object id=' + this.hq( id ) +
 419+ '<div><object id=' + this.hq( id ) +
405420 ' classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921"' +
406421 ' codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab#Version=0,8,6,0"' +
407 - ' width=' + this.hq( width ) +
408 - ' height=' + this.hq( height ) +
409 - ' style="width: ' + this.hx( width ) + 'px; height: ' + this.hx( height ) + 'px;"' +
 422+ ' width=' + this.hq( params.width ) +
 423+ ' height=' + this.hq( params.height ) +
 424+ ' style="width: ' + this.hx( params.width ) + 'px; height: ' + this.hx( params.height ) + 'px;"' +
410425 ">" +
411 - '<param name="mrl" value=' + this.hq( videoUrl ) + '/>' +
412 - '</object>';
 426+ '<param name="mrl" value=' + this.hq( params.videoUrl ) + '/>' +
 427+ '</object></div>';
413428 elt.innerHTML += html;
414429
415430 var videoElt = document.getElementById( id );
416431
417432 // IE says "sorry, I wasn't listening, what were the dimensions again?"
418 - if ( width && height ) {
419 - videoElt.width = width;
420 - videoElt.height = height;
421 - videoElt.style.width = width + 'px';
422 - videoElt.style.height = height + 'px';
 433+ if ( params.width && params.height ) {
 434+ videoElt.width = params.width;
 435+ videoElt.height = params.height;
 436+ videoElt.style.width = params.width + 'px';
 437+ videoElt.style.height = params.height + 'px';
423438 }
424 -
425 - elt.appendChild( document.createElement( 'br' ) );
 439+ var div = document.createElement( 'div' );
426440 // TODO: seek bar
427 - elt.appendChild( this.newButton( 'ogg-play', function() { videoElt.playlist.play(); } ) );
 441+ div.appendChild( this.newButton( 'ogg-play', 'play.png', function() { videoElt.playlist.play(); } ) );
428442 // FIXME: playlist.pause() doesn't work
429 - elt.appendChild( this.newButton( 'ogg-stop', function() { videoElt.playlist.stop(); } ) );
 443+ div.appendChild( this.newButton( 'ogg-stop', 'stop.png', function() { videoElt.playlist.stop(); } ) );
 444+ elt.appendChild( div );
430445 },
431446
432 - 'embedCortado' : function ( elt, videoUrl, width, height, length ) {
 447+ 'embedCortado' : function ( elt, params ) {
433448 var statusHeight = 18;
434449 // Given extra vertical space, cortado centres the video and then overlays the status
435450 // line, leaving an ugly black bar at the top. So we don't give it any.
436 - var playerHeight = height < statusHeight ? statusHeight : height;
 451+ var playerHeight = params.height < statusHeight ? statusHeight : params.height;
437452
438453 // Create the applet all at once
439454 // In Opera, document.createElement('applet') immediately creates
440455 // a non-working applet with unchangeable parameters, similar to the
441456 // problem with IE and ActiveX.
442 - elt.innerHTML =
 457+ elt.innerHTML = '<div>' +
443458 '<applet code="com.fluendo.player.Cortado.class" ' +
444 - ' width=' + this.hq( width ) +
 459+ ' width=' + this.hq( params.width ) +
445460 ' height=' + this.hq( playerHeight ) +
446461 ' archive=' + this.hq( this.cortadoUrl ) + '>' +
447 - ' <param name="url" value=' + this.hq( videoUrl ) + '/>' +
448 - ' <param name="duration" value=' + this.hq( length ) + '/>' +
 462+ ' <param name="url" value=' + this.hq( params.videoUrl ) + '/>' +
 463+ ' <param name="duration" value=' + this.hq( params.length ) + '/>' +
449464 ' <param name="seekable" value="true"/>' +
450465 ' <param name="autoPlay" value="true"/>' +
451466 ' <param name="showStatus" value="show"/>' +
452467 ' <param name="statusHeight" value="' + statusHeight + '"/>' +
453 - '</applet>';
 468+ '</applet>' +
 469+ '</div>';
454470
455471 // Disable autoPlay in the DOM right now, to prevent Mozilla from
456472 // restarting an arbitrary number of applet instances on a back button click.
457473 // Unfortunately this means that some clients (e.g. Opera) won't autoplay at all
458 - var videoElt = elt.getElementsByTagName( 'applet' )[0];
 474+ var videoElt = elt.getElementsByTagName( 'div' ) [0] .
 475+ getElementsByTagName( 'applet' )[0];
459476 this.setParam( videoElt, 'autoPlay', '' );
460477 },
461478
462 - 'embedQuicktimePlugin': function ( elt, videoUrl, width, height, length ) {
 479+ 'embedQuicktimePlugin': function ( elt, params ) {
463480 var id = elt.id + "_obj";
464481 var controllerHeight = 16; // by observation
465482 elt.innerHTML +=
466 - "<object id=" + this.hq( id ) +
 483+ "<div><object id=" + this.hq( id ) +
467484 " type='video/quicktime'" +
468 - " width=" + this.hq( width ) +
469 - " height=" + this.hq( height + controllerHeight ) +
 485+ " width=" + this.hq( params.width ) +
 486+ " height=" + this.hq( params.height + controllerHeight ) +
470487
471488 // Use QTSRC parameter instead of data attribute to allow progressive download
472489 // The data attribute and src parameter point to a small file, as recommended in
473490 // http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_6.html
474 - " data=" + this.hq( this.smallFileUrl ) +
 491+ " data=" + this.hq( this.extPathUrl + '/null_file' ) +
475492 ">" +
476493 // Scale, don't clip
477494 "<param name='SCALE' value='Aspect'/>" +
478495 "<param name='AUTOPLAY' value='True'/>" +
479 - "<param name='src' value=" + this.hq( this.smallFileUrl ) + "/>" +
480 - "<param name='QTSRC' value=" + this.hq( videoUrl ) + "/>" +
481 - "</object>";
 496+ "<param name='src' value=" + this.hq( this.extPathUrl + '/null_file' ) + "/>" +
 497+ "<param name='QTSRC' value=" + this.hq( params.videoUrl ) + "/>" +
 498+ "</object></div>";
482499
483500 // Disable autoplay on back button
484501 var this_ = this;
Index: trunk/extensions/OggHandler/play.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/OggHandler/play.png
___________________________________________________________________
Name: svn:mime-type
485502 + image/png
Index: trunk/extensions/OggHandler/stop.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/OggHandler/stop.png
___________________________________________________________________
Name: svn:mime-type
486503 + image/png
Index: trunk/extensions/OggHandler/OggHandler_body.php
@@ -1,8 +1,12 @@
22 <?php
33
 4+// TODO: Fix core printable stylesheet. Descendant selectors suck.
 5+
46 class OggHandler extends MediaHandler {
57 const OGG_METADATA_VERSION = 1;
68
 9+ static $magicDone = false;
 10+
711 var $videoTypes = array( 'Theora' );
812 var $audioTypes = array( 'Vorbis', 'Speex', 'FLAC' );
913
@@ -10,36 +14,80 @@
1115 return true;
1216 }
1317
 18+ static function registerMagicWords( &$magicData, $code ) {
 19+ wfLoadExtensionMessages( 'OggHandler' );
 20+ return true;
 21+ }
 22+
1423 function getParamMap() {
15 - // TODO: add thumbtime, noplayer
16 - return array( 'img_width' => 'width' );
 24+ wfLoadExtensionMessages( 'OggHandler' );
 25+ return array(
 26+ 'img_width' => 'width',
 27+ 'ogg_noplayer' => 'noplayer',
 28+ 'ogg_thumbtime' => 'thumbtime',
 29+ );
1730 }
1831
1932 function validateParam( $name, $value ) {
20 - // TODO
 33+ if ( $name == 'thumbtime' ) {
 34+ if ( $this->parseTimeString( $value ) === false ) {
 35+ return false;
 36+ }
 37+ }
2138 return true;
2239 }
2340
 41+ function parseTimeString( $seekString, $length = false ) {
 42+ $parts = explode( ':', $seekString );
 43+ $time = 0;
 44+ for ( $i = 0; $i < count( $parts ); $i++ ) {
 45+ if ( !is_numeric( $parts[$i] ) ) {
 46+ return false;
 47+ }
 48+ $time += intval( $parts[$i] ) * pow( 60, count( $parts ) - $i - 1 );
 49+ }
 50+
 51+ if ( $time < 0 ) {
 52+ wfDebug( __METHOD__.": specified negative time, using zero\n" );
 53+ $time = 0;
 54+ } elseif ( $length !== false && $time > $length - 1 ) {
 55+ wfDebug( __METHOD__.": specified near-end or past-the-end time {$time}s, using end minus 1s\n" );
 56+ $time = $length - 1;
 57+ }
 58+ return $time;
 59+ }
 60+
2461 function makeParamString( $params ) {
25 - // No parameters just yet, the thumbnails are always full-size
26 - return '';
27 - /*
28 - $s = '';
29 - foreach ( $params as $name => $value ) {
30 - if ( $s !== '' ) {
31 - $s .= '-';
 62+ if ( isset( $params['thumbtime'] ) ) {
 63+ $time = $this->parseTimeString( $params['thumbtime'] );
 64+ if ( $time !== false ) {
 65+ return 'seek=' . $time;
3266 }
33 - $s .= "$name=$value";
34 - }*/
 67+ }
 68+ return 'mid';
3569 }
3670
3771 function parseParamString( $str ) {
38 - // TODO
 72+ $m = false;
 73+ if ( preg_match( '/^seek=(\d+)$/', $str, $m ) ) {
 74+ return array( 'thumbtime' => $m[0] );
 75+ }
3976 return array();
4077 }
4178
4279 function normaliseParams( $image, &$params ) {
43 - // TODO
 80+ if ( isset( $params['thumbtime'] ) ) {
 81+ $length = $this->getLength( $image );
 82+ $time = $this->parseTimeString( $params['thumbtime'] );
 83+ if ( $time === false ) {
 84+ return false;
 85+ } elseif ( $time > $length - 1 ) {
 86+ $params['thumbtime'] = $length - 1;
 87+ } elseif ( $time <= 0 ) {
 88+ $params['thumbtime'] = 0;
 89+ }
 90+ }
 91+
4492 return true;
4593 }
4694
@@ -124,32 +172,52 @@
125173 function doTransform( $file, $dstPath, $dstUrl, $params, $flags = 0 ) {
126174 global $wgFFmpegLocation;
127175
128 - // Hack for miscellaneous callers
129 - global $wgOut;
130 - $this->setHeaders( $wgOut );
131 -
132176 $width = $params['width'];
133177 $srcWidth = $file->getWidth();
134178 $srcHeight = $file->getHeight();
135179 $height = $srcWidth == 0 ? $srcHeight : $width * $srcHeight / $srcWidth;
136180 $length = $this->getLength( $file );
 181+ $noPlayer = isset( $params['noplayer'] );
137182
 183+ if ( !$noPlayer ) {
 184+ // Hack for miscellaneous callers
 185+ global $wgOut;
 186+ $this->setHeaders( $wgOut );
 187+ }
 188+
138189 if ( $srcHeight == 0 || $srcWidth == 0 ) {
139190 // Make audio player
140 - $icon = $file->iconThumb();
 191+ if ( $noPlayer ) {
 192+ $scriptPath = self::getMyScriptPath();
 193+ return new ThumbnailImage( $file, "$scriptPath/info.png", 22, 22 );
 194+ }
141195 if ( empty( $params['width'] ) ) {
142196 $width = 200;
143197 } else {
144198 $width = $params['width'];
145199 }
146 - $height = $icon->getHeight();
147 - return new OggAudioDisplay( $file, $file->getURL(), $icon->getUrl(), $width, $height, $length );
 200+ $height = empty( $params['height'] ) ? 20 : $params['height'];
 201+ return new OggAudioDisplay( $file, $file->getURL(), $width, $height, $length );
148202 }
149203
 204+ // Video thumbnail only
 205+ if ( $noPlayer ) {
 206+ return new ThumbnailImage( $file, $dstUrl, $width, $height, $dstPath );
 207+ }
 208+
150209 if ( $flags & self::TRANSFORM_LATER ) {
151210 return new OggVideoDisplay( $file, $file->getURL(), $dstUrl, $width, $height, $length );
152211 }
153212
 213+ $thumbTime = false;
 214+ if ( isset( $params['thumbtime'] ) ) {
 215+ $thumbTime = $this->parseTimeString( $params['thumbtime'], $length );
 216+ }
 217+ if ( $thumbTime === false ) {
 218+ # Seek to midpoint by default, it tends to be more interesting than the start
 219+ $thumbTime = $length / 2;
 220+ }
 221+
154222 wfMkdirParents( dirname( $dstPath ) );
155223
156224 wfDebug( "Creating video thumbnail at $dstPath\n" );
@@ -159,14 +227,13 @@
160228 # MJPEG, that's the same as JPEG except it's supported by the windows build of ffmpeg
161229 # No audio, one frame
162230 ' -f mjpeg -an -vframes 1' .
163 - # Seek to midpoint, it tends to be more interesting than the fade in at the start
164 - ' -ss ' . intval( $length / 2 ) . ' ' .
 231+ ' -ss ' . intval( $thumbTime ) . ' ' .
165232 wfEscapeShellArg( $dstPath ) . ' 2>&1';
166233
167234 $retval = 0;
168235 $returnText = wfShellExec( $cmd, $retval );
169236
170 - if ( $retval ) {
 237+ if ( $this->removeBadFile( $dstPath, $retval ) || $retval ) {
171238 // Filter nonsense
172239 $lines = explode( "\n", str_replace( "\r\n", "\n", $returnText ) );
173240 if ( substr( $lines[0], 0, 6 ) == 'FFmpeg' ) {
@@ -308,8 +375,13 @@
309376 }
310377 }
311378
 379+ static function getMyScriptPath() {
 380+ global $wgScriptPath;
 381+ return "$wgScriptPath/extensions/OggHandler";
 382+ }
 383+
312384 function setHeaders( $out ) {
313 - global $wgScriptPath, $wgOggScriptVersion, $wgCortadoJarFile;
 385+ global $wgOggScriptVersion, $wgCortadoJarFile;
314386 if ( $out->hasHeadItem( 'OggHandler' ) ) {
315387 return;
316388 }
@@ -320,23 +392,24 @@
321393 'ogg-player-videoElement', 'ogg-player-oggPlugin', 'ogg-player-cortado', 'ogg-player-vlc-mozilla',
322394 'ogg-player-vlc-activex', 'ogg-player-quicktime-mozilla', 'ogg-player-quicktime-activex',
323395 'ogg-player-thumbnail', 'ogg-player-selected', 'ogg-use-player', 'ogg-more', 'ogg-download',
324 - 'ogg-desc-link', 'ogg-dismiss' );
 396+ 'ogg-desc-link', 'ogg-dismiss', 'ogg-player-soundthumb' );
325397 $msgValues = array_map( 'wfMsg', $msgNames );
326398 $jsMsgs = Xml::encodeJsVar( (object)array_combine( $msgNames, $msgValues ) );
327399 $cortadoUrl = $wgCortadoJarFile;
 400+ $scriptPath = self::getMyScriptPath();
328401 if( substr( $cortadoUrl, 0, 1 ) != '/'
329402 && substr( $cortadoUrl, 0, 4 ) != 'http' ) {
330 - $cortadoUrl = "$wgScriptPath/extensions/OggHandler/$cortadoUrl";
 403+ $cortadoUrl = "$scriptPath/$cortadoUrl";
331404 }
332405 $encCortadoUrl = Xml::encodeJsVar( $cortadoUrl );
333 - $encSmallFileUrl = Xml::encodeJsVar( "$wgScriptPath/extensions/OggHandler/null_file" );
 406+ $encExtPathUrl = Xml::encodeJsVar( $scriptPath );
334407
335408 $out->addHeadItem( 'OggHandler', <<<EOT
336 -<script type="text/javascript" src="$wgScriptPath/extensions/OggHandler/OggPlayer.js?$wgOggScriptVersion"></script>
 409+<script type="text/javascript" src="$scriptPath/OggPlayer.js?$wgOggScriptVersion"></script>
337410 <script type="text/javascript">
338411 wgOggPlayer.msg = $jsMsgs;
339412 wgOggPlayer.cortadoUrl = $encCortadoUrl;
340 -wgOggPlayer.smallFileUrl = $encSmallFileUrl;
 413+wgOggPlayer.extPathUrl = $encExtPathUrl;
341414 </script>
342415 <style type="text/css">
343416 .ogg-player-options {
@@ -390,47 +463,110 @@
391464
392465 if ( substr( $this->videoUrl, 0, 4 ) != 'http' ) {
393466 global $wgServer;
394 - $encUrl = Xml::encodeJsVar( $wgServer . $this->videoUrl );
 467+ $url = $wgServer . $this->videoUrl;
395468 } else {
396 - $encUrl = Xml::encodeJsVar( $this->videoUrl );
 469+ $url = $this->videoUrl;
397470 }
398471 $length = intval( $this->length );
399472 $width = intval( $this->width );
400473 $height = intval( $this->height );
401 - $alt = empty( $options['alt'] ) ? '' : $options['alt'];
402 - $attribs = array( 'src' => $this->url );
 474+ $alt = empty( $options['alt'] ) ? $this->file->getTitle()->getText() : $options['alt'];
 475+ $scriptPath = OggHandler::getMyScriptPath();
 476+ $thumbDivAttribs = array();
 477+ $showDescIcon = false;
403478 if ( $this->isVideo ) {
404479 $msgStartPlayer = wfMsg( 'ogg-play-video' );
405 - $attribs['width'] = $width;
406 - $attribs['height'] = $height;
 480+ $imgAttribs = array(
 481+ 'src' => $this->url,
 482+ 'width' => $width,
 483+ 'height' => $height );
407484 $playerHeight = $height;
408485 } else {
 486+ // Sound file
 487+ if ( $height > 100 ) {
 488+ // Use a big file icon
 489+ global $wgScriptPath;
 490+ $imgAttribs = array(
 491+ 'src' => "$wgScriptPath/skins/common/images/icons/fileicon-ogg.png",
 492+ 'width' => 125,
 493+ 'height' => 125,
 494+ );
 495+ } else {
 496+ // make an icon later if necessary
 497+ $imgAttribs = false;
 498+ $showDescIcon = true;
 499+ //$thumbDivAttribs = array( 'style' => 'text-align: right;' );
 500+ }
409501 $msgStartPlayer = wfMsg( 'ogg-play-sound' );
410502 $playerHeight = 0;
411 - // Don't add width and height to the icon image, it won't match its true size
412503 }
413504
414 - $thumb = Xml::element( 'img', $attribs, null );
 505+ // Set $thumb to the thumbnail img tag, or the thing that goes where
 506+ // the thumbnail usually goes
 507+ $descIcon = false;
415508 if ( !empty( $options['desc-link'] ) ) {
416509 $linkAttribs = $this->getDescLinkAttribs( $alt );
417 - $thumb = Xml::tags( 'a', $linkAttribs, $thumb );
418 - $encLink = Xml::encodeJsVar( $linkAttribs['href'] );
 510+ if ( $showDescIcon ) {
 511+ // Make image description icon link
 512+ $imgAttribs = array(
 513+ 'src' => "$scriptPath/info.png",
 514+ 'width' => 22,
 515+ 'height' => 22
 516+ );
 517+ $linkAttribs['title'] = wfMsg( 'ogg-desc-link' );
 518+ $descIcon = Xml::tags( 'a', $linkAttribs,
 519+ Xml::element( 'img', $imgAttribs, null ) );
 520+ $thumb = '';
 521+ } else {
 522+ $thumb = Xml::tags( 'a', $linkAttribs,
 523+ Xml::element( 'img', $imgAttribs, null ) );
 524+ }
 525+ $linkUrl = $linkAttribs['href'];
419526 } else {
420527 // We don't respect the file-link option, click-through to download is not appropriate
421 - $encLink = 'false';
 528+ $linkUrl = false;
 529+ if ( $imgAttribs ) {
 530+ $thumb = Xml::element( 'img', $imgAttribs, null );
 531+ } else {
 532+ $thumb = '';
 533+ }
422534 }
423 - $thumb .= "<br/>\n";
424535
425536 $id = "ogg_player_" . OggTransformOutput::$serial;
426537
427 - $s = Xml::tags( 'div', array( 'id' => $id, /*'align' => 'center',*/ 'style' => 'width: ' . $width . 'px' ),
428 - $thumb .
429 - Xml::element( 'button',
430 - array(
431 - 'onclick' => "wgOggPlayer.init(false, '$id', $encUrl, $width, $playerHeight, $length, $encLink);",
432 - ),
433 - $msgStartPlayer
434 - )
 538+ $playerParams = Xml::encodeJsVar( (object)array(
 539+ 'id' => $id,
 540+ 'videoUrl' => $url,
 541+ 'width' => $width,
 542+ 'height' => $playerHeight,
 543+ 'length' => $length,
 544+ 'linkUrl' => $linkUrl,
 545+ 'isVideo' => $this->isVideo ) );
 546+
 547+ $s = Xml::tags( 'div',
 548+ array(
 549+ 'id' => $id,
 550+ 'style' => "width: {$width}px;" ),
 551+ ( $thumb ? Xml::tags( 'div', array(), $thumb ) : '' ) .
 552+ Xml::tags( 'div', array(),
 553+ Xml::tags( 'button',
 554+ array(
 555+ 'onclick' => "wgOggPlayer.init(false, $playerParams);",
 556+ 'style' => "width: {$width}px;",
 557+ 'title' => $msgStartPlayer,
 558+ ),
 559+ Xml::element( 'img',
 560+ array(
 561+ 'src' => "$scriptPath/play.png",
 562+ 'width' => 22,
 563+ 'height' => 22,
 564+ 'alt' => $msgStartPlayer
 565+ ),
 566+ null
 567+ )
 568+ )
 569+ ) .
 570+ ( $descIcon ? Xml::tags( 'div', array(), $descIcon ) : '' )
435571 );
436572 return $s;
437573 }
@@ -443,8 +579,8 @@
444580 }
445581
446582 class OggAudioDisplay extends OggTransformOutput {
447 - function __construct( $file, $videoUrl, $iconUrl, $width, $height, $length ) {
448 - parent::__construct( $file, $videoUrl, $iconUrl, $width, $height, $length, false );
 583+ function __construct( $file, $videoUrl, $width, $height, $length ) {
 584+ parent::__construct( $file, $videoUrl, false, $width, $height, $length, false );
449585 }
450586 }
451587
Index: trunk/extensions/OggHandler/README
@@ -20,9 +20,19 @@
2121
2222 http://www.flumotion.net/cortado/
2323
 24+We have patched Cortado to allow multiple instances to exist on the one page,
 25+the patch is in cortado-tweak.diff. The recompiled binary is at
 26+cortado-ovt-stripped-0.2.2.1-patched.jar .
 27+
2428 The PEAR directory contains a fork of the File_Ogg package, licensed under the
2529 LGPL. The stock File_Ogg will not work -- I have made many aggressive changes
2630 including support for stream formats other than Vorbis. I'll try to get my
2731 changes committed to the official PEAR tree at some point in the future.
2832
 33+The icons play.png, pause.png, stop.png and info.png are from the Crystal Project:
 34+
 35+ http://www.everaldo.com/crystal/
 36+
 37+They are licensed under the LGPL.
 38+
2939 -- Tim Starling
Index: trunk/extensions/OggHandler/OggHandler.i18n.php
@@ -25,6 +25,7 @@
2626 'ogg-player-quicktime-mozilla' => 'QuickTime', # only translate this message to other languages if you have to change it
2727 'ogg-player-quicktime-activex' => 'QuickTime (ActiveX)', # only translate this message to other languages if you have to change it
2828 'ogg-player-thumbnail' => 'Still image only',
 29+ 'ogg-player-soundthumb' => 'No player',
2930 'ogg-player-selected' => '(selected)',
3031 'ogg-use-player' => 'Use player: ',
3132 'ogg-more' => 'More...',
@@ -79,3 +80,10 @@
8081 'ogg-player-oggPlugin' => 'Ogg-plugin',
8182 ),
8283 );
 84+
 85+$magicWords = array(
 86+ 'en' => array(
 87+ 'ogg_noplayer' => array( 0, 'noplayer' ),
 88+ 'ogg_thumbtime' => array( 0, 'thumbtime=$1' ),
 89+ ),
 90+);
Index: trunk/extensions/OggHandler/info.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
Property changes on: trunk/extensions/OggHandler/info.png
___________________________________________________________________
Name: svn:mime-type
8391 + image/png

Follow-up revisions

RevisionCommit summaryAuthorDate
r98208Fix for r87923, which made broken but harmless code from r62223 actually star...tstarling04:19, 27 September 2011

Status & tagging log