diff options
Diffstat (limited to 'extensions/TimedMediaHandler/TimedMediaHandler.hooks.php')
-rw-r--r-- | extensions/TimedMediaHandler/TimedMediaHandler.hooks.php | 485 |
1 files changed, 485 insertions, 0 deletions
diff --git a/extensions/TimedMediaHandler/TimedMediaHandler.hooks.php b/extensions/TimedMediaHandler/TimedMediaHandler.hooks.php new file mode 100644 index 00000000..70768479 --- /dev/null +++ b/extensions/TimedMediaHandler/TimedMediaHandler.hooks.php @@ -0,0 +1,485 @@ +<?php + +/** + * Hooks for TimedMediaHandler extension + * + * @file + * @ingroup Extensions + */ + +class TimedMediaHandlerHooks { + + // Register TimedMediaHandler namespace IDs + // These are configurable due to Commons history: T123823 + // These need to be before registerhooks due to: T123695 + public static function onSetupAfterCache() { + global $wgEnableLocalTimedText, $wgExtraNamespaces, $wgTimedTextNS; + if ( $wgEnableLocalTimedText ) { + define( "NS_TIMEDTEXT", $wgTimedTextNS ); + define( "NS_TIMEDTEXT_TALK", $wgTimedTextNS +1 ); + + $wgExtraNamespaces[NS_TIMEDTEXT] = "TimedText"; + $wgExtraNamespaces[NS_TIMEDTEXT_TALK] = "TimedText_talk"; + } else { + $wgTimedTextNS = false; + } + return true; + } + + // Register TimedMediaHandler Hooks + public static function register() { + global $wgHooks, $wgJobClasses, $wgJobTypesExcludedFromDefaultQueue, $wgMediaHandlers, + $wgResourceModules, $wgExcludeFromThumbnailPurge, $wgParserOutputHooks, + $wgFileExtensions, $wgTmhEnableMp4Uploads, $wgExtensionAssetsPath, + $wgMwEmbedModuleConfig, $timedMediaDir, $wgEnableLocalTimedText, + $wgTmhFileExtensions, $wgTmhTheoraTwoPassEncoding, $wgTmhWebPlayer; + + // Remove mp4 if not enabled: + if( $wgTmhEnableMp4Uploads === false ){ + $index = array_search( 'mp4', $wgFileExtensions ); + if ( $index !== false ) { + array_splice( $wgFileExtensions, $index, 1 ); + } + } + + if( !class_exists( 'MwEmbedResourceManager' ) ) { + echo "TimedMediaHandler requires the MwEmbedSupport extension.\n"; + exit( 1 ); + } + + // Register the Timed Media Handler javascript resources ( MwEmbed modules ) + MwEmbedResourceManager::register( 'extensions/TimedMediaHandler/MwEmbedModules/EmbedPlayer' ); + MwEmbedResourceManager::register( 'extensions/TimedMediaHandler/MwEmbedModules/TimedText' ); + + // Set the default webPath for this embed player extension + $wgMwEmbedModuleConfig['EmbedPlayer.WebPath'] = $wgExtensionAssetsPath . + '/' . basename ( $timedMediaDir ) . '/MwEmbedModules/EmbedPlayer'; + + // Setup media Handlers: + $wgMediaHandlers['application/ogg'] = 'OggHandlerTMH'; + $wgMediaHandlers['audio/webm'] = 'WebMHandler'; + $wgMediaHandlers['video/webm'] = 'WebMHandler'; + $wgMediaHandlers['video/mp4'] = 'Mp4Handler'; + $wgMediaHandlers['audio/x-flac'] = 'FLACHandler'; + $wgMediaHandlers['audio/flac'] = 'FLACHandler'; + $wgMediaHandlers['audio/wav'] = 'WAVHandler'; + + // Add transcode job class: + $wgJobClasses['webVideoTranscode'] = 'WebVideoTranscodeJob'; + + // Transcode jobs must be explicitly requested from the job queue: + $wgJobTypesExcludedFromDefaultQueue[] = 'webVideoTranscode'; + + $baseExtensionResource = array( + 'localBasePath' => __DIR__, + 'remoteExtPath' => 'TimedMediaHandler', + ); + + // Add the PopUpMediaTransform module ( specific to timedMedia handler ( no support in mwEmbed modules ) + $wgResourceModules+= array( + 'mw.PopUpMediaTransform' => $baseExtensionResource + array( + 'scripts' => 'resources/mw.PopUpThumbVideo.js', + 'dependencies' => array( 'mw.MwEmbedSupport', 'mediawiki.Title' ), + ), + 'mw.PopUpMediaTransform.styles' => $baseExtensionResource + array( + 'position' => 'top', + 'styles' => 'resources/PopUpThumbVideo.css', + ), + 'mw.TMHGalleryHook.js' => $baseExtensionResource + array( + 'scripts' => 'resources/mw.TMHGalleryHook.js', + // position top needed as it needs to load before mediawiki.page.gallery + 'position' => 'top', + ), + 'embedPlayerIframeStyle'=> $baseExtensionResource + array( + 'styles' => 'resources/embedPlayerIframe.css', + ), + 'ext.tmh.transcodetable' => $baseExtensionResource + array( + 'scripts' => 'resources/ext.tmh.transcodetable.js', + 'styles' => 'resources/transcodeTable.css', + 'dependencies' => array( + 'mediawiki.api.edit', + 'mw.MwEmbedSupport', + ), + 'messages'=> array( + 'mwe-ok', + 'mwe-cancel', + 'timedmedia-reset-error', + 'timedmedia-reset', + 'timedmedia-reset-confirm' + ) + ), + 'ext.tmh.TimedTextSelector' => $baseExtensionResource + array( + 'scripts' => 'resources/ext.tmh.TimedTextSelector.js', + ), + "mw.MediaWikiPlayerSupport" => $baseExtensionResource + array( + 'scripts' => 'resources/mw.MediaWikiPlayerSupport.js', + 'dependencies'=> 'mw.Api', + ), + // adds support MediaWikiPlayerSupport player bindings + "mw.MediaWikiPlayer.loader" => $baseExtensionResource + array( + 'loaderScripts' => 'resources/mw.MediaWikiPlayer.loader.js', + ), + ); + + // Add OgvJs-related modules for Safari/IE/Edge Ogg playback + $wgResourceModules += array( + 'ext.tmh.OgvJsSupport' => $baseExtensionResource + array( + 'scripts' => array( + 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv-support.js', + 'resources/ext.tmh.OgvJsSupport.js', + ), + 'targets' => array( 'mobile', 'desktop' ), + ), + 'ext.tmh.OgvJs' => $baseExtensionResource + array( + 'scripts' => array( + 'MwEmbedModules/EmbedPlayer/binPlayers/ogv.js/ogv.js', + ), + 'dependencies' => 'ext.tmh.OgvJsSupport', + 'targets' => array( 'mobile', 'desktop' ), + ), + ); + + // Setup a hook for iframe embed handling: + $wgHooks['ArticleFromTitle'][] = 'TimedMediaIframeOutput::iframeHook'; + + // When an upload completes ( check clear any existing transcodes ) + $wgHooks['UploadComplete'][] = 'TimedMediaHandlerHooks::checkUploadComplete'; + + // When an image page is moved: + $wgHooks['TitleMove'][] = 'TimedMediaHandlerHooks::checkTitleMove'; + + // When image page is deleted so that we remove transcode settings / files. + $wgHooks['FileDeleteComplete'][] = 'TimedMediaHandlerHooks::onFileDeleteComplete'; + + // Add parser hook + $wgParserOutputHooks['TimedMediaHandler'] = array( 'TimedMediaHandler', 'outputHook' ); + + // Use a BeforePageDisplay hook to load the styles in pages that pull in media dynamically. + // (Special:Upload, for example, when there is an "existing file" warning.) + $wgHooks['BeforePageDisplay'][] = 'TimedMediaHandlerHooks::pageOutputHook'; + + // Make sure modules are loaded on image pages that don't have a media file in the wikitext. + $wgHooks['ImageOpenShowImageInlineBefore'][] = 'TimedMediaHandlerHooks::onImageOpenShowImageInlineBefore'; + + // Exclude transcoded assets from normal thumbnail purging + // ( a maintenance script could handle transcode asset purging) + if ( isset( $wgExcludeFromThumbnailPurge ) ) { + $wgExcludeFromThumbnailPurge = array_merge( $wgExcludeFromThumbnailPurge, $wgTmhFileExtensions ); + // Also add the .log file ( used in two pass encoding ) + // ( probably should move in-progress encodes out of web accessible directory ) + $wgExcludeFromThumbnailPurge[] = 'log'; + } + + $wgHooks['LoadExtensionSchemaUpdates'][] = 'TimedMediaHandlerHooks::loadExtensionSchemaUpdates'; + + // Add unit tests + $wgHooks['UnitTestsList'][] = 'TimedMediaHandlerHooks::registerUnitTests'; + + /** + * Add support for the "TimedText" NameSpace + */ + if ( $wgEnableLocalTimedText ) { + // Check for timed text page: + $wgHooks[ 'ArticleFromTitle' ][] = 'TimedMediaHandlerHooks::checkForTimedTextPage'; + $wgHooks[ 'ArticleContentOnDiff' ][] = 'TimedMediaHandlerHooks::checkForTimedTextDiff'; + } else { + // overwrite TimedText.ShowInterface for video with mw-provider=local + $wgMwEmbedModuleConfig['TimedText.ShowInterface.local'] = 'off'; + } + + // Add transcode status to video asset pages: + $wgHooks[ 'ImagePageAfterImageLinks' ][] = 'TimedMediaHandlerHooks::checkForTranscodeStatus'; + $wgHooks[ 'NewRevisionFromEditComplete' ][] = 'TimedMediaHandlerHooks::onNewRevisionFromEditComplete'; + $wgHooks[ 'ArticlePurge' ][] = 'TimedMediaHandlerHooks::onArticlePurge'; + + $wgHooks['LoadExtensionSchemaUpdates'][] = 'TimedMediaHandlerHooks::checkSchemaUpdates'; + $wgHooks['wgQueryPages'][] = 'TimedMediaHandlerHooks::onwgQueryPages'; + return true; + } + + /** + * @param $imagePage ImagePage + * @param $wgOut OutputPage + * @return bool + */ + public static function onImageOpenShowImageInlineBefore( $imagePage, $out ) { + $handler = $imagePage->getDisplayedFile()->getHandler(); + if ( $handler !== false && $handler instanceof TimedMediaHandler ) { + TimedMediaHandler::outputHook( $out, null, null ); + } + return true; + } + + /** + * @param $title Title + * @param $article Article + * @return bool + */ + public static function checkForTimedTextPage( &$title, &$article ){ + global $wgTimedTextNS; + if ( $title->getNamespace() === $wgTimedTextNS ) { + $article = new TimedTextPage( $title ); + } + return true; + } + + /** + * @param $diffEngine DifferenceEngine + * @param $output OutputPage + * @return bool + */ + public static function checkForTimedTextDiff( $diffEngine, $output ) { + global $wgTimedTextNS; + if ( $output->getTitle()->getNamespace() === $wgTimedTextNS ) { + $article = new TimedTextPage( $output->getTitle() ); + $article->renderOutput( $output ); + return false; + } + return true; + } + + /** + * Wraps the isTranscodableFile function + * @param $title Title + * @return bool + */ + public static function isTranscodableTitle( $title ){ + if( $title->getNamespace() != NS_FILE ){ + return false; + } + $file = wfFindFile( $title ); + return self::isTranscodableFile( $file ); + } + + /** + * Utility function to check if a given file can be "transcoded" + * @param $file File object + * @return bool + */ + public static function isTranscodableFile( & $file ){ + global $wgEnableTranscode, $wgEnabledAudioTranscodeSet; + + // don't show the transcode table if transcode is disabled + if( !$wgEnableTranscode && !$wgEnabledAudioTranscodeSet ){ + return false; + } + // Can't find file + if( !$file ){ + return false; + } + // We can only transcode local files + if( !$file->isLocal() ){ + return false; + } + + $handler = $file->getHandler(); + // Not able to transcode files without handler + if( !$handler ) { + return false; + } + $mediaType = $handler->getMetadataType( $file ); + // If ogg or webm format and not audio we can "transcode" this file + $isAudio = $handler instanceof TimedMediaHandler && $handler->isAudio( $file ); + if( ( $mediaType == 'webm' || $mediaType == 'ogg' || $mediaType =='mp4' ) + && !$isAudio + ){ + return true; + } + if( $isAudio && count( $wgEnabledAudioTranscodeSet ) ) { + return true; + } + return false; + } + + /** + * @param $article Article + * @param $html string + * @return bool + */ + public static function checkForTranscodeStatus( $article, &$html ){ + // load the file: + $file = wfFindFile( $article->getTitle() ); + if( self::isTranscodableFile( $file ) ){ + $html .= TranscodeStatusTable::getHTML( $file ); + } + return true; + } + + /** + * @param $image UploadBase + * @return bool + */ + public static function checkUploadComplete( $upload ){ + $file = $upload->getLocalFile(); + // Check that the file is a transcodable asset: + if( $file && self::isTranscodableFile( $file ) ){ + // Remove all the transcode files and db states for this asset + WebVideoTranscode::removeTranscodes( $file ); + WebVideoTranscode::startJobQueue( $file ); + } + return true; + } + /** + * Handle moved titles + * + * For now we just remove all the derivatives for the oldTitle. In the future we could + * look at moving the files, but right now thumbs are not moved, so I don't want to be + * inconsistent. + * @param $title Title + * @param $newTitle Title + * @param $user User + * @return bool + */ + public static function checkTitleMove( $title, $newTitle, $user ){ + if( self::isTranscodableTitle( $title ) ){ + // Remove all the transcode files and db states for this asset + // ( will be re-added the first time the asset is displayed with its new title ) + $file = wfFindFile( $title ); + WebVideoTranscode::removeTranscodes( $file ); + } + return true; + } + + /** + * Hook to FileDeleteComplete + * remove transcodes on delete + * @param $file File + * @param $oldimage + * @param $article Article + * @param $user User + * @param $reason string + * @return bool + */ + public static function onFileDeleteComplete( $file, $oldimage, $article, $user, $reason ) { + if ( !$oldimage ) { + if( self::isTranscodableFile( $file ) ){ + WebVideoTranscode::removeTranscodes( $file ); + } + } + return true; + } + + /* + * If file gets reverted to a previous version, reset transcodes. + */ + public static function onNewRevisionFromEditComplete( $article, Revision $rev, $baseID, User $user ) { + if ( $baseID !== false ) { + // Check if the article is a file and remove transcode files: + if( $article->getTitle()->getNamespace() == NS_FILE ) { + $file = wfFindFile( $article->getTitle() ); + if( self::isTranscodableFile( $file ) ){ + WebVideoTranscode::removeTranscodes( $file ); + WebVideoTranscode::startJobQueue( $file ); + } + } + } + return true; + } + + /** + * When a user asks for a purge, perhaps through our handy "update transcode status" + * link, make sure we've got the updated set of transcodes. This'll allow a user or + * automated process to see their status and reset them. + */ + public static function onArticlePurge( $article ) { + if( $article->getTitle()->getNamespace() == NS_FILE ) { + $file = wfFindFile( $article->getTitle() ); + if( self::isTranscodableFile( $file ) ){ + WebVideoTranscode::cleanupTranscodes( $file ); + } + } + return true; + } + + /** + * Adds the transcode sql + * @param $updater DatabaseUpdater + * @return bool + */ + public static function loadExtensionSchemaUpdates( $updater ){ + $updater->addExtensionTable( 'transcode', __DIR__ . '/TimedMediaHandler.sql' ); + return true; + } + + /** + * Hook to add list of PHPUnit test cases. + * @param $files Array of files + * @return bool + */ + public static function registerUnitTests( array &$files ) { + $testDir = __DIR__ . '/tests/phpunit/'; + $testFiles = array( + 'TestTimeParsing.php', + 'TestApiUploadVideo.php', + 'TestVideoThumbnail.php', + 'TestVideoTranscode.php', + 'TestOggHandler.php', + 'TestTimedMediaTransformOutput.php', + 'TestTimedMediaHandler.php' + ); + foreach( $testFiles as $fileName ){ + $files[] = $testDir . $fileName; + } + return true; + } + + /** + * Add JavaScript and CSS for special pages that may include timed media + * but which will not fire the parser hook. + * + * FIXME: There ought to be a better interface for determining whether the + * page is liable to contain timed media. + * + * @param $out OutputPage + * @param $sk + * @return bool + */ + static function pageOutputHook( &$out, &$sk ){ + global $wgTimedTextNS; + + $title = $out->getTitle(); + $namespace = $title->getNamespace(); + $addModules = false; + + if ( $namespace === NS_CATEGORY || $namespace === $wgTimedTextNS ) { + $addModules = true; + } + + if ( $title->isSpecialPage() ) { + list( $name, /* subpage */ ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); + if ( stripos( $name, 'file' ) !== false || stripos( $name, 'image' ) !== false + || $name === 'Search' || $name === 'GlobalUsage' ) { + $addModules = true; + } + } + + if ( $addModules ) { + $out->addModuleScripts( 'mw.PopUpMediaTransform' ); + $out->addModuleStyles( 'mw.PopUpMediaTransform.styles' ); + } + + return true; + } + + public static function checkSchemaUpdates( DatabaseUpdater $updater ) { + $base = __DIR__ ; + + switch ( $updater->getDB()->getType() ) { + case 'mysql': + case 'sqlite': + $updater->addExtensionTable( 'transcode', "$base/TimedMediaHandler.sql" ); // Initial install tables + $updater->addExtensionUpdate( array( 'addIndex', 'transcode', 'transcode_name_key', + "$base/archives/transcode_name_key.sql", true ) ); + break; + case 'postgres': + //TODO + break; + } + return true; + } + + public static function onwgQueryPages( $qp ) { + $qp[] = array( 'SpecialOrphanedTimedText', 'OrphanedTimedText' ); + return true; + } +} |