MediaWiki r25467 - Code Review

Jump to: navigation, search
Repository:MediaWiki
Revision:r25466‎ | r25467 (on ViewVC)‎ | 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:

Diff [purge]

Index: trunk/extensions/OggHandler/OggHandler.php
===================================================================
--- trunk/extensions/OggHandler/OggHandler.php	(revision 25466)
+++ trunk/extensions/OggHandler/OggHandler.php	(revision 25467)
@@ -22,6 +22,7 @@
 $wgFFmpegLocation = 'ffmpeg';
 $wgExtensionMessagesFiles['OggHandler'] = "$oggDir/OggHandler.i18n.php";
 $wgParserOutputHooks['OggHandler'] = array( 'OggHandler', 'outputHook' );
+$wgHooks['LanguageGetMagic'][] = 'OggHandler::registerMagicWords';
 
 // Filename or URL path to the Cortado Java player applet.
 //
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
   + image/png

Index: trunk/extensions/OggHandler/OggPlayer.js
===================================================================
--- trunk/extensions/OggHandler/OggPlayer.js	(revision 25466)
+++ trunk/extensions/OggHandler/OggPlayer.js	(revision 25467)
@@ -13,21 +13,24 @@
 	// Configuration from MW
 	'msg': {},
 	'cortadoUrl' : '',
-	'smallFileUrl' : '',
+	'extPathUrl' : '',
 	'showPlayerSelect': true,
 	'controlsHeightGuess': 20, 
 
-	// Main entry point: initialise a video player
-	// Player will be created as a child of the given ID
-	// There may be multiple players in a document
-	'init': function ( player, id, videoUrl, width, height, length, linkUrl ) {
-		elt = document.getElementById( id );
+	/**
+	 * Main entry point: initialise a video player
+	 * Player will be created as a child of the given ID
+	 * There may be multiple players in a document.
+	 * Parameters are: id, videoUrl, width, height, length, linkUrl
+	 */
+	'init': function ( player, params ) {
+		elt = document.getElementById( params.id );
 
 		// Save still image HTML
-		if ( !(id in this.savedThumbs) ) {
+		if ( !(params.id in this.savedThumbs) ) {
 			var thumb = document.createDocumentFragment();
 			thumb.appendChild( elt.cloneNode( true ) );
-			this.savedThumbs[id] = thumb;
+			this.savedThumbs[params.id] = thumb;
 		}
 
 		this.detect( elt );
@@ -62,40 +65,41 @@
 			
 		switch ( player ) {
 			case 'videoElement':
-				this.embedVideoElement( elt, videoUrl, width, height, length );
+				this.embedVideoElement( elt, params );
 				break;
 			case 'oggPlugin':
-				this.embedOggPlugin( elt, videoUrl, width, height, length );
+				this.embedOggPlugin( elt, params );
 				break;
 			case 'vlc-mozilla':
-				this.embedVlcPlugin( elt, videoUrl, width, height, length );
+				this.embedVlcPlugin( elt, params );
 				break;
 			case 'vlc-activex':
-				this.embedVlcActiveX( elt, videoUrl, width, height, length );
+				this.embedVlcActiveX( elt, params );
 				break;
 			case 'cortado':
-				this.embedCortado( elt, videoUrl, width, height, length );
+				this.embedCortado( elt, params );
 				break;
 			case 'quicktime-mozilla':
-				this.embedQuicktimePlugin( elt, videoUrl, width, height, length );
+				this.embedQuicktimePlugin( elt, params );
 				break;
 			case 'thumbnail':
-				if ( id in this.savedThumbs ) {
-					elt.appendChild( this.savedThumbs[id].cloneNode( true ) );
+				if ( params.id in this.savedThumbs ) {
+					elt.appendChild( this.savedThumbs[params.id].cloneNode( true ) );
 				} else {
-					elt.appendChild( document.createTextNode( 'Missing saved thumbnail for ' + id ) );
+					elt.appendChild( document.createTextNode( 'Missing saved thumbnail for ' + params.id ) );
 				}
 				break;
 			default:
-				elt.innerHTML = this.msg['ogg-no-player'] + '<br/>';
+				elt.innerHTML = '<div>' + this.msg['ogg-no-player'] + '</div>';
 				player = 'none';
 		}
 		if ( player != 'thumbnail' ) {
-			var optionsBox = this.makeOptionsBox( player, id, videoUrl, width, height, length, linkUrl );
-			var optionsLink = this.makeOptionsLink( id );
-			elt.appendChild( document.createElement( 'br' ) );
-			elt.appendChild( optionsBox );
-			elt.appendChild( optionsLink );
+			var optionsBox = this.makeOptionsBox( player, params );
+			var optionsLink = this.makeOptionsLink( params.id );
+			var div = document.createElement( 'div' );
+			div.appendChild( optionsBox );
+			div.appendChild( optionsLink );
+			elt.appendChild( div );
 		}
 	},
 
@@ -197,22 +201,22 @@
 		}
 	},
 
-	'makeOptionsBox' : function ( selectedPlayer, id, videoUrl, width, height, length, linkUrl ) {
+	'makeOptionsBox' : function ( selectedPlayer, params ) {
 		var div, p, a, ul, li, button;
 
 		div = document.createElement( 'div' );
-		div.style.cssText = "width: " + ( width - 10 ) + "px; display: none;";
+		div.style.cssText = "width: " + ( params.width - 10 ) + "px; display: none;";
 		div.className = 'ogg-player-options';
-		div.id = id + '_options_box';
+		div.id = params.id + '_options_box';
 		div.align = 'center';
 
 		ul = document.createElement( 'ul' );
 
 		// Description page link
-		if ( linkUrl ) {
+		if ( params.linkUrl ) {
 			li = document.createElement( 'li' );
 			a = document.createElement( 'a' );
-			a.href = linkUrl;
+			a.href = params.linkUrl;
 			a.appendChild( document.createTextNode( this.msg['ogg-desc-link'] ) );
 			li.appendChild( a );
 			ul.appendChild( li );
@@ -221,7 +225,7 @@
 		// Download link
 		li = document.createElement( 'li' );
 		a = document.createElement( 'a' );
-		a.href = videoUrl;
+		a.href = params.videoUrl;
 		a.appendChild( document.createTextNode( this.msg['ogg-download'] ) );
 		li.appendChild( a );
 		ul.appendChild( li );
@@ -236,15 +240,21 @@
 		// Make player list
 		ul = document.createElement( 'ul' );
 		for ( var i = 0; i < this.players.length + 1; i++ ) {
-			var player;
+			var player, playerMsg;
 			if ( i == this.players.length ) {
 				player = 'thumbnail';
+				if ( params.isVideo ) {
+					playerMsg = 'ogg-player-thumbnail';
+				} else {
+					playerMsg = 'ogg-player-soundthumb';
+				}
 			} else {
 				player = this.players[i];
 				// Skip unsupported players
 				if ( ! this.clientSupports[player] ) {
 					continue;
 				}
+				playerMsg = 'ogg-player-' + player;
 			}
 
 			// Make list item
@@ -252,13 +262,13 @@
 			if ( player == selectedPlayer ) {
 				var strong = document.createElement( 'strong' );
 				strong.appendChild( document.createTextNode( 
-					this.msg['ogg-player-' + player] + ' ' + this.msg['ogg-player-selected'] ) );
+					this.msg[playerMsg] + ' ' + this.msg['ogg-player-selected'] ) );
 				li.appendChild( strong );
 			} else {
 				a = document.createElement( 'a' );
 				a.href = 'javascript:void("' + player + '")';
-				a.onclick = this.makePlayerFunction( player, id, videoUrl, width, height, length, linkUrl );
-				a.appendChild( document.createTextNode( this.msg['ogg-player-' + player] ) );
+				a.onclick = this.makePlayerFunction( player, params );
+				a.appendChild( document.createTextNode( this.msg[playerMsg] ) );
 				li.appendChild( a );
 			}
 			ul.appendChild( li );
@@ -269,7 +279,7 @@
 		div2.style.cssText = 'text-align: center;';
 		button = document.createElement( 'button' );
 		button.appendChild( document.createTextNode( this.msg['ogg-dismiss'] ) );
-		button.onclick = this.makeDismissFunction( id );
+		button.onclick = this.makeDismissFunction( params.id );
 		div2.appendChild( button );
 		div.appendChild( div2 );
 
@@ -319,165 +329,172 @@
 		}
 	},
 
-	'makePlayerFunction' : function ( player, id, videoUrl, width, height, length, linkUrl ) {
+	'makePlayerFunction' : function ( player, params ) {
 		var this_ = this;
 		return function () {
 			if ( player != 'thumbnail' ) {
 				document.cookie = "ogg_player=" + player;
 			}
-			this_.init( player, id, videoUrl, width, height, length, linkUrl );
+			this_.init( player, params );
 		};
 	},
 
-	'newButton': function ( caption, callback ) {
+	'newButton': function ( caption, image, callback ) {
 		var elt = document.createElement('input');
-		elt.type = 'button';
-		elt.value = this.msg[caption];
+		elt.type = 'image';
+		elt.src = this.extPathUrl + '/' + image;
+		elt.alt = elt.value = elt.title = this.msg[caption];
 		elt.onclick = callback;
 		return elt;
 	},
 
 	'newPlayButton': function ( videoElt ) {
-		return this.newButton( 'ogg-play', function () { videoElt.play(); } );
+		return this.newButton( 'ogg-play', 'play.png', function () { videoElt.play(); } );
 	},
 
 	'newPauseButton': function ( videoElt ) {
-		return this.newButton( 'ogg-pause', function () { videoElt.pause(); } );
+		return this.newButton( 'ogg-pause', 'pause.png', function () { videoElt.pause(); } );
 	},
 
 	'newStopButton': function ( videoElt ) {
-		return this.newButton( 'ogg-stop', function () { videoElt.stop(); } );
+		return this.newButton( 'ogg-stop', 'stop.png', function () { videoElt.stop(); } );
 	},
 
-	'embedVideoElement': function ( elt, videoUrl, width, height, length ) {
+	'embedVideoElement': function ( elt, params ) {
 		var videoElt = document.createElement('video');
-		videoElt.setAttribute( 'width', width );
-		videoElt.setAttribute( 'height', height + this.controlsHeightGuess );
-		videoElt.setAttribute( 'src', videoUrl );
+		videoElt.setAttribute( 'width', params.width );
+		videoElt.setAttribute( 'height', params.height + this.controlsHeightGuess );
+		videoElt.setAttribute( 'src', params.videoUrl );
 		videoElt.setAttribute( 'autoplay', '1' );
 		videoElt.setAttribute( 'controls', '1' );
-		elt.appendChild( videoElt );
+		var div = document.createElement( 'div' );
+		div.appendChild( videoElt );
+		elt.appendChild( div );
 
 		// Try to detect implementations that don't support controls
 		// This works for the Opera test build
 		if ( !videoElt.controls ) {
-			elt.appendChild( document.createElement( 'br' ) );
-			elt.appendChild( this.newPlayButton( videoElt ) );
-			elt.appendChild( this.newPauseButton( videoElt ) );
-			elt.appendChild( this.newStopButton( videoElt ) );
+			div = document.createElement( 'div' );
+			div.appendChild( this.newPlayButton( videoElt ) );
+			div.appendChild( this.newPauseButton( videoElt ) );
+			div.appendChild( this.newStopButton( videoElt ) );
+			elt.appendChild( div );
 			//videoElt.play();
 		}
 	},
 
-	'embedOggPlugin': function ( elt, videoUrl, width, height, length ) {
+	'embedOggPlugin': function ( elt, params ) {
 		var id = elt.id + "_obj";
 		elt.innerHTML += 
-			"<object id=" + this.hq( id ) + 
+			"<div><object id=" + this.hq( id ) + 
 			" type='application/ogg'" +
-			" width=" + this.hq( width ) + 
-			" height=" + this.hq( height + this.controlsHeightGuess ) + 
-			" data=" + this.hq( videoUrl ) + "></object>";
+			" width=" + this.hq( params.width ) + 
+			" height=" + this.hq( params.height + this.controlsHeightGuess ) + 
+			" data=" + this.hq( params.videoUrl ) + "></object></div>";
 	},
 
-	'embedVlcPlugin' : function ( elt, videoUrl, width, height, length ) {
+	'embedVlcPlugin' : function ( elt, params ) {
 		var id = elt.id + "_obj";
 		elt.innerHTML += 	
-			"<object id=" + this.hq( id ) + 
+			"<div><object id=" + this.hq( id ) + 
 			" type='application/x-vlc-plugin'" +
-			" width=" + this.hq( width ) + 
-			" height=" + this.hq( height ) + 
-			" data=" + this.hq( videoUrl ) + "></object>";
+			" width=" + this.hq( params.width ) + 
+			" height=" + this.hq( params.height ) + 
+			" data=" + this.hq( params.videoUrl ) + "></object></div>";
 		
 		var videoElt = document.getElementById( id );
-		elt.appendChild( document.createElement( 'br' ) );
+		var div = document.createElement( 'div' );
 		// TODO: seek bar
-		elt.appendChild( this.newPlayButton( videoElt ) );
-		elt.appendChild( this.newPauseButton( videoElt ) );
-		elt.appendChild( this.newStopButton( videoElt ) );
+		div.appendChild( this.newPlayButton( videoElt ) );
+		div.appendChild( this.newPauseButton( videoElt ) );
+		div.appendChild( this.newStopButton( videoElt ) );
+		elt.appendChild( div );
 	},
 
-	'embedVlcActiveX' : function ( elt, videoUrl, width, height, length ) {
+	'embedVlcActiveX' : function ( elt, params ) {
 		var id = elt.id + "_obj";
 
 		var html = 
-			'<object id=' + this.hq( id ) + 
+			'<div><object id=' + this.hq( id ) + 
 			' classid="clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921"' + 
 			' codebase="http://downloads.videolan.org/pub/videolan/vlc/latest/win32/axvlc.cab#Version=0,8,6,0"' + 
-			' width=' + this.hq( width ) + 
-			' height=' + this.hq( height ) + 
-			' style="width: ' + this.hx( width ) + 'px; height: ' + this.hx( height ) + 'px;"' +
+			' width=' + this.hq( params.width ) + 
+			' height=' + this.hq( params.height ) + 
+			' style="width: ' + this.hx( params.width ) + 'px; height: ' + this.hx( params.height ) + 'px;"' +
 			">" + 
-			'<param name="mrl" value=' + this.hq( videoUrl ) + '/>' + 
-			'</object>';
+			'<param name="mrl" value=' + this.hq( params.videoUrl ) + '/>' + 
+			'</object></div>';
 		elt.innerHTML += html;
 
 		var videoElt = document.getElementById( id );
 
 		// IE says "sorry, I wasn't listening, what were the dimensions again?"
-		if ( width && height ) {
-			videoElt.width = width;
-			videoElt.height = height;
-			videoElt.style.width = width + 'px';
-			videoElt.style.height = height + 'px';
+		if ( params.width && params.height ) {
+			videoElt.width = params.width;
+			videoElt.height = params.height;
+			videoElt.style.width = params.width + 'px';
+			videoElt.style.height = params.height + 'px';
 		}
-
-		elt.appendChild( document.createElement( 'br' ) );
+		var div = document.createElement( 'div' );
 		// TODO: seek bar
-		elt.appendChild( this.newButton( 'ogg-play', function() { videoElt.playlist.play(); } ) );
+		div.appendChild( this.newButton( 'ogg-play', 'play.png', function() { videoElt.playlist.play(); } ) );
 		// FIXME: playlist.pause() doesn't work
-		elt.appendChild( this.newButton( 'ogg-stop', function() { videoElt.playlist.stop(); } ) );
+		div.appendChild( this.newButton( 'ogg-stop', 'stop.png', function() { videoElt.playlist.stop(); } ) );
+		elt.appendChild( div );
 	},
 
-	'embedCortado' : function ( elt, videoUrl, width, height, length ) {
+	'embedCortado' : function ( elt, params ) {
 		var statusHeight = 18;
 		// Given extra vertical space, cortado centres the video and then overlays the status 
 		// line, leaving an ugly black bar at the top. So we don't give it any.
-		var playerHeight = height < statusHeight ? statusHeight : height;
+		var playerHeight = params.height < statusHeight ? statusHeight : params.height;
 
 		// Create the applet all at once
 		// In Opera, document.createElement('applet') immediately creates
 		// a non-working applet with unchangeable parameters, similar to the 
 		// problem with IE and ActiveX. 
-		elt.innerHTML = 
+		elt.innerHTML = '<div>' +
 		    '<applet code="com.fluendo.player.Cortado.class" ' +
-		    '      width=' + this.hq( width ) +
+		    '      width=' + this.hq( params.width ) +
 		    '      height=' + this.hq( playerHeight ) + 
 		    '      archive=' + this.hq( this.cortadoUrl ) + '>' +
-		    '  <param name="url"  value=' + this.hq( videoUrl ) + '/>' +
-		    '  <param name="duration"  value=' + this.hq( length ) + '/>' +
+		    '  <param name="url"  value=' + this.hq( params.videoUrl ) + '/>' +
+		    '  <param name="duration"  value=' + this.hq( params.length ) + '/>' +
 		    '  <param name="seekable"  value="true"/>' +
 		    '  <param name="autoPlay" value="true"/>' +
 		    '  <param name="showStatus"  value="show"/>' +
 		    '  <param name="statusHeight"  value="' + statusHeight + '"/>' +
-		    '</applet>';
+		    '</applet>' +
+			'</div>';
 
 		// Disable autoPlay in the DOM right now, to prevent Mozilla from 
 		// restarting an arbitrary number of applet instances on a back button click.
 		// Unfortunately this means that some clients (e.g. Opera) won't autoplay at all
-		var videoElt = elt.getElementsByTagName( 'applet' )[0];
+		var videoElt = elt.getElementsByTagName( 'div' ) [0] . 
+			getElementsByTagName( 'applet' )[0];
 		this.setParam( videoElt, 'autoPlay', '' );
 	},
 
-	'embedQuicktimePlugin': function ( elt, videoUrl, width, height, length ) {
+	'embedQuicktimePlugin': function ( elt, params ) {
 		var id = elt.id + "_obj";
 		var controllerHeight = 16; // by observation
 		elt.innerHTML += 
-			"<object id=" + this.hq( id ) + 
+			"<div><object id=" + this.hq( id ) + 
 			" type='video/quicktime'" +
-			" width=" + this.hq( width ) + 
-			" height=" + this.hq( height + controllerHeight ) + 
+			" width=" + this.hq( params.width ) + 
+			" height=" + this.hq( params.height + controllerHeight ) + 
 			
 			// Use QTSRC parameter instead of data attribute to allow progressive download
 			// The data attribute and src parameter point to a small file, as recommended in
 			// http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_HTML/QTScripting_HTML_Document/chapter_1000_section_6.html
-			" data=" + this.hq( this.smallFileUrl ) +
+			" data=" + this.hq( this.extPathUrl + '/null_file' ) +
 			">" + 
 			// Scale, don't clip
 			"<param name='SCALE' value='Aspect'/>" + 
 			"<param name='AUTOPLAY' value='True'/>" +
-			"<param name='src' value=" + this.hq( this.smallFileUrl ) +  "/>" +
-			"<param name='QTSRC' value=" + this.hq( videoUrl ) + "/>" +
-			"</object>";
+			"<param name='src' value=" + this.hq( this.extPathUrl + '/null_file' ) +  "/>" +
+			"<param name='QTSRC' value=" + this.hq( params.videoUrl ) + "/>" +
+			"</object></div>";
 
 		// Disable autoplay on back button
 		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
   + 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
   + image/png

Index: trunk/extensions/OggHandler/OggHandler_body.php
===================================================================
--- trunk/extensions/OggHandler/OggHandler_body.php	(revision 25466)
+++ trunk/extensions/OggHandler/OggHandler_body.php	(revision 25467)
@@ -1,8 +1,12 @@
 <?php
 
+// TODO: Fix core printable stylesheet. Descendant selectors suck.
+
 class OggHandler extends MediaHandler {
 	const OGG_METADATA_VERSION = 1;
 
+	static $magicDone = false;
+
 	var $videoTypes = array( 'Theora' );
 	var $audioTypes = array( 'Vorbis', 'Speex', 'FLAC' );
 
@@ -10,36 +14,80 @@
 		return true;
 	}
 
+	static function registerMagicWords( &$magicData, $code ) {
+		wfLoadExtensionMessages( 'OggHandler' );
+		return true;
+	}
+
 	function getParamMap() {
-		// TODO: add thumbtime, noplayer
-		return array( 'img_width' => 'width' );
+		wfLoadExtensionMessages( 'OggHandler' );
+		return array( 
+			'img_width' => 'width',
+			'ogg_noplayer' => 'noplayer',
+			'ogg_thumbtime' => 'thumbtime',
+		);
 	}
 
 	function validateParam( $name, $value ) {
-		// TODO
+		if ( $name == 'thumbtime' ) {
+			if ( $this->parseTimeString( $value ) === false ) {
+				return false;
+			}
+		}
 		return true;
 	}
 
+	function parseTimeString( $seekString, $length = false ) {
+		$parts = explode( ':', $seekString );
+		$time = 0;
+		for ( $i = 0; $i < count( $parts ); $i++ ) {
+			if ( !is_numeric( $parts[$i] ) ) {
+				return false;
+			}
+			$time += intval( $parts[$i] ) * pow( 60, count( $parts ) - $i - 1 );
+		}
+		
+		if ( $time < 0 ) {
+			wfDebug( __METHOD__.": specified negative time, using zero\n" );
+			$time = 0;
+		} elseif ( $length !== false && $time > $length - 1 ) {
+			wfDebug( __METHOD__.": specified near-end or past-the-end time {$time}s, using end minus 1s\n" );
+			$time = $length - 1;
+		}
+		return $time;
+	}
+
 	function makeParamString( $params ) {
-		// No parameters just yet, the thumbnails are always full-size
-		return '';
-		/*
-		$s = '';
-		foreach ( $params as $name => $value ) {
-			if ( $s !== '' ) {
-				$s .= '-';
+		if ( isset( $params['thumbtime'] ) ) {
+			$time = $this->parseTimeString( $params['thumbtime'] );
+			if ( $time !== false ) {
+				return 'seek=' . $time;
 			}
-			$s .= "$name=$value";
-		}*/
+		}
+		return 'mid';
 	}
 
 	function parseParamString( $str ) {
-		// TODO
+		$m = false;
+		if ( preg_match( '/^seek=(\d+)$/', $str, $m ) ) {
+			return array( 'thumbtime' => $m[0] );
+		}
 		return array();
 	}
 
 	function normaliseParams( $image, &$params ) {
-		// TODO
+		if ( isset( $params['thumbtime'] ) ) {
+			$length = $this->getLength( $image );
+			$time = $this->parseTimeString( $params['thumbtime'] );
+			if ( $time === false ) {
+				return false;
+			} elseif ( $time > $length - 1 ) {
+				$params['thumbtime'] = $length - 1;
+			} elseif ( $time <= 0 ) {
+				$params['thumbtime'] = 0;
+			}
+		}
+
 		return true;
 	}
 
@@ -124,32 +172,52 @@
 	function doTransform( $file, $dstPath, $dstUrl, $params, $flags = 0 ) {
 		global $wgFFmpegLocation;
 
-		// Hack for miscellaneous callers
-		global $wgOut;
-		$this->setHeaders( $wgOut );
-
 		$width = $params['width'];
 		$srcWidth = $file->getWidth();
 		$srcHeight = $file->getHeight();
 		$height = $srcWidth == 0 ? $srcHeight : $width * $srcHeight / $srcWidth;
 		$length = $this->getLength( $file );
+		$noPlayer = isset( $params['noplayer'] );
 
+		if ( !$noPlayer ) {
+			// Hack for miscellaneous callers
+			global $wgOut;
+			$this->setHeaders( $wgOut );
+		}
+
 		if ( $srcHeight == 0 || $srcWidth == 0 ) {
 			// Make audio player
-			$icon = $file->iconThumb();
+			if ( $noPlayer ) {
+				$scriptPath = self::getMyScriptPath();
+				return new ThumbnailImage( $file, "$scriptPath/info.png", 22, 22 );
+			}
 			if ( empty( $params['width'] ) ) {
 				$width = 200;
 			} else {
 				$width = $params['width'];
 			}
-			$height = $icon->getHeight();
-			return new OggAudioDisplay( $file, $file->getURL(), $icon->getUrl(), $width, $height, $length );
+			$height = empty( $params['height'] ) ? 20 : $params['height'];
+			return new OggAudioDisplay( $file, $file->getURL(), $width, $height, $length );
 		}
 
+		// Video thumbnail only
+		if ( $noPlayer ) {
+			return new ThumbnailImage( $file, $dstUrl, $width, $height, $dstPath );
+		}
+
 		if ( $flags & self::TRANSFORM_LATER ) {
 			return new OggVideoDisplay( $file, $file->getURL(), $dstUrl, $width, $height, $length );
 		}
 
+		$thumbTime = false;
+		if ( isset( $params['thumbtime'] ) ) {
+			$thumbTime = $this->parseTimeString( $params['thumbtime'], $length );
+		}
+		if ( $thumbTime === false ) {
+			# Seek to midpoint by default, it tends to be more interesting than the start
+			$thumbTime = $length / 2;
+		}
+
 		wfMkdirParents( dirname( $dstPath ) );
 
 		wfDebug( "Creating video thumbnail at $dstPath\n" );
@@ -159,14 +227,13 @@
 			# MJPEG, that's the same as JPEG except it's supported by the windows build of ffmpeg
 			# No audio, one frame
 			' -f mjpeg -an -vframes 1' .
-			# Seek to midpoint, it tends to be more interesting than the fade in at the start
-			' -ss ' . intval( $length / 2 ) . ' ' .
+			' -ss ' . intval( $thumbTime ) . ' ' .
 			wfEscapeShellArg( $dstPath ) . ' 2>&1';
 
 		$retval = 0;
 		$returnText = wfShellExec( $cmd, $retval );
 
-		if ( $retval ) {
+		if ( $this->removeBadFile( $dstPath, $retval ) || $retval ) {
 			// Filter nonsense
 			$lines = explode( "\n", str_replace( "\r\n", "\n", $returnText ) );
 			if ( substr( $lines[0], 0, 6 ) == 'FFmpeg' ) {
@@ -308,8 +375,13 @@
 		}
 	}
 
+	static function getMyScriptPath() {
+		global $wgScriptPath;
+		return "$wgScriptPath/extensions/OggHandler";
+	}
+
 	function setHeaders( $out ) {
-		global $wgScriptPath, $wgOggScriptVersion, $wgCortadoJarFile;
+		global $wgOggScriptVersion, $wgCortadoJarFile;
 		if ( $out->hasHeadItem( 'OggHandler' ) ) {
 			return;
 		}
@@ -320,23 +392,24 @@
 			'ogg-player-videoElement', 'ogg-player-oggPlugin', 'ogg-player-cortado', 'ogg-player-vlc-mozilla', 
 			'ogg-player-vlc-activex', 'ogg-player-quicktime-mozilla', 'ogg-player-quicktime-activex',
 			'ogg-player-thumbnail', 'ogg-player-selected', 'ogg-use-player', 'ogg-more', 'ogg-download',
-	   		'ogg-desc-link', 'ogg-dismiss' );
+	   		'ogg-desc-link', 'ogg-dismiss', 'ogg-player-soundthumb' );
 		$msgValues = array_map( 'wfMsg', $msgNames );
 		$jsMsgs = Xml::encodeJsVar( (object)array_combine( $msgNames, $msgValues ) );
 		$cortadoUrl = $wgCortadoJarFile;
+		$scriptPath = self::getMyScriptPath();
 		if( substr( $cortadoUrl, 0, 1 ) != '/'
 			&& substr( $cortadoUrl, 0, 4 ) != 'http' ) {
-			$cortadoUrl = "$wgScriptPath/extensions/OggHandler/$cortadoUrl";
+			$cortadoUrl = "$scriptPath/$cortadoUrl";
 		}
 		$encCortadoUrl = Xml::encodeJsVar( $cortadoUrl );
-		$encSmallFileUrl = Xml::encodeJsVar( "$wgScriptPath/extensions/OggHandler/null_file" );
+		$encExtPathUrl = Xml::encodeJsVar( $scriptPath );
 
 		$out->addHeadItem( 'OggHandler', <<<EOT
-<script type="text/javascript" src="$wgScriptPath/extensions/OggHandler/OggPlayer.js?$wgOggScriptVersion"></script>
+<script type="text/javascript" src="$scriptPath/OggPlayer.js?$wgOggScriptVersion"></script>
 <script type="text/javascript">
 wgOggPlayer.msg = $jsMsgs;
 wgOggPlayer.cortadoUrl = $encCortadoUrl;
-wgOggPlayer.smallFileUrl = $encSmallFileUrl;
+wgOggPlayer.extPathUrl = $encExtPathUrl;
 </script>
 <style type="text/css">
 .ogg-player-options {
@@ -390,47 +463,110 @@
 
 		if ( substr( $this->videoUrl, 0, 4 ) != 'http' ) {
 			global $wgServer;
-			$encUrl = Xml::encodeJsVar( $wgServer . $this->videoUrl );
+			$url = $wgServer . $this->videoUrl;
 		} else {
-			$encUrl = Xml::encodeJsVar( $this->videoUrl );
+			$url = $this->videoUrl;
 		}
 		$length = intval( $this->length );
 		$width = intval( $this->width );
 		$height = intval( $this->height );
-		$alt = empty( $options['alt'] ) ? '' : $options['alt'];
-		$attribs = array( 'src' => $this->url );
+		$alt = empty( $options['alt'] ) ? $this->file->getTitle()->getText() : $options['alt'];
+		$scriptPath = OggHandler::getMyScriptPath();
+		$thumbDivAttribs = array();
+		$showDescIcon = false;
 		if ( $this->isVideo ) {
 			$msgStartPlayer = wfMsg( 'ogg-play-video' );
-			$attribs['width'] = $width;
-			$attribs['height'] = $height;
+			$imgAttribs = array( 
+				'src' => $this->url,
+				'width' => $width,
+				'height' => $height );
 			$playerHeight = $height;
 		} else {
+			// Sound file
+			if ( $height > 100 ) {
+				// Use a big file icon
+				global $wgScriptPath;
+				$imgAttribs = array( 
+					'src' => "$wgScriptPath/skins/common/images/icons/fileicon-ogg.png",
+					'width' => 125,
+					'height' => 125,
+				);
+			} else {
+				 // make an icon later if necessary
+				$imgAttribs = false;
+				$showDescIcon = true;
+				//$thumbDivAttribs = array( 'style' => 'text-align: right;' );
+			}
 			$msgStartPlayer = wfMsg( 'ogg-play-sound' );
 			$playerHeight = 0;
-			// Don't add width and height to the icon image, it won't match its true size
 		}
 
-		$thumb = Xml::element( 'img', $attribs, null );
+		// Set $thumb to the thumbnail img tag, or the thing that goes where 
+		// the thumbnail usually goes
+		$descIcon = false;
 		if ( !empty( $options['desc-link'] ) ) {
 			$linkAttribs = $this->getDescLinkAttribs( $alt );
-			$thumb = Xml::tags( 'a', $linkAttribs, $thumb );
-			$encLink = Xml::encodeJsVar( $linkAttribs['href'] );
+			if ( $showDescIcon ) {
+				// Make image description icon link
+				$imgAttribs = array( 
+					'src' => "$scriptPath/info.png",
+					'width' => 22,
+					'height' => 22
+				);
+				$linkAttribs['title'] = wfMsg( 'ogg-desc-link' );
+				$descIcon = Xml::tags( 'a', $linkAttribs, 
+					Xml::element( 'img', $imgAttribs, null ) );
+				$thumb = '';
+			} else {
+				$thumb = Xml::tags( 'a', $linkAttribs, 
+					Xml::element( 'img', $imgAttribs, null ) );
+			}
+			$linkUrl = $linkAttribs['href'];
 		} else {
 			// We don't respect the file-link option, click-through to download is not appropriate
-			$encLink = 'false';
+			$linkUrl = false;
+			if ( $imgAttribs ) {
+				$thumb = Xml::element( 'img', $imgAttribs, null );
+			} else {
+				$thumb = '';
+			}
 		}
-		$thumb .= "<br/>\n";
 
 		$id = "ogg_player_" . OggTransformOutput::$serial;
 
-		$s = Xml::tags( 'div', array( 'id' => $id, /*'align' => 'center',*/ 'style' => 'width: ' . $width . 'px' ), 
-			$thumb .
-			Xml::element( 'button', 
-				array(
-					'onclick' => "wgOggPlayer.init(false, '$id', $encUrl, $width, $playerHeight, $length, $encLink);",
-				), 
-				$msgStartPlayer
-			)
+		$playerParams = Xml::encodeJsVar( (object)array(
+			'id' => $id,
+			'videoUrl' => $url,
+			'width' => $width,
+			'height' => $playerHeight,
+			'length' => $length,
+			'linkUrl' => $linkUrl,
+			'isVideo' => $this->isVideo ) );
+
+		$s = Xml::tags( 'div', 
+			array( 
+				'id' => $id, 
+				'style' => "width: {$width}px;" ), 
+			( $thumb ? Xml::tags( 'div', array(), $thumb ) : '' ) .
+			Xml::tags( 'div', array(), 
+				Xml::tags( 'button', 
+					array(
+						'onclick' => "wgOggPlayer.init(false, $playerParams);",
+						'style' => "width: {$width}px;",
+						'title' => $msgStartPlayer,
+					), 
+					Xml::element( 'img', 
+						array( 
+							'src' => "$scriptPath/play.png",
+							'width' => 22,
+							'height' => 22,
+							'alt' => $msgStartPlayer
+						), 
+						null 
+					)
+				)
+			) .
+			( $descIcon ? Xml::tags( 'div', array(), $descIcon ) : '' )
 		);
 		return $s;
 	}
@@ -443,8 +579,8 @@
 }
 
 class OggAudioDisplay extends OggTransformOutput {
-	function __construct( $file, $videoUrl, $iconUrl, $width, $height, $length ) {
-		parent::__construct( $file, $videoUrl, $iconUrl, $width, $height, $length, false );
+	function __construct( $file, $videoUrl, $width, $height, $length ) {
+		parent::__construct( $file, $videoUrl, false, $width, $height, $length, false );
 	}
 }
 
Index: trunk/extensions/OggHandler/README
===================================================================
--- trunk/extensions/OggHandler/README	(revision 25466)
+++ trunk/extensions/OggHandler/README	(revision 25467)
@@ -20,9 +20,19 @@
 
     http://www.flumotion.net/cortado/
 
+We have patched Cortado to allow multiple instances to exist on the one page, 
+the patch is in cortado-tweak.diff. The recompiled binary is at 
+cortado-ovt-stripped-0.2.2.1-patched.jar .
+
 The PEAR directory contains a fork of the File_Ogg package, licensed under the
 LGPL. The stock File_Ogg will not work -- I have made many aggressive changes
 including support for stream formats other than Vorbis. I'll try to get my
 changes committed to the official PEAR tree at some point in the future.
 
+The icons play.png, pause.png, stop.png and info.png are from the Crystal Project:
+
+   http://www.everaldo.com/crystal/
+
+They are licensed under the LGPL. 
+
 -- Tim Starling
Index: trunk/extensions/OggHandler/OggHandler.i18n.php
===================================================================
--- trunk/extensions/OggHandler/OggHandler.i18n.php	(revision 25466)
+++ trunk/extensions/OggHandler/OggHandler.i18n.php	(revision 25467)
@@ -25,6 +25,7 @@
 		'ogg-player-quicktime-mozilla' => 'QuickTime', # only translate this message to other languages if you have to change it
 		'ogg-player-quicktime-activex' => 'QuickTime (ActiveX)', # only translate this message to other languages if you have to change it
 		'ogg-player-thumbnail' => 'Still image only',
+		'ogg-player-soundthumb' => 'No player',
 		'ogg-player-selected'  => '(selected)',
 		'ogg-use-player'       => 'Use player: ',
 		'ogg-more'             => 'More...',
@@ -79,3 +80,10 @@
 		'ogg-player-oggPlugin' => 'Ogg-plugin',
 	),
 );
+
+$magicWords = array(
+	'en' => array(
+		'ogg_noplayer' => array( 0, 'noplayer' ),
+		'ogg_thumbtime' => array( 0, 'thumbtime=$1' ),
+	),
+);
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
   + image/png

Image changes

Index: /trunk/extensions/OggHandler/info.png
added
Index: /trunk/extensions/OggHandler/pause.png
added
Index: /trunk/extensions/OggHandler/play.png
added
Index: /trunk/extensions/OggHandler/stop.png
added

Follow-up revisions

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

Status & tagging log

  • 15:20, 12 September 2011 Meno25 (Talk | contribs) changed the status of r25467 [removed: ok added: old]
Personal tools
Namespaces
Variants
Views
Actions
Site
Support
Download
Development
Communication
Toolbox