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
|
# 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
|
## 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.
|
* **X** - advance video by 10 seconds.
|
||||||
* **V** - show/hide the controller.
|
* **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:
|
_Note: you can customize these shortcut keys in the extension settings page._
|
||||||
|
|
||||||
* 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.
|
|
||||||
|
|
||||||
### FAQ
|
### FAQ
|
||||||
|
|
||||||
|
12
inject.css
12
inject.css
@@ -29,6 +29,18 @@
|
|||||||
top: 40px;
|
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 */
|
||||||
#netflix-player:not(.player-cinema-mode) .vsc-controller {
|
#netflix-player:not(.player-cinema-mode) .vsc-controller {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
66
inject.js
66
inject.js
@@ -50,13 +50,17 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
|
|
||||||
function defineVideoController() {
|
function defineVideoController() {
|
||||||
tc.videoController = function(target, parent) {
|
tc.videoController = function(target, parent) {
|
||||||
|
if (target.dataset['vscid']) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.video = target;
|
this.video = target;
|
||||||
this.parent = target.parentElement || parent;
|
this.parent = target.parentElement || parent;
|
||||||
this.document = target.ownerDocument;
|
this.document = target.ownerDocument;
|
||||||
this.id = Math.random().toString(36).substr(2, 9);
|
this.id = Math.random().toString(36).substr(2, 9);
|
||||||
if (!tc.settings.rememberSpeed) {
|
if (!tc.settings.rememberSpeed) {
|
||||||
tc.settings.speed = 1.0;
|
tc.settings.speed = 1.0;
|
||||||
tc.settings.resetSpeed = 1.0;
|
tc.settings.resetSpeed = tc.settings.fastSpeed;
|
||||||
}
|
}
|
||||||
this.initializeControls();
|
this.initializeControls();
|
||||||
|
|
||||||
@@ -65,9 +69,6 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
target.addEventListener('ratechange', function(event) {
|
target.addEventListener('ratechange', function(event) {
|
||||||
if (target.readyState === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var speed = this.getSpeed();
|
var speed = this.getSpeed();
|
||||||
this.speedIndicator.textContent = speed;
|
this.speedIndicator.textContent = speed;
|
||||||
tc.settings.speed = speed;
|
tc.settings.speed = speed;
|
||||||
@@ -103,6 +104,10 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
wrapper.addEventListener('mousedown', prevent, false);
|
wrapper.addEventListener('mousedown', prevent, false);
|
||||||
wrapper.addEventListener('click', prevent, false);
|
wrapper.addEventListener('click', prevent, false);
|
||||||
|
|
||||||
|
if (tc.settings.startHidden) {
|
||||||
|
wrapper.classList.add('vsc-hidden');
|
||||||
|
}
|
||||||
|
|
||||||
var styleElem = document.createElement('style')
|
var styleElem = document.createElement('style')
|
||||||
var shadowCSS = chrome.runtime.getURL('shadow.css')
|
var shadowCSS = chrome.runtime.getURL('shadow.css')
|
||||||
var textElem = document.createTextNode(`@import "${shadowCSS}";`)
|
var textElem = document.createTextNode(`@import "${shadowCSS}";`)
|
||||||
@@ -169,12 +174,26 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
var fragment = document.createDocumentFragment();
|
var fragment = document.createDocumentFragment();
|
||||||
fragment.appendChild(wrapper);
|
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
|
// Note: when triggered via a MutationRecord, it's possible that the
|
||||||
// target is not the immediate parent. This appends the controller as
|
// target is not the immediate parent. This appends the controller as
|
||||||
// the first element of the target, which may not be the parent.
|
// the first element of the target, which may not be the parent.
|
||||||
this.parent.insertBefore(fragment, this.parent.firstChild);
|
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) {
|
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) {
|
if (document === window.document) {
|
||||||
defineVideoController();
|
defineVideoController();
|
||||||
} else {
|
} else {
|
||||||
@@ -263,9 +289,7 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
function checkForVideo(node, parent, added) {
|
function checkForVideo(node, parent, added) {
|
||||||
if (node.nodeName === 'VIDEO') {
|
if (node.nodeName === 'VIDEO') {
|
||||||
if (added) {
|
if (added) {
|
||||||
if (!node.dataset['vscid']) {
|
|
||||||
new tc.videoController(node, parent);
|
new tc.videoController(node, parent);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (node.classList.contains('vsc-initialized')) {
|
if (node.classList.contains('vsc-initialized')) {
|
||||||
let id = node.dataset['vscid'];
|
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);
|
var s = Math.max(v.playbackRate - tc.settings.speedStep, 0.0625);
|
||||||
v.playbackRate = Number(s.toFixed(2));
|
v.playbackRate = Number(s.toFixed(2));
|
||||||
} else if (action === 'reset') {
|
} else if (action === 'reset') {
|
||||||
if(v.playbackRate === 1.0) {
|
resetSpeed(v, 1.0);
|
||||||
v.playbackRate = tc.settings.resetSpeed;
|
|
||||||
} else {
|
|
||||||
tc.settings.resetSpeed = v.playbackRate;
|
|
||||||
chrome.storage.local.set({'resetSpeed': v.playbackRate});
|
|
||||||
v.playbackRate = 1.0;
|
|
||||||
}
|
|
||||||
} else if (action === 'close') {
|
} else if (action === 'close') {
|
||||||
v.classList.add('vsc-cancelled');
|
v.classList.add('vsc-cancelled');
|
||||||
controller.remove();
|
controller.remove();
|
||||||
@@ -358,23 +376,25 @@ chrome.runtime.sendMessage({}, function(response) {
|
|||||||
} else if (action === 'drag') {
|
} else if (action === 'drag') {
|
||||||
handleDrag(v, controller);
|
handleDrag(v, controller);
|
||||||
} else if (action === 'fast') {
|
} else if (action === 'fast') {
|
||||||
playVideoAtFastSpeed(v);
|
resetSpeed(v, tc.settings.fastSpeed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function playVideoAtFastSpeed(video) {
|
function resetSpeed(v, target) {
|
||||||
video.playbackRate = tc.settings.fastSpeed;
|
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) {
|
function handleDrag(video, controller) {
|
||||||
const parentElement = controller.parentElement,
|
const parentElement = controller.parentElement,
|
||||||
boundRect = parentElement.getBoundingClientRect(),
|
shadowController = controller.querySelector('#controller');
|
||||||
shadowController = controller.querySelector('#controller'),
|
|
||||||
drag = shadowController.querySelector('.draggable'),
|
|
||||||
offsetLeft = boundRect.left + drag.offsetLeft + drag.offsetWidth,
|
|
||||||
offsetTop = boundRect.top + drag.offsetTop + drag.offsetHeight;
|
|
||||||
|
|
||||||
video.classList.add('vcs-dragging');
|
video.classList.add('vcs-dragging');
|
||||||
shadowController.classList.add('dragging');
|
shadowController.classList.add('dragging');
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Video Speed Controller",
|
"name": "Video Speed Controller",
|
||||||
"short_name": "videospeed",
|
"short_name": "videospeed",
|
||||||
"version": "0.4.6.1",
|
"version": "0.4.8",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"description": "Speed up, slow down, advance and rewind any HTML5 video with quick shortcuts.",
|
"description": "Speed up, slow down, advance and rewind any HTML5 video with quick shortcuts.",
|
||||||
"homepage_url": "https://github.com/codebicycle/videospeed",
|
"homepage_url": "https://github.com/codebicycle/videospeed",
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"exclude_matches": [
|
"exclude_matches": [
|
||||||
"https://plus.google.com/hangouts/*",
|
"https://plus.google.com/hangouts/*",
|
||||||
"https://hangouts.google.com/hangouts/*",
|
"https://hangouts.google.com/hangouts/*",
|
||||||
|
"https://meet.google.com/*",
|
||||||
"https://teamtreehouse.com/*",
|
"https://teamtreehouse.com/*",
|
||||||
"http://www.hitbox.tv/*"
|
"http://www.hitbox.tv/*"
|
||||||
],
|
],
|
||||||
|
Reference in New Issue
Block a user