mirror of
https://github.com/SoPat712/videospeed.git
synced 2025-12-26 11:37:21 -05:00
Compare commits
2 Commits
1f8cb4411e
...
firefox-po
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef86a70ca5
|
||
|
|
5009e83f62
|
16
build.py
16
build.py
@@ -63,14 +63,26 @@ def main():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"⚠️ Failed to remove {f}: {e}")
|
print(f"⚠️ Failed to remove {f}: {e}")
|
||||||
|
|
||||||
|
# Read current version from manifest.json
|
||||||
|
current_dir = os.getcwd()
|
||||||
|
manifest_path = os.path.join(current_dir, TARGET_FILE)
|
||||||
|
current_version = "unknown"
|
||||||
|
|
||||||
|
if os.path.exists(manifest_path):
|
||||||
|
with open(manifest_path, "r", encoding="utf-8") as f:
|
||||||
|
for line in f:
|
||||||
|
match = re.match(r'\s*"version":\s*"([^"]+)"', line)
|
||||||
|
if match:
|
||||||
|
current_version = match.group(1)
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"📦 Current version: {current_version}")
|
||||||
base_version = input("Enter the new base version (e.g., 2.0.1): ").strip()
|
base_version = input("Enter the new base version (e.g., 2.0.1): ").strip()
|
||||||
if not base_version:
|
if not base_version:
|
||||||
print("❌ No version entered. Exiting.")
|
print("❌ No version entered. Exiting.")
|
||||||
return
|
return
|
||||||
|
|
||||||
firefox_version = f"{base_version}.0"
|
firefox_version = f"{base_version}.0"
|
||||||
current_dir = os.getcwd()
|
|
||||||
manifest_path = os.path.join(current_dir, TARGET_FILE)
|
|
||||||
|
|
||||||
# Step 1: Update manifest.json on disk to base_version
|
# Step 1: Update manifest.json on disk to base_version
|
||||||
if os.path.exists(manifest_path):
|
if os.path.exists(manifest_path):
|
||||||
|
|||||||
293
inject.js
293
inject.js
@@ -24,8 +24,8 @@ var tc = {
|
|||||||
`.replace(regStrip, ""),
|
`.replace(regStrip, ""),
|
||||||
defaultLogLevel: 4,
|
defaultLogLevel: 4,
|
||||||
logLevel: 5, // Set to 5 to see your debug logs
|
logLevel: 5, // Set to 5 to see your debug logs
|
||||||
enableSubtitleNudge: true,
|
enableSubtitleNudge: true, // Enabled by default, but only activates on YouTube
|
||||||
subtitleNudgeInterval: 25,
|
subtitleNudgeInterval: 100, // Reduced from 25ms to 100ms (10x/sec instead of 40x/sec)
|
||||||
subtitleNudgeAmount: 0.001
|
subtitleNudgeAmount: 0.001
|
||||||
},
|
},
|
||||||
mediaElements: [],
|
mediaElements: [],
|
||||||
@@ -123,7 +123,7 @@ chrome.storage.sync.get(tc.settings, function (storage) {
|
|||||||
? Boolean(storage.enableSubtitleNudge)
|
? Boolean(storage.enableSubtitleNudge)
|
||||||
: tc.settings.enableSubtitleNudge;
|
: tc.settings.enableSubtitleNudge;
|
||||||
tc.settings.subtitleNudgeInterval =
|
tc.settings.subtitleNudgeInterval =
|
||||||
Number(storage.subtitleNudgeInterval) || 25;
|
Number(storage.subtitleNudgeInterval) || 100; // Default 100ms for better performance
|
||||||
tc.settings.subtitleNudgeAmount =
|
tc.settings.subtitleNudgeAmount =
|
||||||
Number(storage.subtitleNudgeAmount) || tc.settings.subtitleNudgeAmount;
|
Number(storage.subtitleNudgeAmount) || tc.settings.subtitleNudgeAmount;
|
||||||
if (
|
if (
|
||||||
@@ -184,7 +184,7 @@ function defineVideoController() {
|
|||||||
target.vsc = this;
|
target.vsc = this;
|
||||||
this.video = target;
|
this.video = target;
|
||||||
this.parent = target.parentElement || parent;
|
this.parent = target.parentElement || parent;
|
||||||
this.nudgeIntervalId = null;
|
this.nudgeAnimationId = null;
|
||||||
|
|
||||||
log(`Creating video controller for ${target.tagName} with src: ${target.src || target.currentSrc || 'none'}`, 4);
|
log(`Creating video controller for ${target.tagName} with src: ${target.src || target.currentSrc || 'none'}`, 4);
|
||||||
|
|
||||||
@@ -367,62 +367,103 @@ function defineVideoController() {
|
|||||||
this.video.currentSrc &&
|
this.video.currentSrc &&
|
||||||
this.video.currentSrc.includes("googlevideo.com")) ||
|
this.video.currentSrc.includes("googlevideo.com")) ||
|
||||||
location.hostname.includes("youtube.com");
|
location.hostname.includes("youtube.com");
|
||||||
if (!isYouTube) return;
|
|
||||||
if (
|
if (
|
||||||
|
!isYouTube ||
|
||||||
!tc.settings.enableSubtitleNudge ||
|
!tc.settings.enableSubtitleNudge ||
|
||||||
this.nudgeIntervalId !== null ||
|
this.nudgeAnimationId !== null ||
|
||||||
!this.video
|
!this.video ||
|
||||||
|
this.video.paused ||
|
||||||
|
this.video.playbackRate === 1.0
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.video.paused || this.video.playbackRate === 1.0) {
|
|
||||||
this.stopSubtitleNudge();
|
// Store the target speed so we can always revert to it
|
||||||
return;
|
this.targetSpeed = this.video.playbackRate;
|
||||||
}
|
|
||||||
// Additional check to not start if paused
|
const performNudge = () => {
|
||||||
if (this.video.paused) {
|
// Check if we should stop
|
||||||
return;
|
if (!this.video || this.video.paused || this.video.playbackRate === 1.0) {
|
||||||
}
|
|
||||||
log(`Nudge: Starting interval: ${tc.settings.subtitleNudgeInterval}ms.`, 5);
|
|
||||||
this.nudgeIntervalId = setInterval(() => {
|
|
||||||
if (
|
|
||||||
!this.video ||
|
|
||||||
this.video.paused ||
|
|
||||||
this.video.ended ||
|
|
||||||
this.video.playbackRate === 1.0 ||
|
|
||||||
tc.isNudging
|
|
||||||
) {
|
|
||||||
this.stopSubtitleNudge();
|
this.stopSubtitleNudge();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Double-check pause state before nudging
|
|
||||||
if (this.video.paused) {
|
// CRITICAL: Don't nudge if tab is hidden - prevents speed drift
|
||||||
this.stopSubtitleNudge();
|
if (document.hidden) {
|
||||||
|
this.nudgeAnimationId = setTimeout(performNudge, tc.settings.subtitleNudgeInterval);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const currentRate = this.video.playbackRate;
|
|
||||||
const nudgeAmount = tc.settings.subtitleNudgeAmount;
|
// Set flag to prevent ratechange listener from interfering
|
||||||
tc.isNudging = true;
|
tc.isNudging = true;
|
||||||
this.video.playbackRate = currentRate + nudgeAmount;
|
|
||||||
requestAnimationFrame(() => {
|
// Cache values to avoid repeated property access
|
||||||
if (
|
const targetSpeed = this.targetSpeed;
|
||||||
this.video &&
|
const nudgeAmount = tc.settings.subtitleNudgeAmount;
|
||||||
Math.abs(this.video.playbackRate - (currentRate + nudgeAmount)) <
|
|
||||||
nudgeAmount * 1.5
|
// Apply nudge from the stored target speed (not current rate)
|
||||||
) {
|
this.video.playbackRate = targetSpeed + nudgeAmount;
|
||||||
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;
|
tc.isNudging = false;
|
||||||
});
|
});
|
||||||
}, tc.settings.subtitleNudgeInterval);
|
|
||||||
|
// Schedule next nudge
|
||||||
|
this.nudgeAnimationId = setTimeout(performNudge, tc.settings.subtitleNudgeInterval);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start the first nudge
|
||||||
|
this.nudgeAnimationId = setTimeout(performNudge, tc.settings.subtitleNudgeInterval);
|
||||||
|
log(`Nudge: Starting with interval ${tc.settings.subtitleNudgeInterval}ms.`, 5);
|
||||||
};
|
};
|
||||||
|
|
||||||
tc.videoController.prototype.stopSubtitleNudge = function () {
|
tc.videoController.prototype.stopSubtitleNudge = function () {
|
||||||
if (this.nudgeIntervalId !== null) {
|
if (this.nudgeAnimationId !== null) {
|
||||||
|
clearTimeout(this.nudgeAnimationId);
|
||||||
|
this.nudgeAnimationId = null;
|
||||||
log(`Nudge: Stopping.`, 5);
|
log(`Nudge: Stopping.`, 5);
|
||||||
clearInterval(this.nudgeIntervalId);
|
|
||||||
this.nudgeIntervalId = null;
|
|
||||||
}
|
}
|
||||||
|
// Clear the target speed when stopping
|
||||||
|
this.targetSpeed = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
tc.videoController.prototype.performImmediateNudge = function () {
|
||||||
|
const isYouTube =
|
||||||
|
(this.video &&
|
||||||
|
this.video.currentSrc &&
|
||||||
|
this.video.currentSrc.includes("googlevideo.com")) ||
|
||||||
|
location.hostname.includes("youtube.com");
|
||||||
|
|
||||||
|
if (
|
||||||
|
!isYouTube ||
|
||||||
|
!tc.settings.enableSubtitleNudge ||
|
||||||
|
!this.video ||
|
||||||
|
this.video.paused ||
|
||||||
|
this.video.playbackRate === 1.0 ||
|
||||||
|
document.hidden
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetRate = this.targetSpeed || this.video.playbackRate;
|
||||||
|
const nudgeAmount = tc.settings.subtitleNudgeAmount;
|
||||||
|
|
||||||
|
tc.isNudging = true;
|
||||||
|
this.video.playbackRate = targetRate + nudgeAmount;
|
||||||
|
|
||||||
|
// Revert synchronously via microtask
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
if (this.video) {
|
||||||
|
this.video.playbackRate = targetRate;
|
||||||
|
}
|
||||||
|
tc.isNudging = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
log(`Immediate nudge performed at rate ${targetRate.toFixed(2)}`, 5);
|
||||||
};
|
};
|
||||||
|
|
||||||
tc.videoController.prototype.initializeControls = function () {
|
tc.videoController.prototype.initializeControls = function () {
|
||||||
@@ -711,56 +752,69 @@ function initializeNow(doc, forceReinit = false) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!doc.vscMutationObserverAttached) {
|
if (!doc.vscMutationObserverAttached) {
|
||||||
|
// Throttle mutation processing to reduce CPU usage
|
||||||
|
let mutationProcessingScheduled = false;
|
||||||
|
let pendingMutations = [];
|
||||||
|
|
||||||
const observer = new MutationObserver(function (mutations) {
|
const observer = new MutationObserver(function (mutations) {
|
||||||
requestIdleCallback(
|
pendingMutations.push(...mutations);
|
||||||
(_) => {
|
|
||||||
mutations.forEach(function (mutation) {
|
if (!mutationProcessingScheduled) {
|
||||||
switch (mutation.type) {
|
mutationProcessingScheduled = true;
|
||||||
case "childList":
|
requestIdleCallback(
|
||||||
mutation.addedNodes.forEach(function (node) {
|
(_) => {
|
||||||
if (typeof node === "function") return;
|
const mutationsToProcess = pendingMutations.splice(0);
|
||||||
checkForVideo(node, node.parentNode || mutation.target, true);
|
mutationProcessingScheduled = false;
|
||||||
});
|
|
||||||
mutation.removedNodes.forEach(function (node) {
|
mutationsToProcess.forEach(function (mutation) {
|
||||||
if (typeof node === "function") return;
|
switch (mutation.type) {
|
||||||
checkForVideo(
|
case "childList":
|
||||||
node,
|
mutation.addedNodes.forEach(function (node) {
|
||||||
node.parentNode || mutation.target,
|
if (typeof node === "function") return;
|
||||||
false
|
checkForVideo(node, node.parentNode || mutation.target, true);
|
||||||
);
|
});
|
||||||
});
|
mutation.removedNodes.forEach(function (node) {
|
||||||
break;
|
if (typeof node === "function") return;
|
||||||
case "attributes":
|
|
||||||
// Enhanced attribute monitoring for video detection
|
|
||||||
const target = mutation.target;
|
|
||||||
if (target.tagName === "VIDEO" || target.tagName === "AUDIO") {
|
|
||||||
// Video/audio element had attributes changed - check if it needs controller
|
|
||||||
if (!target.vsc && (target.src || target.currentSrc)) {
|
|
||||||
checkForVideo(target, target.parentNode, true);
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
target.attributes["aria-hidden"] &&
|
|
||||||
target.attributes["aria-hidden"].value == "false"
|
|
||||||
) {
|
|
||||||
var flattenedNodes = getShadow(document.body);
|
|
||||||
var node = flattenedNodes.filter(
|
|
||||||
(x) => x.tagName == "VIDEO"
|
|
||||||
)[0];
|
|
||||||
if (node) {
|
|
||||||
if (node.vsc) node.vsc.remove();
|
|
||||||
checkForVideo(
|
checkForVideo(
|
||||||
node,
|
node,
|
||||||
node.parentNode || mutation.target,
|
node.parentNode || mutation.target,
|
||||||
true
|
false
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "attributes":
|
||||||
|
// Enhanced attribute monitoring for video detection
|
||||||
|
const target = mutation.target;
|
||||||
|
if (target.tagName === "VIDEO" || target.tagName === "AUDIO") {
|
||||||
|
// Video/audio element had attributes changed - check if it needs controller
|
||||||
|
if (!target.vsc && (target.src || target.currentSrc)) {
|
||||||
|
checkForVideo(target, target.parentNode, true);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
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"
|
||||||
|
)[0];
|
||||||
|
if (node) {
|
||||||
|
if (node.vsc) node.vsc.remove();
|
||||||
|
checkForVideo(
|
||||||
|
node,
|
||||||
|
node.parentNode || mutation.target,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
}
|
||||||
}
|
});
|
||||||
});
|
},
|
||||||
},
|
{ timeout: 1000 }
|
||||||
{ timeout: 1000 }
|
);
|
||||||
);
|
}
|
||||||
});
|
});
|
||||||
function checkForVideo(node, parent, added) {
|
function checkForVideo(node, parent, added) {
|
||||||
if (!added && document.body.contains(node)) return;
|
if (!added && document.body.contains(node)) return;
|
||||||
@@ -802,7 +856,7 @@ function initializeNow(doc, forceReinit = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
observer.observe(doc, {
|
observer.observe(doc, {
|
||||||
attributeFilter: ["aria-hidden", "src", "currentSrc", "style", "class"],
|
attributeFilter: ["aria-hidden", "src", "currentSrc"], // Removed "style" and "class" for better performance
|
||||||
childList: true,
|
childList: true,
|
||||||
subtree: true,
|
subtree: true,
|
||||||
attributes: true
|
attributes: true
|
||||||
@@ -932,21 +986,28 @@ function initializeNow(doc, forceReinit = false) {
|
|||||||
// Listen for hashchange as well
|
// Listen for hashchange as well
|
||||||
window.addEventListener('hashchange', () => handleNavigation('hashchange'));
|
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;
|
const originalFetch = window.fetch;
|
||||||
window.fetch = function (...args) {
|
window.fetch = function (...args) {
|
||||||
return originalFetch.apply(this, args).then(response => {
|
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
|
||||||
setTimeout(() => {
|
const now = Date.now();
|
||||||
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
if (now - lastFetchScanTime > FETCH_SCAN_THROTTLE) {
|
||||||
const videos = document.querySelectorAll(q);
|
lastFetchScanTime = now;
|
||||||
videos.forEach(video => {
|
setTimeout(() => {
|
||||||
if (!video.vsc && (video.src || video.currentSrc || video.readyState > 0)) {
|
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
||||||
log(`Post-fetch scan found video`, 5);
|
const videos = document.querySelectorAll(q);
|
||||||
checkForVideo(video, video.parentElement, true);
|
videos.forEach(video => {
|
||||||
}
|
if (!video.vsc && (video.src || video.currentSrc || video.readyState > 0)) {
|
||||||
});
|
log(`Post-fetch scan found video`, 5);
|
||||||
}, 200);
|
checkForVideo(video, video.parentElement, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -959,27 +1020,27 @@ function initializeNow(doc, forceReinit = false) {
|
|||||||
const periodicScan = () => {
|
const periodicScan = () => {
|
||||||
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
const q = tc.settings.audioBoolean ? "video,audio" : "video";
|
||||||
const allVideos = doc.querySelectorAll(q);
|
const allVideos = doc.querySelectorAll(q);
|
||||||
let foundNew = false;
|
let foundNewCount = 0;
|
||||||
|
|
||||||
allVideos.forEach(video => {
|
allVideos.forEach(video => {
|
||||||
if (!video.vsc && (video.src || video.currentSrc || video.readyState > 0)) {
|
if (!video.vsc && (video.src || video.currentSrc || video.readyState > 0)) {
|
||||||
log(`Periodic scan found missed ${video.tagName.toLowerCase()}`, 4);
|
log(`Periodic scan found missed ${video.tagName.toLowerCase()}`, 4);
|
||||||
checkForVideo(video, video.parentElement, true);
|
checkForVideo(video, video.parentElement, true);
|
||||||
foundNew = true;
|
foundNewCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (foundNew) {
|
if (foundNewCount > 0) {
|
||||||
log(`Periodic scan found ${foundNew} new videos`, 4);
|
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(() => {
|
setInterval(() => {
|
||||||
if (tc.mediaElements.length > 0 || doc.querySelector(tc.settings.audioBoolean ? "video,audio" : "video")) {
|
if (tc.mediaElements.length > 0 || doc.querySelector(tc.settings.audioBoolean ? "video,audio" : "video")) {
|
||||||
periodicScan();
|
periodicScan();
|
||||||
}
|
}
|
||||||
}, 3000);
|
}, 5000); // Increased from 3s to 5s for better performance
|
||||||
|
|
||||||
doc.vscPeriodicScanAttached = true;
|
doc.vscPeriodicScanAttached = true;
|
||||||
}
|
}
|
||||||
@@ -999,6 +1060,9 @@ function setSpeed(video, speed, isInitialCall = false, isUserKeyPress = false) {
|
|||||||
tc.settings.lastSpeed = numericSpeed;
|
tc.settings.lastSpeed = numericSpeed;
|
||||||
video.vsc.speedIndicator.textContent = numericSpeed.toFixed(2);
|
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) {
|
if (isUserKeyPress && !isInitialCall && video.vsc && video.vsc.div) {
|
||||||
runAction("blink", 1000, null, video); // Pass video to blink
|
runAction("blink", 1000, null, video); // Pass video to blink
|
||||||
}
|
}
|
||||||
@@ -1085,18 +1149,19 @@ function runAction(action, value, e) {
|
|||||||
v.currentTime += numValue;
|
v.currentTime += numValue;
|
||||||
break;
|
break;
|
||||||
case "faster":
|
case "faster":
|
||||||
setSpeed(
|
// Round to the step precision to avoid floating-point issues (e.g., 1.80 + 0.1 = 1.9000000000000001)
|
||||||
v,
|
var fasterStep = numValue;
|
||||||
Math.min(
|
var fasterPrecision = Math.round(1 / fasterStep); // e.g., 0.1 -> 10, 0.05 -> 20, 0.25 -> 4
|
||||||
(v.playbackRate < 0.07 ? 0.07 : v.playbackRate) + numValue,
|
var newFasterSpeed = (v.playbackRate < 0.07 ? 0.07 : v.playbackRate) + fasterStep;
|
||||||
16
|
newFasterSpeed = Math.round(newFasterSpeed * fasterPrecision) / fasterPrecision;
|
||||||
),
|
setSpeed(v, Math.min(newFasterSpeed, 16), false, true);
|
||||||
false,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "slower":
|
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;
|
break;
|
||||||
case "reset":
|
case "reset":
|
||||||
resetSpeed(v, 1.0, false); // Use enhanced resetSpeed
|
resetSpeed(v, 1.0, false); // Use enhanced resetSpeed
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Video Speed Controller",
|
"name": "Video Speed Controller",
|
||||||
"short_name": "videospeed",
|
"short_name": "videospeed",
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts",
|
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts",
|
||||||
"homepage_url": "https://github.com/SoPat712/videospeed",
|
"homepage_url": "https://github.com/SoPat712/videospeed",
|
||||||
|
|||||||
Reference in New Issue
Block a user