MediaWiki:Gadget-sound.js

From TERMSOFSERVICE
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
mw.hook('wikipage.content').add(function ($content) {
	var i18n = {
		playTitle: 'Click to play',
		stopTitle: 'Click to stop',
	};
	var $sound = $content.find('.sound');

	$sound.prop('title', i18n.playTitle);
	
	// for MW 1.40
	$sound.each(function(){
		if($(this).find("audio").attr("disabled")){
			var $audio = $(this).find("audio");
			var mwtitle = $audio.attr("data-mwtitle");
			getInfo( mwtitle ).then( function( info ){
				var attr = {};
				if( !info ) {
					attr.src = "/w/Special:FilePath/" + mwtitle.replaceAll(" ", "_");
				} else {
					var metadata = meta2obj(info.metadata);
					attr.src = info.url;
					try {
						attr.type = info.mime.replace(/application/, "audio") + "; codecs='" + metadata.streams[0].data.type + "'";
					} catch(error){}
				}
				$audio.removeAttr('disabled')
				.attr("preload", "metadata")
				.attr("role", "application")
				.attr("src", attr.src)
				.append(
					$("<source />", attr)
				);
			});
		}
	});
	
	$sound.on('click', function (e) {
		// Ignore links
		if (e.target.tagName === 'A') {
			return;
		}

		var audio = $(this).find('audio')[0];
		if (audio) {
			audio.paused ? audio.play() : audio.pause();
		}
	});
	
	$sound.find("audio").on('play', function () {
		// Stop any already playing sounds
		var playing = $('.sound-playing audio')[0];
		playing && playing.pause();

		$(this).closest('.sound')
			.addClass('sound-playing').prop('title', i18n.stopTitle);
	}).on('pause', function () {
		// Reset back to the start
		this.currentTime = 0;

		$(this).closest('.sound')
			.removeClass('sound-playing').prop('title', i18n.playTitle);
	});

});

/**
 * Retries a request once if it fails
 *
 * Always returns an abortable promise, even if the request itself isn't abortable.
 *
 * "request" is a function which will run the request,
 * and should return the request's promise.
 * "delay" is the amount of milliseconds to wait before retrying.
 * "retries" is the amount of times to retry
 */
function retryableRequest(request, delay, retries) {
	var deferred = $.Deferred();
	var curRequest;
	var timeout;
	retries = retries || 1;
	var attemptRequest = function (attempt) {
		(curRequest = request()).then(deferred.resolve, function (code, data) {
			if (attempt <= retries) {
				timeout = setTimeout(function () {
					attemptRequest(++attempt);
				}, delay || 1000);
			} else {
				deferred.reject(code, data);
			}

		});
	};
	attemptRequest(1);

	return deferred.promise({
		abort: function () {
			if (curRequest.abort) {
				curRequest.abort();
			}
			clearTimeout(timeout);
		}
	});
}

/*****************************
 * Request audio info
 */
function getInfo(filename) {
	filename = filename.replaceAll(" ", "_");
	if (filename.indexOf("File:") != 0) {
		filename = "File:" + filename;
	}
	return retryableRequest(function () {
		return new mw.Api().get({
			action: 'query',
			titles: filename,
			prop: 'videoinfo',
			viprop: 'url|mime|metadata'
		});
	}).then(function (data) {
		if (data) {
			var pageid = Object.keys(data.query.pages)[0];
			if (pageid > -1) {
				return data.query.pages[pageid].videoinfo[0];
			}
		}
		return "";
	}).fail(function () {
		return "";
	});
}

function meta2obj(value){
	if(typeof value === 'object' && Array.isArray(value)){
		var obj = {}, idx = 0;
		value.forEach( function( v, i ){
			if(v.hasOwnProperty("name") && v.hasOwnProperty("value")){
				if(typeof v.name === 'number'){
					obj[idx++] = {
						id: v.name,
						data: meta2obj(v.value)
					};
				} else {
					obj[v.name] = meta2obj(v.value);
				}
			}
		});
		return obj;
	}
	return value;
}