Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.php |
— | — | @@ -40,6 +40,7 @@ |
41 | 41 | // Transcode support |
42 | 42 | $wgAutoloadClasses['WebVideoTranscode'] = "$timedMediaDir/WebVideoTranscode/WebVideoTranscode.php"; |
43 | 43 | $wgAutoloadClasses['WebVideoTranscodeJob'] = "$timedMediaDir/WebVideoTranscode/WebVideoTranscodeJob.php"; |
| 44 | +$wgAutoloadClasses['ApiQueryVideoInfo'] = "$timedMediaDir/ApiQueryVideoInfo.php"; |
44 | 45 | |
45 | 46 | // Register the Timed Media Handler javascript resources ( MwEmbed modules ) |
46 | 47 | MwEmbedResourceManager::register( 'extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer' ); |
Index: trunk/extensions/TimedMediaHandler/WebVideoTranscode/WebVideoTranscode.php |
— | — | @@ -157,12 +157,17 @@ |
158 | 158 | * If no transcode is in progress or ready add the job to the jobQueue |
159 | 159 | * |
160 | 160 | * @param {Object} File object |
| 161 | + * @param {Object} Options, a set of options: |
| 162 | + * 'nodata' Strips the data- attribute, useful when your output is not html |
161 | 163 | * @returns an associative array of sources suitable for <source> tag output |
162 | 164 | */ |
163 | | - static public function getSources( &$file ){ |
| 165 | + static public function getSources( &$file , $options=array() ){ |
164 | 166 | global $wgEnabledTranscodeSet, $wgLang; |
165 | 167 | $sources = array(); |
166 | 168 | |
| 169 | + // Setup options |
| 170 | + $dataPrefix = in_array( 'nodata', $options )? '': 'data-'; |
| 171 | + |
167 | 172 | // Add the original file: |
168 | 173 | $source = array( |
169 | 174 | 'src' => $file->getUrl(), |
— | — | @@ -172,23 +177,23 @@ |
173 | 178 | $wgLang->formatNum( $file->getHeight() ), |
174 | 179 | $wgLang->formatBitrate( $file->getHandler()->getBitrate( $file ) ) |
175 | 180 | ), |
176 | | - 'data-shorttitle' => wfMsg('timedmedia-source-file', wfMsg( 'timedmedia-' . $file->getHandler()->getMetadataType() ) ), |
| 181 | + "{$dataPrefix}shorttitle" => wfMsg('timedmedia-source-file', wfMsg( 'timedmedia-' . $file->getHandler()->getMetadataType() ) ), |
177 | 182 | |
178 | | - 'data-width' => $file->getWidth(), |
179 | | - 'data-height' => $file->getHeight(), |
| 183 | + "{$dataPrefix}width" => $file->getWidth(), |
| 184 | + "{$dataPrefix}height" => $file->getHeight(), |
180 | 185 | // TODO add some title and data about the file |
181 | 186 | ); |
182 | 187 | |
183 | | - // Just directly return audio sources ( for now no transcoding for audio ) |
| 188 | + // Just directly return audio sources ( No transcoding for audio ) |
184 | 189 | if( $file->getHandler()->isAudio( $file ) ){ |
185 | 190 | return $sources; |
186 | 191 | } |
187 | 192 | // For video include bitrate and framerate: |
188 | 193 | $bitrate = $file->getHandler()->getBitrate( $file ); |
189 | | - if( $bitrate ) $source['data-bandwidth'] = $bitrate; |
| 194 | + if( $bitrate ) $source["{$dataPrefix}bandwidth"] = round ( $bitrate ); |
190 | 195 | |
191 | 196 | $framerate = $file->getHandler()->getFramerate( $file ); |
192 | | - if( $framerate ) $source['data-framerate'] = $framerate; |
| 197 | + if( $framerate ) $source["{$dataPrefix}framerate"] = $framerate; |
193 | 198 | |
194 | 199 | // Add the source to the sources array |
195 | 200 | $sources[] = $source; |
— | — | @@ -223,29 +228,28 @@ |
224 | 229 | $addWebMFlag = true; |
225 | 230 | } |
226 | 231 | // Try and add the source |
227 | | - self::tryAddSource( $file, $sources,$transcodeKey ); |
| 232 | + self::tryAddSource( $file, $sources, $transcodeKey, $dataPrefix ); |
228 | 233 | } |
229 | 234 | // Make sure we got at least one ogg and webm encode |
230 | 235 | if( !$addOggFlag || !$addWebMFlag){ |
231 | 236 | foreach( $wgEnabledTranscodeSet as $transcodeKey ){ |
232 | 237 | if( !$addOggFlag && self::$derivativeSettings[$transcodeKey]['codec'] == 'theora' ){ |
233 | | - self::tryAddSource( $file, $sources,$transcodeKey ); |
| 238 | + self::tryAddSource( $file, $sources, $transcodeKey, $dataPrefix ); |
234 | 239 | $addOggFlag = true; |
235 | 240 | } |
236 | 241 | if( !$addWebMFlag && self::$derivativeSettings[$transcodeKey]['codec'] == 'vp8' ){ |
237 | | - self::tryAddSource( $file, $sources, $transcodeKey ); |
| 242 | + self::tryAddSource( $file, $sources, $transcodeKey, $dataPrefix ); |
238 | 243 | $addWebMFlag = true; |
239 | 244 | } |
240 | 245 | } |
241 | 246 | } |
242 | 247 | return $sources; |
243 | 248 | } |
244 | | - |
245 | 249 | /** |
246 | 250 | * Try to add a source to the sources param |
247 | | - * if the source is not found update the job queue |
| 251 | + * if the source is not found update the job queue |
248 | 252 | */ |
249 | | - public static function tryAddSource( &$file, &$sources, $transcodeKey){ |
| 253 | + public static function tryAddSource( &$file, &$sources, $transcodeKey, $dataPrefix=''){ |
250 | 254 | global $wgLang; |
251 | 255 | $derivativeFile = self::getDerivativeFilePath( $file, $transcodeKey); |
252 | 256 | |
— | — | @@ -256,7 +260,7 @@ |
257 | 261 | // if the source size is < $transcodeKey assume source size: |
258 | 262 | if( is_file( $derivativeFile ) ){ |
259 | 263 | // Estimate bandwidth: |
260 | | - $bandwidth = intval( filesize( $derivativeFile ) / $file->getLength() ) * 8; |
| 264 | + $bandwidth = round( intval( filesize( $derivativeFile ) / $file->getLength() ) * 8 ); |
261 | 265 | |
262 | 266 | list( $width, $height ) = WebVideoTranscode::getMaxSizeTransform( |
263 | 267 | $file, |
— | — | @@ -269,14 +273,14 @@ |
270 | 274 | $sources[] = array( |
271 | 275 | 'src' => $thumbUrlDir . '/' .$file->getName() . '.' . $transcodeKey, |
272 | 276 | 'title' => wfMsg('timedmedia-derivative-desc-' . $transcodeKey ), |
273 | | - 'data-shorttitle' => wfMsg('timedmedia-derivative-' . $transcodeKey), |
| 277 | + "{$dataPrefix}shorttitle" => wfMsg('timedmedia-derivative-' . $transcodeKey), |
274 | 278 | |
275 | 279 | // Add data attributes per emerging DASH / webTV adaptive streaming attributes |
276 | 280 | // eventually we will define a manifest xml entry point. |
277 | | - 'data-width' => $width, |
278 | | - 'data-height' => $height, |
279 | | - 'data-bandwidth' => $bandwidth, |
280 | | - 'data-framerate' => $framerate, |
| 281 | + "{$dataPrefix}width" => $width, |
| 282 | + "{$dataPrefix}height" => $height, |
| 283 | + "{$dataPrefix}bandwidth" => $bandwidth, |
| 284 | + "{$dataPrefix}framerate" => $framerate, |
281 | 285 | ); |
282 | 286 | } else { |
283 | 287 | self::updateJobQueue($file, $transcodeKey); |
Index: trunk/extensions/TimedMediaHandler/ApiQueryVideoInfo.php |
— | — | @@ -0,0 +1,226 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * Extends imageinfo with support for videoinfo sources property. |
| 5 | + * |
| 6 | + * Alternativly core ApiQueryImageInfo could support being extended in obvious ways. |
| 7 | + */ |
| 8 | +class ApiQueryVideoInfo extends ApiQueryImageInfo { |
| 9 | + |
| 10 | + public function __construct( $query, $moduleName, $prefix = 'vi' ) { |
| 11 | + // We allow a subclass to override the prefix, to create a related API module. |
| 12 | + // Some other parts of MediaWiki construct this with a null $prefix, which used to be ignored when this only took two arguments |
| 13 | + if ( is_null( $prefix ) ) { |
| 14 | + $prefix = 'vi'; |
| 15 | + } |
| 16 | + ApiQueryBase::__construct( $query, $moduleName, $prefix ); |
| 17 | + } |
| 18 | + |
| 19 | + public function getDescription() { |
| 20 | + return 'Extends imageinfo to include video source information'; |
| 21 | + } |
| 22 | + |
| 23 | + static function getInfo( $file, $prop, $result, $thumbParams = null ) { |
| 24 | + $vals = parent::getInfo( $file, $prop, $result, $thumbParams = null ); |
| 25 | + if( isset( $prop['derivatives'] ) ){ |
| 26 | + $vals['derivatives'] = WebVideoTranscode::getSources( $file, array( 'nodata', 'fullurl') ); |
| 27 | + } |
| 28 | + return $vals; |
| 29 | + } |
| 30 | + |
| 31 | + public static function getPropertyNames() { |
| 32 | + $prop = parent::getPropertyNames(); |
| 33 | + $prop[] = 'derivatives'; |
| 34 | + return $prop; |
| 35 | + } |
| 36 | + public static function getPropertyDescriptions() { |
| 37 | + $s = parent::getPropertyDescriptions(); |
| 38 | + $s[] = ' derivatives -Adds an array of video source derivatives'; |
| 39 | + return $s; |
| 40 | + } |
| 41 | + |
| 42 | + protected function getExamples() { |
| 43 | + return array( |
| 44 | + 'api.php?action=query&titles=File:Folgers.ogv&prop=videoinfo', |
| 45 | + ); |
| 46 | + } |
| 47 | + |
| 48 | + |
| 49 | + /** |
| 50 | + * execute and getAllowedprops have to be verbatim copied because of static self:: references |
| 51 | + * |
| 52 | + * With late static binding this would be avoidable: |
| 53 | + * http://php.net/manual/en/language.oop5.late-static-bindings.php |
| 54 | + */ |
| 55 | + public function execute() { |
| 56 | + $params = $this->extractRequestParams(); |
| 57 | + |
| 58 | + $prop = array_flip( $params['prop'] ); |
| 59 | + |
| 60 | + $scale = $this->getScale( $params ); |
| 61 | + |
| 62 | + $pageIds = $this->getPageSet()->getAllTitlesByNamespace(); |
| 63 | + if ( !empty( $pageIds[NS_FILE] ) ) { |
| 64 | + $titles = array_keys( $pageIds[NS_FILE] ); |
| 65 | + asort( $titles ); // Ensure the order is always the same |
| 66 | + |
| 67 | + $skip = false; |
| 68 | + if ( !is_null( $params['continue'] ) ) { |
| 69 | + $skip = true; |
| 70 | + $cont = explode( '|', $params['continue'] ); |
| 71 | + if ( count( $cont ) != 2 ) { |
| 72 | + $this->dieUsage( 'Invalid continue param. You should pass the original ' . |
| 73 | + 'value returned by the previous query', '_badcontinue' ); |
| 74 | + } |
| 75 | + $fromTitle = strval( $cont[0] ); |
| 76 | + $fromTimestamp = $cont[1]; |
| 77 | + // Filter out any titles before $fromTitle |
| 78 | + foreach ( $titles as $key => $title ) { |
| 79 | + if ( $title < $fromTitle ) { |
| 80 | + unset( $titles[$key] ); |
| 81 | + } else { |
| 82 | + break; |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + $result = $this->getResult(); |
| 88 | + $images = RepoGroup::singleton()->findFiles( $titles ); |
| 89 | + foreach ( $images as $img ) { |
| 90 | + // Skip redirects |
| 91 | + if ( $img->getOriginalTitle()->isRedirect() ) { |
| 92 | + continue; |
| 93 | + } |
| 94 | + |
| 95 | + $start = $skip ? $fromTimestamp : $params['start']; |
| 96 | + $pageId = $pageIds[NS_IMAGE][ $img->getOriginalTitle()->getDBkey() ]; |
| 97 | + |
| 98 | + $fit = $result->addValue( |
| 99 | + array( 'query', 'pages', intval( $pageId ) ), |
| 100 | + 'imagerepository', $img->getRepoName() |
| 101 | + ); |
| 102 | + if ( !$fit ) { |
| 103 | + if ( count( $pageIds[NS_IMAGE] ) == 1 ) { |
| 104 | + // The user is screwed. imageinfo can't be solely |
| 105 | + // responsible for exceeding the limit in this case, |
| 106 | + // so set a query-continue that just returns the same |
| 107 | + // thing again. When the violating queries have been |
| 108 | + // out-continued, the result will get through |
| 109 | + $this->setContinueEnumParameter( 'start', |
| 110 | + wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); |
| 111 | + } else { |
| 112 | + $this->setContinueEnumParameter( 'continue', |
| 113 | + $this->getContinueStr( $img ) ); |
| 114 | + } |
| 115 | + break; |
| 116 | + } |
| 117 | + |
| 118 | + // Check if we can make the requested thumbnail, and get transform parameters. |
| 119 | + $finalThumbParams = $this->mergeThumbParams( $img, $scale, $params['urlparam'] ); |
| 120 | + |
| 121 | + // Get information about the current version first |
| 122 | + // Check that the current version is within the start-end boundaries |
| 123 | + $gotOne = false; |
| 124 | + if ( |
| 125 | + ( is_null( $start ) || $img->getTimestamp() <= $start ) && |
| 126 | + ( is_null( $params['end'] ) || $img->getTimestamp() >= $params['end'] ) |
| 127 | + ) { |
| 128 | + $gotOne = true; |
| 129 | + |
| 130 | + $fit = $this->addPageSubItem( $pageId, |
| 131 | + self::getInfo( $img, $prop, $result, $finalThumbParams ) ); |
| 132 | + if ( !$fit ) { |
| 133 | + if ( count( $pageIds[NS_IMAGE] ) == 1 ) { |
| 134 | + // See the 'the user is screwed' comment above |
| 135 | + $this->setContinueEnumParameter( 'start', |
| 136 | + wfTimestamp( TS_ISO_8601, $img->getTimestamp() ) ); |
| 137 | + } else { |
| 138 | + $this->setContinueEnumParameter( 'continue', |
| 139 | + $this->getContinueStr( $img ) ); |
| 140 | + } |
| 141 | + break; |
| 142 | + } |
| 143 | + } |
| 144 | + |
| 145 | + // Now get the old revisions |
| 146 | + // Get one more to facilitate query-continue functionality |
| 147 | + $count = ( $gotOne ? 1 : 0 ); |
| 148 | + $oldies = $img->getHistory( $params['limit'] - $count + 1, $start, $params['end'] ); |
| 149 | + foreach ( $oldies as $oldie ) { |
| 150 | + if ( ++$count > $params['limit'] ) { |
| 151 | + // We've reached the extra one which shows that there are additional pages to be had. Stop here... |
| 152 | + // Only set a query-continue if there was only one title |
| 153 | + if ( count( $pageIds[NS_FILE] ) == 1 ) { |
| 154 | + $this->setContinueEnumParameter( 'start', |
| 155 | + wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); |
| 156 | + } |
| 157 | + break; |
| 158 | + } |
| 159 | + $fit = $this->addPageSubItem( $pageId, |
| 160 | + self::getInfo( $oldie, $prop, $result, $finalThumbParams ) ); |
| 161 | + if ( !$fit ) { |
| 162 | + if ( count( $pageIds[NS_IMAGE] ) == 1 ) { |
| 163 | + $this->setContinueEnumParameter( 'start', |
| 164 | + wfTimestamp( TS_ISO_8601, $oldie->getTimestamp() ) ); |
| 165 | + } else { |
| 166 | + $this->setContinueEnumParameter( 'continue', |
| 167 | + $this->getContinueStr( $oldie ) ); |
| 168 | + } |
| 169 | + break; |
| 170 | + } |
| 171 | + } |
| 172 | + if ( !$fit ) { |
| 173 | + break; |
| 174 | + } |
| 175 | + $skip = false; |
| 176 | + } |
| 177 | + |
| 178 | + $data = $this->getResultData(); |
| 179 | + foreach ( $data['query']['pages'] as $pageid => $arr ) { |
| 180 | + if ( !isset( $arr['imagerepository'] ) ) { |
| 181 | + $result->addValue( |
| 182 | + array( 'query', 'pages', $pageid ), |
| 183 | + 'imagerepository', '' |
| 184 | + ); |
| 185 | + } |
| 186 | + // The above can't fail because it doesn't increase the result size |
| 187 | + } |
| 188 | + } |
| 189 | + } |
| 190 | + |
| 191 | + public function getAllowedParams() { |
| 192 | + return array( |
| 193 | + 'prop' => array( |
| 194 | + ApiBase::PARAM_ISMULTI => true, |
| 195 | + ApiBase::PARAM_DFLT => 'timestamp|user', |
| 196 | + ApiBase::PARAM_TYPE => self::getPropertyNames() |
| 197 | + ), |
| 198 | + 'limit' => array( |
| 199 | + ApiBase::PARAM_TYPE => 'limit', |
| 200 | + ApiBase::PARAM_DFLT => 1, |
| 201 | + ApiBase::PARAM_MIN => 1, |
| 202 | + ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1, |
| 203 | + ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2 |
| 204 | + ), |
| 205 | + 'start' => array( |
| 206 | + ApiBase::PARAM_TYPE => 'timestamp' |
| 207 | + ), |
| 208 | + 'end' => array( |
| 209 | + ApiBase::PARAM_TYPE => 'timestamp' |
| 210 | + ), |
| 211 | + 'urlwidth' => array( |
| 212 | + ApiBase::PARAM_TYPE => 'integer', |
| 213 | + ApiBase::PARAM_DFLT => -1 |
| 214 | + ), |
| 215 | + 'urlheight' => array( |
| 216 | + ApiBase::PARAM_TYPE => 'integer', |
| 217 | + ApiBase::PARAM_DFLT => -1 |
| 218 | + ), |
| 219 | + 'urlparam' => array( |
| 220 | + ApiBase::PARAM_DFLT => '', |
| 221 | + ApiBase::PARAM_TYPE => 'string', |
| 222 | + ), |
| 223 | + 'continue' => null, |
| 224 | + ); |
| 225 | + } |
| 226 | + |
| 227 | +} |
\ No newline at end of file |
Index: trunk/extensions/TimedMediaHandler/TimedMediaHandler.hooks.php |
— | — | @@ -12,7 +12,7 @@ |
13 | 13 | static function register(){ |
14 | 14 | global $wgParserOutputHooks, $wgHooks, $wgJobClasses, $wgJobExplitRequestTypes, |
15 | 15 | $wgMediaHandlers, $wgResourceModules, $wgExcludeFromThumbnailPurge, |
16 | | - $wgTimedMediaHandlerFileExtensions, $wgParserOutputHooks, $wgOut; |
| 16 | + $wgTimedMediaHandlerFileExtensions, $wgParserOutputHooks, $wgOut, $wgAPIPropModules; |
17 | 17 | |
18 | 18 | // Setup media Handlers: |
19 | 19 | $wgMediaHandlers['application/ogg'] = 'OggHandler'; |
— | — | @@ -47,10 +47,10 @@ |
48 | 48 | ), |
49 | 49 | 'embedPlayerIframeStyle'=> $baseExtensionResource + array( |
50 | 50 | 'styles' => 'resources/embedPlayerIframe.css', |
51 | | - ) |
| 51 | + ) |
52 | 52 | ); |
53 | | - // We should probably move this to a parser function but not working right |
54 | | - // on special upload, when there is an "existing file" warning. |
| 53 | + // We should probably move this to a parser function but not working correctly in |
| 54 | + // dynamic contexts ( for example in special upload, when there is an "existing file" warning. ) |
55 | 55 | $wgHooks['BeforePageDisplay'][] = 'TimedMediaHandlerHooks::pageOutputHook'; |
56 | 56 | |
57 | 57 | |
— | — | @@ -60,6 +60,11 @@ |
61 | 61 | // Also add the .log file ( used in two pass encoding ) |
62 | 62 | // ( probably should move in-progress encodes out of web accessible directory ) |
63 | 63 | $wgExcludeFromThumbnailPurge+= array( 'log'); |
| 64 | + |
| 65 | + // Api hooks for derivatives and query video derivatives |
| 66 | + $wgAPIPropModules += array( |
| 67 | + 'videoinfo' => 'ApiQueryVideoInfo' |
| 68 | + ); |
64 | 69 | |
65 | 70 | /** |
66 | 71 | * Add support for the "TimedText" NameSpace |
Index: trunk/extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer/resources/mw.EmbedPlayer.js |
— | — | @@ -814,7 +814,6 @@ |
815 | 815 | */ |
816 | 816 | setupSourcePlayer: function() { |
817 | 817 | mw.log("EmbedPlayer::setupSourcePlayer: " + this.id + ' sources: ' + this.mediaElement.sources.length ); |
818 | | - |
819 | 818 | // Autoseletct the media source |
820 | 819 | this.mediaElement.autoSelectSource(); |
821 | 820 | |