Coursera Extension -- enables setting custom video speed in 10% increments by pressing [ and ] keys.
// ==UserScript== // @name Coursera EXT - Custom video speed // @description Coursera Extension -- enables setting custom video speed in 10% increments by pressing [ and ] keys. // @namespace http://sepczuk.com/ // @version 0.32 // @include https://*.coursera.org/*/lecture/view* // @match https://*.coursera.org/*/lecture/view* // @copyright 2012-2013, Damian Sepczuk, damian at sepczuk period delme com // ==/UserScript== function mainWrapper(){ var debug = false; var US_SHORT_NAME = 'CourseraEXT'; var US_VERSION = 0.32; function debugLog(msg) { if (!debug) return; console.log(US_SHORT_NAME + ": " + msg); } /*! * jQuery Cookie Plugin * https://github.com/carhartl/jquery-cookie * * Copyright 2011, Klaus Hartl * Dual licensed under the MIT or GPL Version 2 licenses. * http://www.opensource.org/licenses/mit-license.php * http://www.opensource.org/licenses/GPL-2.0 */ (function($) { $.cookie = function(key, value, options) { // key and at least value given, set cookie... if (arguments.length > 1 && (!/Object/.test(Object.prototype.toString.call(value)) || value === null || value === undefined)) { options = $.extend({}, options); if (value === null || value === undefined) { options.expires = -1; } if (typeof options.expires === 'number') { var days = options.expires, t = options.expires = new Date(); t.setDate(t.getDate() + days); } value = String(value); return (document.cookie = [ encodeURIComponent(key), '=', options.raw ? value : encodeURIComponent(value), options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE options.path ? '; path=' + options.path : '', options.domain ? '; domain=' + options.domain : '', options.secure ? '; secure' : '' ].join('')); } // key and possibly options given, get cookie... options = value || {}; var decode = options.raw ? function(s) { return s; } : decodeURIComponent; var pairs = document.cookie.split('; '); for (var i = 0, pair; pair = pairs[i] && pairs[i].split('='); i++) { if (decode(pair[0]) === key) return decode(pair[1] || ''); // IE saves cookies with empty string as "c; ", e.g. without "=" as opposed to EOMB, thus pair[1] may be undefined } return null; }; })(jQuery); function roundToNthDigit(v, n) { return parseFloat((v).toFixed(n)); } function getRoundedVideoSpeed() { return roundToNthDigit(QL_player.get_speed(), 2); } function updateInterface() { $('.course-lecture-view-speed-label').text(getRoundedVideoSpeed() + 'x'); } function main(){ debugLog("Running main!"); if (window.QL_player === undefined) { var retryAfterTImeout = 300; debugLog("QL_player window not found. Setting timeout to " + retryAfterTImeout + "ms."); setTimeout(main, retryAfterTImeout); return; } //debugLog("QL_player window not found!"); // ======================== <modify standard code> // get rid of bounds! debugLog("Registering new increase_speed function... "); QL_player.increase_speed = function(deltaSpeed){ var currentSpeed = getRoundedVideoSpeed(); var newSpeed = roundToNthDigit(currentSpeed + deltaSpeed, 2); debugLog("QL_player.increase_speed: current speed = " + currentSpeed + "; delta = " + deltaSpeed + "; new rounded speed = " + newSpeed + ". Setting..."); QL_player.set_speed(newSpeed); debugLog("QL_player.increase_speed: DEBUG check of current speed. Current EXACT speed = " + QL_player.get_speed()); } debugLog("Registering new increase_speed function... DONE."); // change the way, video speed is saved debugLog("Overriding update_speed_preference function... "); var old_update_speed_preference = update_speed_preference; update_speed_preference = function(){ debugLog("update_speed_preference: invoking old function..."); old_update_speed_preference(); debugLog("update_speed_preference: DONE."); debugLog("update_speed_preference: Saving new speed in a cookie customVideoSpeed___ByFireBiker."); $.cookie('customVideoSpeed___ByFireBiker', getRoundedVideoSpeed(), {expires: 24*7*30*365*10, secure: true}); } debugLog("Overriding update_speed_preference function... DONE."); // ======================== </modify standard code> // set video speed to previously cookied one (if available) var cookiedSpeed = $.cookie('customVideoSpeed___ByFireBiker'); debugLog("Video speed from customVideoSpeed___ByFireBiker cookie: " + cookiedSpeed); if (null !== cookiedSpeed ) { debugLog("Not null, so update speed!"); QL_player.set_speed(cookiedSpeed); updateInterface(); } // Add full-screen bubble var mediaStatusBubble = $('<div id="player_state" aria-live="assertive" role="alert" style="display: block; position: absolute; top: 10px; right: 10px; z-index: 10000; padding: 10px 20px; background-color: #eee; display: none; border-radius: 10px; -webkit-border-radius: 10px; -moz-border-radius: 10px;">Player state</div>').appendTo('#lecture_view_dialog'); var isShowingMediaBubble = false; var mediaBubbleTimeout = null; function showMediaStatusBubble(t) { debugLog("showMediaStatusBubble: showing the bubble!"); if (isShowingMediaBubble) clearTimeout(mediaBubbleTimeout); else mediaStatusBubble.fadeIn(300); isShowingMediaBubble = true; mediaStatusBubble.html(t); mediaBubbleTimeout = setTimeout(function(){mediaStatusBubble.fadeOut(200); isShowingMediaBubble = false;}, 800); } // Define speed actions var decSpeed = function(e){ debugLog("decSpeed"); var newRate = getRoundedVideoSpeed() - 0.1; if(newRate > 0) { QL_player.set_speed(newRate); } }; var incSpeed = function(e){ debugLog("incSpeed"); var newRate = getRoundedVideoSpeed() + 0.1; QL_player.set_speed(newRate); }; // Add keyboard shortcuts LBRACKET_KEY = 219; RBRACKET_KEY = 221; NUM0_KEY = 96; ALNUM_0 = 48; QL_player.mediaelement_handle.options.keyActions.push({keys: [LBRACKET_KEY], action: function(player, media){decSpeed();}}); QL_player.mediaelement_handle.options.keyActions.push({keys: [RBRACKET_KEY], action: function(player, media){incSpeed();}}); QL_player.mediaelement_handle.options.keyActions.push({keys: [NUM0_KEY, ALNUM_0], action: function(player, media){QL_player.set_speed(1);}}); // Add entries to the legend ([?]-key) $('.course-lecture-view-shortcuts-block li').eq(7).append('<li><strong>[:</strong> Decrease speed by 10%</li>\n' + '<li><strong>]:</strong> Increase speed by 10%</li>\n' + '<li><strong>Num0:</strong> Set speed to 100%</li>\n'); // Respond to speed change (from any source!) by updating user interface QL_player.mediaelement_media.addEventListener('ratechange', function(){ debugLog("Speed change detected! Update the interface."); updateInterface(); debugLog("... and store the new speed."); update_speed(); debugLog("Show bubble if full screen."); // Show bubble only on full-screen. When in window mode, there's a button showing current speed. if (QL_player.mediaelement_handle.isFullScreen) { debugLog("YES, full screen! Showing bubble."); var s = QL_player.mediaelement_media.playbackRate.toFixed(2); showMediaStatusBubble("Video speed: ×" + s); } }); }; main(); }; if (!document.xmlVersion) { var script = document.createElement('script'); script.appendChild(document.createTextNode('('+ mainWrapper +')();')); document.documentElement.appendChild(script); }