From a5f917bbc55e295896b8084f6657eb8b6abaf8a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Fabian=20Silva=20Delgado?= Date: Fri, 15 Jul 2016 15:33:36 -0300 Subject: Add TimedMediaHandler extension that allows display audio and video files in wiki pages, using the same syntax as for image files --- .../resources/mw.MediaWikiPlayerSupport.js | 380 +++++++++++++++++++++ 1 file changed, 380 insertions(+) create mode 100644 extensions/TimedMediaHandler/resources/mw.MediaWikiPlayerSupport.js (limited to 'extensions/TimedMediaHandler/resources/mw.MediaWikiPlayerSupport.js') diff --git a/extensions/TimedMediaHandler/resources/mw.MediaWikiPlayerSupport.js b/extensions/TimedMediaHandler/resources/mw.MediaWikiPlayerSupport.js new file mode 100644 index 00000000..16ce367c --- /dev/null +++ b/extensions/TimedMediaHandler/resources/mw.MediaWikiPlayerSupport.js @@ -0,0 +1,380 @@ +( function ( mw, $ ) { + /** + * Merge in the default video attributes supported by embedPlayer: + */ + mw.mergeConfig( 'EmbedPlayer.Attributes', { + // A apiTitleKey for looking up subtitles, credits and related videos + 'data-mwtitle': null, + + // The apiProvider where to lookup the title key + 'data-mwprovider': null + } ); + + // Add mediaWiki player support to target embedPlayer + $( mw ).bind( 'EmbedPlayerNewPlayer', function ( event, embedPlayer ) { + mw.addMediaWikiPlayerSupport( embedPlayer ); + }); + + /** + * Closure function wraps mediaWiki embedPlayer bindings + */ + mw.addMediaWikiPlayerSupport = function ( embedPlayer ) { + var apiTitleKey, apiProvider, $creditsCache = false; + // Set some local variables: + if ( !embedPlayer['data-mwtitle'] ) { + return false; + } else { + apiTitleKey = embedPlayer['data-mwtitle']; + // legacy support ( set as attribute ) + embedPlayer.apiTitleKey = apiTitleKey; + } + // Set local apiProvider via config if not defined + apiProvider = embedPlayer['data-mwprovider']; + if ( !apiProvider ) { + apiProvider = mw.config.get( 'EmbedPlayer.ApiProvider' ); + } + + /** + * Loads mediaWiki sources for a given embedPlayer + * @param {function} callback Function called once player sources have been added + */ + function loadPlayerSources( callback ) { + // Setup the request + var request = { + 'prop': 'imageinfo', + // In case the user added File: or Image: to the apiKey: + 'titles': 'File:' + decodeURIComponent( apiTitleKey ).replace( /^(File:|Image:)/, '' ), + 'iiprop': 'url|size|dimensions|metadata', + 'iiurlwidth': embedPlayer.getWidth(), + 'redirects': true // automatically resolve redirects + }; + + // Run the request: + mw.getJSON( mw.getApiProviderURL( apiProvider ), request, function ( data ) { + var i, page, imageinfo; + if ( data.query.pages ) { + for ( i in data.query.pages ) { + if ( i === '-1' ) { + callback( false ); + return ; + } + page = data.query.pages[i]; + } + } else { + callback( false ); + return ; + } + // Make sure we have imageinfo: + if ( !page.imageinfo || !page.imageinfo[0] ) { + callback( false ); + return ; + } + imageinfo = page.imageinfo[0]; + + // TODO these should call public methods rather than update internals: + + // Update the poster + embedPlayer.poster = imageinfo.thumburl; + + // Add the media src + embedPlayer.mediaElement.tryAddSource( + $( '' ) + .attr( 'src', imageinfo.url ) + .get( 0 ) + ); + + // Set the duration + if ( imageinfo.metadata[2].name === 'length' ) { + embedPlayer.duration = imageinfo.metadata[2].value; + } + + // Set the width height + // Make sure we have an accurate aspect ratio + if ( imageinfo.height !== 0 && imageinfo.width !== 0 ) { + embedPlayer.height = Math.floor( embedPlayer.width * ( imageinfo.height / imageinfo.width ) ); + } + + // Update the css for the player interface + $( embedPlayer ).css( 'height', embedPlayer.height ); + + callback(); + }); + } + + /** + * Build a clip credit from the resource wikiText page + * + * TODO parse the resource page template + * + * @param {String} resourceHTML Resource wiki text page contents + */ + function doCreditLine( resourceHTML, articleUrl ) { + var authUrl, $page, $author, $authorText, $links, $date, $authorLink, + imgSize = {}, + // Get the title string ( again a "Title" like js object could help out here. ) + titleStr = embedPlayer.apiTitleKey.replace( /_/g, ' ' ), + // Setup the initial credits line: + $creditLine = $( '
' ); + + // Add the title: + $creditLine.append( + $( '' ).html( + mw.msg( 'mwe-embedplayer-credit-title', + // get the link + $( '
' ).append( + $( '' ).attr( { + 'href': articleUrl, + 'title': titleStr + } ) + .text( titleStr ) + )[0].innerHTML + ) + ) + ); + + // Parse some data from the page info template if possible: + $page = $( resourceHTML ); + + // Look for author: + $author = $page.find( '#fileinfotpl_aut' ); + if ( $author.length ) { + // Get the real author sibling of fileinfotpl_aut + $authorText = $author.next(); + // Remove white space: + $authorText.find( 'br' ).remove(); + + // Update link to be absolute per page url context: + $links = $authorText.find( 'a' ); + if ( $links.length ) { + $links.each( function ( i, authorLink ) { + $authorLink = $( authorLink ); + authUrl = $authorLink.attr( 'href' ); + authUrl = mw.absoluteUrl( authUrl, articleUrl ); + $authorLink.attr( 'href', authUrl ); + }); + } + $creditLine.append( $( '
' ), + mw.msg( 'mwe-embedplayer-credit-author', $authorText.html() ) + ); + } + + // Look for date: + $date = $page.find( '#fileinfotpl_date' ); + if ( $date.length ) { + // Get the real date sibling of fileinfotpl_date + $date = $date.next(); + + // remove white space: + $date.find( 'br' ).remove(); + $creditLine.append( $( '
' ), + mw.msg( 'mwe-embedplayer-credit-date', $date.html() ) + ); + } + + // Build out the image and credit line + if ( embedPlayer.isAudio() ) { + imgSize.height = imgSize.width = ( embedPlayer.controlBuilder.getOverlayWidth() < 250 ) ? 45 : 80; + } else { + imgSize.width = ( embedPlayer.controlBuilder.getOverlayWidth() < 250 ) ? 45 : 120; + imgSize.height = Math.floor( imgSize.width * ( embedPlayer.getHeight() / embedPlayer.getWidth() ) ); + } + return $( '
' ).addClass( 'creditline' ) + .append( + $( '' ).attr( { + 'href': articleUrl, + 'title': titleStr + } ).html( + $( '' ).attr( { + 'border': 0, + 'src': embedPlayer.poster + } ).css( imgSize ) + ) + ) + .append( + $creditLine + ); + } + + /** + * Issues a request to populate the credits box + */ + function showCredits( $target, callback ) { + var apiUrl, fileTitle, request; + if ( $creditsCache ) { + $target.html( $creditsCache ); + callback( true ); + return; + } + // Setup shortcuts: + apiUrl = mw.getApiProviderURL( apiProvider ); + fileTitle = 'File:' + decodeURIComponent( apiTitleKey ).replace( /^File:|^Image:/, ''); + + // Get the image page ( cache for 1 hour ) + request = { + 'action': 'parse', + 'page': fileTitle, + 'smaxage': 3600, + 'maxage': 3600 + }; + mw.getJSON( apiUrl, request, function ( data ) { + var descUrl = apiUrl.replace( 'api.php', 'index.php'); + descUrl += '?title=' + encodeURIComponent( fileTitle ); + if ( data && data.parse && data.parse.text && data.parse.text['*'] ) { + // TODO improve provider 'concept' to support page title link + $creditsCache = doCreditLine( data.parse.text['*'], descUrl ); + } else { + $creditsCache = doCreditLine( false, descUrl ); + } + $target.html( $creditsCache ); + callback( true ); + } ); + } + /** + * Adds embedPlayer Bindings + */ + + // Show credits when requested + $( embedPlayer ).bindQueueCallback( 'showCredits', function ( $target, callback ) { + if ( $target.data( 'playerId') !== embedPlayer.id ) { + // bad event trigger + return ; + } + // Only request the credits once: + showCredits( $target, callback ); + }); + + // Show credits on clip complete: + $( embedPlayer ).bind( 'onEndedDone', function ( event, id ) { + if ( embedPlayer.id !== id ) { + // possible event trigger error. ( skip ) + return ; + } + // dont show credits for audio elements, + // seek to begining instead + if ( embedPlayer.isAudio() ) { + embedPlayer.setCurrentTime( 0 ); + return ; + } + var cb = embedPlayer.controlBuilder; + cb.checkMenuOverlay(); + cb.showMenuOverlay(); + cb.showMenuItem( 'credits' ); + }); + + $( embedPlayer ).bind( 'showInlineDownloadLink', function () { + // Add recommend HTML5 player if we have non-native playback: + if ( embedPlayer.controlBuilder.checkNativeWarning( ) ) { + embedPlayer.controlBuilder.addWarningBinding( + 'EmbedPlayer.ShowNativeWarning', + mw.msg( 'mwe-embedplayer-for_best_experience', + $( '
' ).append( + $( '' ).attr({ + 'href': 'http://www.mediawiki.org/wiki/Extension:TimedMediaHandler/Client_download', + 'target': '_new' + }) + )[0].innerHTML + ), + true + ); + } + }); + + $( embedPlayer ).bind( 'TimedText_BuildCCMenu', function ( event, $menu, id ) { + var _this, + pageTitle, + addTextPage, + $li; + if ( id !== embedPlayer.id ) { + _this = $( '#' + id )[0].timedText; + embedPlayer = _this.embedPlayer; + } + // Put in the "Make Transcript" link if config enabled and we have an api key + if ( embedPlayer.apiTitleKey ) { + // check if not already there: + if ( $menu.find( '.add-timed-text' ).length ) { + // add text link already present + return ; + } + + pageTitle = 'TimedText:' + + decodeURIComponent( embedPlayer.apiTitleKey ).replace( /^File:|^Image:/, '' ); + addTextPage = mw.getApiProviderURL( apiProvider ) + .replace( 'api.php', 'index.php') + + '?title=' + encodeURIComponent( pageTitle ); + + $li = $.getLineItem( mw.msg( 'mwe-timedtext-upload-timed-text'), 'script', function () { + window.location = addTextPage; + }); + + $li.addClass( 'add-timed-text') + .find( 'a' ) + .attr( { + 'href': addTextPage, + 'target': '_new' + } ); + $menu.append( + $li + ); + } + }); + + $( embedPlayer ).bindQueueCallback( 'checkPlayerSourcesEvent', function ( callback ) { + // Only load source if none are available: + if ( embedPlayer.mediaElement.sources.length === 0 ) { + loadPlayerSources( callback ); + } else { + // No source to load, issue callback directly + callback(); + } + } ); + $( mw ).bindQueueCallback( 'TimedText_LoadTextSource', function ( source, callback ) { + if ( !source.mwtitle || !source.mwprovider ) { + callback(); + return ; + } + // Load via api + var apiUrl = mw.getApiProviderURL( source.mwprovider ), + // Get the image page ( cache for 1 hour ) + request = { + 'action': 'parse', + 'page': source.mwtitle, + 'smaxage': 3600, + 'maxage': 3600 + }; + mw.getJSON( apiUrl, request, function ( data ) { + if ( data && data.parse && data.parse.text && data.parse.text['*'] ) { + source.loaded = true; + source.mimeType = 'text/mw-srt'; + source.captions = source.getCaptions( data.parse.text['*'] ); + callback(); + } else { + mw.log( 'Error: MediaWiki api error in getting timed text:', data ); + callback(); + } + }); + }); + + $( embedPlayer ).bind( 'getShareIframeSrc', function ( event, callback, id ) { + if ( id !== embedPlayer.id ) { + embedPlayer = $( '#' + id )[0]; + } + var iframeUrl = false; + // Do a special check for wikimediacommons provider as a known shared reop + if ( embedPlayer['data-mwprovider'] === 'wikimediacommons' ) { + iframeUrl = '//commons.wikimedia.org/wiki/File:' + decodeURIComponent( embedPlayer.apiTitleKey ).replace( /^(File:|Image:)/, '' ); + } else { + // use the local wiki: + if ( mw.config.get( 'wgServer' ) && mw.config.get( 'wgArticlePath' ) ) { + iframeUrl = mw.config.get( 'wgServer' ) + + mw.config.get( 'wgArticlePath' ).replace( /\$1/, 'File:' + + decodeURIComponent( embedPlayer.apiTitleKey ).replace( /^(File:|Image:)/, '' ) ); + } + } + if ( iframeUrl ) { + iframeUrl += '?embedplayer=yes'; + } + callback( iframeUrl ); + }); + }; + +} )( mediaWiki, jQuery ); -- cgit v1.2.3