Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save zhjgithub/38bff8b658af77b7c102a69e37b2f120 to your computer and use it in GitHub Desktop.
Save zhjgithub/38bff8b658af77b7c102a69e37b2f120 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Udacity Chinese courses video playback controller
// @namespace http://tampermonkey.net/
// @version 0.1
// @description Enable forward/backward and play/pause for current mouse hovering video if more than one video tag on the page.
// @author organism <neuqzhj@gmail.com>
// @source https://gist.github.com/zhjgithub/38bff8b658af77b7c102a69e37b2f120
// @match https://classroom.udacity.com/*
// @run-at document-end
// @grant none
// ==/UserScript==
/*
Inspired By HTML5播放器增强插件 from https://greasyfork.org/users/49622
*/
(function() {
'use strict';
const playbackRateRange = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
const controller = {
player: function() {
return this.currentVideo;
},
_isFocused: false,
isFocused: function() {
return this._isFocused;
},
setPlayer: function(video) {
if (this.currentVideo != video) {
this.currentVideo = video;
this.videoPlayButton = video.parentNode.querySelector('button.video-play');
this.videoPlayButton.focus();
this.playbackRateIndex = 2;
video.parentNode.appendChild(this._tips);
}
this._isFocused = true;
},
clearPlayer: function() {
this._isFocused = false;
},
tips: function(str) {
const tips = this._tips;
const style = tips.style;
tips.innerText = str;
for (var i = 0; i < 3; i++) {
if (this.on_off[i]) clearTimeout(this.on_off[i]);
}
style.display = 'block';
this.on_off[0] = setTimeout(function() {
style.opacity = 1;
}, 50);
this.on_off[1] = setTimeout(function() {
style.opacity = 0;
}, 1000);
this.on_off[2] = setTimeout(function() {
style.display = 'none';
}, 2000);
},
on_off: new Array(3),
init: function() {
if (this.load || document.querySelectorAll('video').length === 0) {
return;
}
var _this = this;
this.load = true;
setTimeout(function() {
// console.log('检测到HTML5视频!');
_this.load = false;
_this.setFocusListener();
_this.initTips();
document.onkeydown = _this.button;
const currentFocusedVideo = document.querySelector(
'video:hover'
);
if (currentFocusedVideo) {
_this.setPlayer(currentFocusedVideo);
}
}, 1000);
},
load: false,
button: function(event) {
if (event.altKey || event.ctrlKey || event.shiftKey) return;
if (!controller.isFocused()) return;
let handled = false;
const _this = controller;
if (event.key === 'ArrowRight' || event.key === 'n') {
//方向键右→:快进3秒
_this.player().currentTime += 3;
_this.tips('快进:3秒');
handled = true;
} else if (event.key === 'ArrowLeft' || event.key === 'b') {
//方向键左←:后退3秒
_this.player().currentTime -= 3;
_this.tips('后退:3秒');
handled = true;
} else if(event.key === ','){
_this.player().currentTime -= 15;
_this.tips('后退:15秒');
handled = true;
} else if(event.key === '.'){
_this.player().currentTime += 15;
_this.tips('快进:15秒');
handled = true;
} else if (event.key === 'ArrowUp') {
//方向键上↑:音量升高 1%
if (_this.player().volume < 1) {
_this.player().volume += 0.01;
}
_this.player().volume = _this.player().volume.toFixed(2);
_this.tips(
'音量:' + parseInt(_this.player().volume * 100) + '%'
);
handled = true;
} else if (event.key === 'ArrowDown') {
//方向键下↓:音量降低 1%
if (_this.player().volume > 0) {
_this.player().volume -= 0.01;
}
_this.player().volume = _this.player().volume.toFixed(2);
_this.tips(
'音量:' + parseInt(_this.player().volume * 100) + '%'
);
handled = true;
} else if (event.key === ' ') {
let onlyShowTips = false;
if (document.activeElement === _this.videoPlayButton) {
onlyShowTips = true;
}
//空格键:暂停/播放
if (_this.player().paused) {
if (!onlyShowTips) _this.player().play();
_this.tips('播放');
} else {
if (!onlyShowTips) _this.player().pause();
_this.tips('暂停');
}
handled = true;
} else if (event.key === 'z') {
_this.changePlaybackSpeed('rest');
_this.tips('播放速度复位');
handled = true;
} else if (event.key === 'x') {
_this.changePlaybackSpeed('-');
_this.tips(`播放速度${playbackRateRange[_this.playbackRateIndex]}`);
handled = true;
} else if (event.key === 'c') {
_this.changePlaybackSpeed('+');
_this.tips(`播放速度${playbackRateRange[_this.playbackRateIndex]}`);
handled = true;
}
if (handled) {
event.stopPropagation();
event.preventDefault();
}
},
changePlaybackSpeed: function (value) {
if (value === '+') {
if (this.playbackRateIndex >= playbackRateRange.length - 1) return;
this.playbackRateIndex += 1;
} else if (value === '-') {
if (this.playbackRateIndex <= 0) return;
this.playbackRateIndex -= 1;
} else {
this.playbackRateIndex = 2;
}
this.player().parentNode.querySelector(`input[name="playback-speed"][value="${playbackRateRange[this.playbackRateIndex]}"]`)
.click();
},
onmouseover: function(event) {
controller.setPlayer(event.target);
},
onmouseout: function(event) {
controller.clearPlayer();
},
setFocusListener: function() {
const allVideos = document.querySelectorAll('video');
allVideos.forEach(element => {
element.onmouseover = this.onmouseover;
element.onmouseout = this.onmouseout;
}, this);
},
initTips: function() {
if (this._tips) {
return;
}
const tips = document.createElement('div');
this._tips = tips;
tips.setAttribute('id', 'video-playback-control-tips');
tips.setAttribute(
'style',
"pointer-events: none;position: absolute;z-index: 999999;padding: 10px;background: rgba(0,0,0,0.8);color:white;top: 50%;left: 50%;transform: translate(-50%,-50%);transition: all 500ms ease;opacity: 0;display: none; -webkit-font-smoothing: subpixel-antialiased;font-family: 'microsoft yahei', Verdana, Geneva, sans-serif;-webkit-user-select: none;"
);
tips.style.fontSize = '20px';
}
};
controller.init();
document.addEventListener('DOMNodeInserted', function() {
controller.init();
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment