mirror of
https://github.com/SoPat712/videospeed.git
synced 2025-12-26 03:28:54 -05:00
general performance updates
This commit is contained in:
120
inject.js
120
inject.js
@@ -24,8 +24,8 @@ var tc = {
|
||||
`.replace(regStrip, ""),
|
||||
defaultLogLevel: 4,
|
||||
logLevel: 5, // Set to 5 to see your debug logs
|
||||
enableSubtitleNudge: true,
|
||||
subtitleNudgeInterval: 25,
|
||||
enableSubtitleNudge: true, // Enabled by default, but only activates on YouTube
|
||||
subtitleNudgeInterval: 100, // Reduced from 25ms to 100ms (10x/sec instead of 40x/sec)
|
||||
subtitleNudgeAmount: 0.001
|
||||
},
|
||||
mediaElements: [],
|
||||
@@ -123,7 +123,7 @@ chrome.storage.sync.get(tc.settings, function (storage) {
|
||||
? Boolean(storage.enableSubtitleNudge)
|
||||
: tc.settings.enableSubtitleNudge;
|
||||
tc.settings.subtitleNudgeInterval =
|
||||
Number(storage.subtitleNudgeInterval) || 25;
|
||||
Number(storage.subtitleNudgeInterval) || 100; // Default 100ms for better performance
|
||||
tc.settings.subtitleNudgeAmount =
|
||||
Number(storage.subtitleNudgeAmount) || tc.settings.subtitleNudgeAmount;
|
||||
if (
|
||||
@@ -378,6 +378,9 @@ function defineVideoController() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the target speed so we can always revert to it
|
||||
this.targetSpeed = this.video.playbackRate;
|
||||
|
||||
const performNudge = () => {
|
||||
// Check if we should stop
|
||||
if (!this.video || this.video.paused || this.video.playbackRate === 1.0) {
|
||||
@@ -385,20 +388,31 @@ function defineVideoController() {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentRate = this.video.playbackRate;
|
||||
// CRITICAL: Don't nudge if tab is hidden - prevents speed drift
|
||||
if (document.hidden) {
|
||||
this.nudgeAnimationId = setTimeout(performNudge, tc.settings.subtitleNudgeInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set flag to prevent ratechange listener from interfering
|
||||
tc.isNudging = true;
|
||||
|
||||
// Cache values to avoid repeated property access
|
||||
const targetSpeed = this.targetSpeed;
|
||||
const nudgeAmount = tc.settings.subtitleNudgeAmount;
|
||||
|
||||
// Apply nudge
|
||||
this.video.playbackRate = currentRate + nudgeAmount;
|
||||
// Apply nudge from the stored target speed (not current rate)
|
||||
this.video.playbackRate = targetSpeed + nudgeAmount;
|
||||
|
||||
// Revert on next frame
|
||||
requestAnimationFrame(() => {
|
||||
if (this.video) {
|
||||
this.video.playbackRate = currentRate;
|
||||
// Revert synchronously after a microtask to ensure it happens immediately
|
||||
Promise.resolve().then(() => {
|
||||
if (this.video && targetSpeed) {
|
||||
this.video.playbackRate = targetSpeed;
|
||||
}
|
||||
tc.isNudging = false;
|
||||
});
|
||||
|
||||
// Schedule next nudge using setTimeout instead of continuous RAF loop
|
||||
// Schedule next nudge
|
||||
this.nudgeAnimationId = setTimeout(performNudge, tc.settings.subtitleNudgeInterval);
|
||||
};
|
||||
|
||||
@@ -413,6 +427,8 @@ function defineVideoController() {
|
||||
this.nudgeAnimationId = null;
|
||||
log(`Nudge: Stopping.`, 5);
|
||||
}
|
||||
// Clear the target speed when stopping
|
||||
this.targetSpeed = null;
|
||||
};
|
||||
|
||||
tc.videoController.prototype.performImmediateNudge = function () {
|
||||
@@ -427,25 +443,27 @@ function defineVideoController() {
|
||||
!tc.settings.enableSubtitleNudge ||
|
||||
!this.video ||
|
||||
this.video.paused ||
|
||||
this.video.playbackRate === 1.0
|
||||
this.video.playbackRate === 1.0 ||
|
||||
document.hidden
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentRate = this.video.playbackRate;
|
||||
const targetRate = this.targetSpeed || this.video.playbackRate;
|
||||
const nudgeAmount = tc.settings.subtitleNudgeAmount;
|
||||
|
||||
// Apply nudge
|
||||
this.video.playbackRate = currentRate + nudgeAmount;
|
||||
tc.isNudging = true;
|
||||
this.video.playbackRate = targetRate + nudgeAmount;
|
||||
|
||||
// Revert on next frame
|
||||
requestAnimationFrame(() => {
|
||||
// Revert synchronously via microtask
|
||||
Promise.resolve().then(() => {
|
||||
if (this.video) {
|
||||
this.video.playbackRate = currentRate;
|
||||
this.video.playbackRate = targetRate;
|
||||
}
|
||||
tc.isNudging = false;
|
||||
});
|
||||
|
||||
log(`Immediate nudge performed at rate ${currentRate.toFixed(2)}`, 5);
|
||||
log(`Immediate nudge performed at rate ${targetRate.toFixed(2)}`, 5);
|
||||
};
|
||||
|
||||
tc.videoController.prototype.initializeControls = function () {
|
||||
@@ -734,10 +752,21 @@ function initializeNow(doc, forceReinit = false) {
|
||||
});
|
||||
|
||||
if (!doc.vscMutationObserverAttached) {
|
||||
// Throttle mutation processing to reduce CPU usage
|
||||
let mutationProcessingScheduled = false;
|
||||
let pendingMutations = [];
|
||||
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
pendingMutations.push(...mutations);
|
||||
|
||||
if (!mutationProcessingScheduled) {
|
||||
mutationProcessingScheduled = true;
|
||||
requestIdleCallback(
|
||||
(_) => {
|
||||
mutations.forEach(function (mutation) {
|
||||
const mutationsToProcess = pendingMutations.splice(0);
|
||||
mutationProcessingScheduled = false;
|
||||
|
||||
mutationsToProcess.forEach(function (mutation) {
|
||||
switch (mutation.type) {
|
||||
case "childList":
|
||||
mutation.addedNodes.forEach(function (node) {
|
||||
@@ -765,6 +794,7 @@ function initializeNow(doc, forceReinit = false) {
|
||||
target.attributes["aria-hidden"] &&
|
||||
target.attributes["aria-hidden"].value == "false"
|
||||
) {
|
||||
// Only scan shadow DOM if absolutely necessary (expensive operation)
|
||||
var flattenedNodes = getShadow(document.body);
|
||||
var node = flattenedNodes.filter(
|
||||
(x) => x.tagName == "VIDEO"
|
||||
@@ -784,6 +814,7 @@ function initializeNow(doc, forceReinit = false) {
|
||||
},
|
||||
{ timeout: 1000 }
|
||||
);
|
||||
}
|
||||
});
|
||||
function checkForVideo(node, parent, added) {
|
||||
if (!added && document.body.contains(node)) return;
|
||||
@@ -825,7 +856,7 @@ function initializeNow(doc, forceReinit = false) {
|
||||
}
|
||||
}
|
||||
observer.observe(doc, {
|
||||
attributeFilter: ["aria-hidden", "src", "currentSrc", "style", "class"],
|
||||
attributeFilter: ["aria-hidden", "src", "currentSrc"], // Removed "style" and "class" for better performance
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true
|
||||
@@ -955,11 +986,17 @@ function initializeNow(doc, forceReinit = false) {
|
||||
// Listen for hashchange as well
|
||||
window.addEventListener('hashchange', () => handleNavigation('hashchange'));
|
||||
|
||||
// Also intercept fetch and XMLHttpRequest for AJAX-heavy sites
|
||||
// Throttle fetch-based video scanning to reduce CPU usage
|
||||
let lastFetchScanTime = 0;
|
||||
const FETCH_SCAN_THROTTLE = 2000; // Only scan once every 2 seconds max
|
||||
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function (...args) {
|
||||
return originalFetch.apply(this, args).then(response => {
|
||||
// After any fetch completes, check for new videos
|
||||
// Throttle video scanning after fetch to avoid excessive CPU usage
|
||||
const now = Date.now();
|
||||
if (now - lastFetchScanTime > FETCH_SCAN_THROTTLE) {
|
||||
lastFetchScanTime = now;
|
||||
setTimeout(() => {
|
||||
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
||||
const videos = document.querySelectorAll(q);
|
||||
@@ -970,6 +1007,7 @@ function initializeNow(doc, forceReinit = false) {
|
||||
}
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
};
|
||||
@@ -982,27 +1020,27 @@ function initializeNow(doc, forceReinit = false) {
|
||||
const periodicScan = () => {
|
||||
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
||||
const allVideos = doc.querySelectorAll(q);
|
||||
let foundNew = false;
|
||||
let foundNewCount = 0;
|
||||
|
||||
allVideos.forEach(video => {
|
||||
if (!video.vsc && (video.src || video.currentSrc || video.readyState > 0)) {
|
||||
log(`Periodic scan found missed ${video.tagName.toLowerCase()}`, 4);
|
||||
checkForVideo(video, video.parentElement, true);
|
||||
foundNew = true;
|
||||
foundNewCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (foundNew) {
|
||||
log(`Periodic scan found ${foundNew} new videos`, 4);
|
||||
if (foundNewCount > 0) {
|
||||
log(`Periodic scan found ${foundNewCount} new videos`, 4);
|
||||
}
|
||||
};
|
||||
|
||||
// Run periodic scan every 3 seconds, but only if we have videos on the page
|
||||
// Run periodic scan every 5 seconds (reduced frequency), only if we have videos
|
||||
setInterval(() => {
|
||||
if (tc.mediaElements.length > 0 || doc.querySelector(tc.settings.audioBoolean ? "video,audio" : "video")) {
|
||||
periodicScan();
|
||||
}
|
||||
}, 3000);
|
||||
}, 5000); // Increased from 3s to 5s for better performance
|
||||
|
||||
doc.vscPeriodicScanAttached = true;
|
||||
}
|
||||
@@ -1022,6 +1060,9 @@ function setSpeed(video, speed, isInitialCall = false, isUserKeyPress = false) {
|
||||
tc.settings.lastSpeed = numericSpeed;
|
||||
video.vsc.speedIndicator.textContent = numericSpeed.toFixed(2);
|
||||
|
||||
// Update the target speed for nudge so it knows what to revert to
|
||||
video.vsc.targetSpeed = numericSpeed;
|
||||
|
||||
if (isUserKeyPress && !isInitialCall && video.vsc && video.vsc.div) {
|
||||
runAction("blink", 1000, null, video); // Pass video to blink
|
||||
}
|
||||
@@ -1108,18 +1149,19 @@ function runAction(action, value, e) {
|
||||
v.currentTime += numValue;
|
||||
break;
|
||||
case "faster":
|
||||
setSpeed(
|
||||
v,
|
||||
Math.min(
|
||||
(v.playbackRate < 0.07 ? 0.07 : v.playbackRate) + numValue,
|
||||
16
|
||||
),
|
||||
false,
|
||||
true
|
||||
);
|
||||
// Round to the step precision to avoid floating-point issues (e.g., 1.80 + 0.1 = 1.9000000000000001)
|
||||
var fasterStep = numValue;
|
||||
var fasterPrecision = Math.round(1 / fasterStep); // e.g., 0.1 -> 10, 0.05 -> 20, 0.25 -> 4
|
||||
var newFasterSpeed = (v.playbackRate < 0.07 ? 0.07 : v.playbackRate) + fasterStep;
|
||||
newFasterSpeed = Math.round(newFasterSpeed * fasterPrecision) / fasterPrecision;
|
||||
setSpeed(v, Math.min(newFasterSpeed, 16), false, true);
|
||||
break;
|
||||
case "slower":
|
||||
setSpeed(v, Math.max(v.playbackRate - numValue, 0.07), false, true);
|
||||
var slowerStep = numValue;
|
||||
var slowerPrecision = Math.round(1 / slowerStep);
|
||||
var newSlowerSpeed = v.playbackRate - slowerStep;
|
||||
newSlowerSpeed = Math.round(newSlowerSpeed * slowerPrecision) / slowerPrecision;
|
||||
setSpeed(v, Math.max(newSlowerSpeed, 0.07), false, true);
|
||||
break;
|
||||
case "reset":
|
||||
resetSpeed(v, 1.0, false); // Use enhanced resetSpeed
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Video Speed Controller",
|
||||
"short_name": "videospeed",
|
||||
"version": "2.0.1",
|
||||
"version": "2.1.0",
|
||||
"manifest_version": 2,
|
||||
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts",
|
||||
"homepage_url": "https://github.com/SoPat712/videospeed",
|
||||
|
||||
Reference in New Issue
Block a user