mirror of
https://github.com/SoPat712/videospeed.git
synced 2025-08-21 18:08:46 -04:00
Merge branch 'master' into dev
# Conflicts: # inject.js # manifest.json
This commit is contained in:
12
README.md
12
README.md
@@ -1,10 +1,10 @@
|
||||
# The science of accelerated playback
|
||||
|
||||
**TL;DR: faster playback translates to quick progress, better engagement, and retention.**
|
||||
**TL;DR: faster playback translates to better engagement and retention.**
|
||||
|
||||
Average adult reads prose text at [250 to 300 words per minute](http://www.paperbecause.com/PIOP/files/f7/f7bb6bc5-2c4a-466f-9ae7-b483a2c0dca4.pdf) (wpm). By contrast, the average rate of speech for English speakers is ~150 wpm, with slide presentations often closer to 100 wpm. As a result, when given the choice, many viewers [speed up video playback to ~1.3~1.5 its recorded rate](http://research.microsoft.com/en-us/um/redmond/groups/coet/compression/chi99/paper.pdf) to compensate for the difference.
|
||||
Average adult reads prose text at [250 to 300 words per minute](http://www.paperbecause.com/PIOP/files/f7/f7bb6bc5-2c4a-466f-9ae7-b483a2c0dca4.pdf) (wpm). By contrast, the average rate of speech for English speakers is ~150 wpm, with slide presentations often closer to 100 wpm. As a result, when given the choice, many viewers [speed up video playback to ~1.3\~1.5 its recorded rate](http://research.microsoft.com/en-us/um/redmond/groups/coet/compression/chi99/paper.pdf) to compensate for the difference.
|
||||
|
||||
Many viewers report that [accelerated viewing keeps their attention longer](http://www.enounce.com/docs/BYUPaper020319.pdf): faster delivery keeps the viewer more engaged with the content. In fact, with a little training many end up watching videos at 2x+ the recorded speed. Some studies report that after being exposed to accelerated playback, [listeners become uncomfortable](http://xenia.media.mit.edu/~barons/html/avios92.html#beasleyalteredspeech) if they are forced to return to normal rate of presentation.
|
||||
Many viewers report that [accelerated viewing keeps their attention longer](http://www.enounce.com/docs/BYUPaper020319.pdf): faster delivery keeps the viewer more engaged with the content. In fact, with a little training many end up watching videos at 2x+ the recorded speed. Some studies report that after being exposed to accelerated playback, [listeners become uncomfortable](http://alumni.media.mit.edu/~barons/html/avios92.html#beasleyalteredspeech) if they are forced to return to normal rate of presentation.
|
||||
|
||||
|
||||
## Faster HTML5 Video
|
||||
@@ -24,11 +24,7 @@ Once the extension is installed simply navigate to any page that offers HTML5 vi
|
||||
* **X** - advance video by 10 seconds.
|
||||
* **V** - show/hide the controller.
|
||||
|
||||
Note that you can customize these shortcuts in the extension settings page. Also, a few tips for enabling and forcing HTML5 video:
|
||||
|
||||
* YouTube: make sure you [enable the HTML5 opt-in experiment](http://www.youtube.com/html5).
|
||||
* If you're adventurous, try disabling the Flash plugin in Chrome in chrome://plugins/
|
||||
* If viewing a video on Wistia, right click to switch to HTML5 video, refresh the page, and the controls will appear.
|
||||
_Note: you can customize these shortcut keys in the extension settings page._
|
||||
|
||||
### FAQ
|
||||
|
||||
|
12
inject.css
12
inject.css
@@ -29,6 +29,18 @@
|
||||
top: 40px;
|
||||
}
|
||||
|
||||
/* Google Photos player */
|
||||
/* Inline preview doesn't have any additional hooks, relying on Aria label */
|
||||
a[aria-label^="Video"] .vsc-controller {
|
||||
position: relative;
|
||||
top: 35px;
|
||||
}
|
||||
/* Google Photos full-screen view */
|
||||
#player:not(.ytd-watch) .html5-video-container .vsc-controller {
|
||||
position: relative;
|
||||
top: 50px;
|
||||
}
|
||||
|
||||
/* Netflix player */
|
||||
#netflix-player:not(.player-cinema-mode) .vsc-controller {
|
||||
position: relative;
|
||||
|
66
inject.js
66
inject.js
@@ -50,13 +50,17 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
|
||||
function defineVideoController() {
|
||||
tc.videoController = function(target, parent) {
|
||||
if (target.dataset['vscid']) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.video = target;
|
||||
this.parent = target.parentElement || parent;
|
||||
this.document = target.ownerDocument;
|
||||
this.id = Math.random().toString(36).substr(2, 9);
|
||||
if (!tc.settings.rememberSpeed) {
|
||||
tc.settings.speed = 1.0;
|
||||
tc.settings.resetSpeed = 1.0;
|
||||
tc.settings.resetSpeed = tc.settings.fastSpeed;
|
||||
}
|
||||
this.initializeControls();
|
||||
|
||||
@@ -65,9 +69,6 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
});
|
||||
|
||||
target.addEventListener('ratechange', function(event) {
|
||||
if (target.readyState === 0) {
|
||||
return;
|
||||
}
|
||||
var speed = this.getSpeed();
|
||||
this.speedIndicator.textContent = speed;
|
||||
tc.settings.speed = speed;
|
||||
@@ -103,6 +104,10 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
wrapper.addEventListener('mousedown', prevent, false);
|
||||
wrapper.addEventListener('click', prevent, false);
|
||||
|
||||
if (tc.settings.startHidden) {
|
||||
wrapper.classList.add('vsc-hidden');
|
||||
}
|
||||
|
||||
var styleElem = document.createElement('style')
|
||||
var shadowCSS = chrome.runtime.getURL('shadow.css')
|
||||
var textElem = document.createTextNode(`@import "${shadowCSS}";`)
|
||||
@@ -169,12 +174,26 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
fragment.appendChild(wrapper);
|
||||
|
||||
this.video.classList.add('vsc-initialized');
|
||||
this.video.dataset['vscid'] = this.id;
|
||||
|
||||
switch (location.hostname) {
|
||||
case 'www.amazon.com':
|
||||
// insert before parent to bypass overlay
|
||||
this.parent.parentElement.insertBefore(fragment, this.parent);
|
||||
break;
|
||||
|
||||
case 'www.facebook.com':
|
||||
// set stacking context to same as parent's parent.
|
||||
// + default fallthrough
|
||||
this.parent.style.zIndex = 'auto';
|
||||
|
||||
default:
|
||||
// Note: when triggered via a MutationRecord, it's possible that the
|
||||
// target is not the immediate parent. This appends the controller as
|
||||
// the first element of the target, which may not be the parent.
|
||||
this.parent.insertBefore(fragment, this.parent.firstChild);
|
||||
this.video.classList.add('vsc-initialized');
|
||||
this.video.dataset['vscid'] = this.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,6 +229,13 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
}
|
||||
|
||||
function initializeNow(document) {
|
||||
// in theory, this should only run once, in practice..
|
||||
// that's not guaranteed, hence we enforce own init-once.
|
||||
if (document.body.classList.contains('vsc-initialized')) {
|
||||
return;
|
||||
}
|
||||
document.body.classList.add('vsc-initialized');
|
||||
|
||||
if (document === window.document) {
|
||||
defineVideoController();
|
||||
} else {
|
||||
@@ -263,9 +289,7 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
function checkForVideo(node, parent, added) {
|
||||
if (node.nodeName === 'VIDEO') {
|
||||
if (added) {
|
||||
if (!node.dataset['vscid']) {
|
||||
new tc.videoController(node, parent);
|
||||
}
|
||||
} else {
|
||||
if (node.classList.contains('vsc-initialized')) {
|
||||
let id = node.dataset['vscid'];
|
||||
@@ -342,13 +366,7 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.0625);
|
||||
v.playbackRate = Number(s.toFixed(2));
|
||||
} else if (action === 'reset') {
|
||||
if(v.playbackRate === 1.0) {
|
||||
v.playbackRate = tc.settings.resetSpeed;
|
||||
} else {
|
||||
tc.settings.resetSpeed = v.playbackRate;
|
||||
chrome.storage.local.set({'resetSpeed': v.playbackRate});
|
||||
v.playbackRate = 1.0;
|
||||
}
|
||||
resetSpeed(v, 1.0);
|
||||
} else if (action === 'close') {
|
||||
v.classList.add('vsc-cancelled');
|
||||
controller.remove();
|
||||
@@ -358,23 +376,25 @@ chrome.runtime.sendMessage({}, function(response) {
|
||||
} else if (action === 'drag') {
|
||||
handleDrag(v, controller);
|
||||
} else if (action === 'fast') {
|
||||
playVideoAtFastSpeed(v);
|
||||
resetSpeed(v, tc.settings.fastSpeed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function playVideoAtFastSpeed(video) {
|
||||
video.playbackRate = tc.settings.fastSpeed;
|
||||
function resetSpeed(v, target) {
|
||||
if (v.playbackRate === target) {
|
||||
v.playbackRate = tc.settings.resetSpeed;
|
||||
} else {
|
||||
tc.settings.resetSpeed = v.playbackRate;
|
||||
chrome.storage.local.set({'resetSpeed': v.playbackRate});
|
||||
v.playbackRate = target;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDrag(video, controller) {
|
||||
const parentElement = controller.parentElement,
|
||||
boundRect = parentElement.getBoundingClientRect(),
|
||||
shadowController = controller.querySelector('#controller'),
|
||||
drag = shadowController.querySelector('.draggable'),
|
||||
offsetLeft = boundRect.left + drag.offsetLeft + drag.offsetWidth,
|
||||
offsetTop = boundRect.top + drag.offsetTop + drag.offsetHeight;
|
||||
shadowController = controller.querySelector('#controller');
|
||||
|
||||
video.classList.add('vcs-dragging');
|
||||
shadowController.classList.add('dragging');
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Video Speed Controller",
|
||||
"short_name": "videospeed",
|
||||
"version": "0.4.6.1",
|
||||
"version": "0.4.8",
|
||||
"manifest_version": 2,
|
||||
"description": "Speed up, slow down, advance and rewind any HTML5 video with quick shortcuts.",
|
||||
"homepage_url": "https://github.com/codebicycle/videospeed",
|
||||
@@ -26,6 +26,7 @@
|
||||
"exclude_matches": [
|
||||
"https://plus.google.com/hangouts/*",
|
||||
"https://hangouts.google.com/hangouts/*",
|
||||
"https://meet.google.com/*",
|
||||
"https://teamtreehouse.com/*",
|
||||
"http://www.hitbox.tv/*"
|
||||
],
|
||||
|
Reference in New Issue
Block a user