mirror of
https://github.com/SoPat712/videospeed.git
synced 2025-08-21 09:58:45 -04:00
Compare commits
6 Commits
3dfee251ec
...
v1.5.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d89853b4d2 | ||
![]() |
d94ab958d5 | ||
![]() |
1277750716 | ||
![]() |
247a46d430 | ||
![]() |
703658335c | ||
![]() |
b2ed0fcb41 |
118
build.py
Normal file
118
build.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
|
||||
SCRIPT_NAME = os.path.basename(__file__)
|
||||
TARGET_FILE = "manifest.json"
|
||||
|
||||
|
||||
def zip_folder(output_name, folder, exclude_files, exclude_dirs):
|
||||
with zipfile.ZipFile(output_name, "w", zipfile.ZIP_DEFLATED) as zipf:
|
||||
for root, dirs, files in os.walk(folder):
|
||||
dirs[:] = [
|
||||
d
|
||||
for d in dirs
|
||||
if os.path.relpath(os.path.join(root, d), folder) not in exclude_dirs
|
||||
]
|
||||
for file in files:
|
||||
rel_path = os.path.relpath(os.path.join(root, file), folder)
|
||||
if (
|
||||
file in exclude_files
|
||||
or rel_path in exclude_files
|
||||
or any(rel_path.startswith(ed + os.sep) for ed in exclude_dirs)
|
||||
):
|
||||
continue
|
||||
zipf.write(os.path.join(root, file), arcname=rel_path)
|
||||
|
||||
|
||||
def update_version_line(file_path, new_version):
|
||||
with open(file_path, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
updated = False
|
||||
for i, line in enumerate(lines):
|
||||
match = re.match(r'\s*"version":\s*"([^"]+)"', line)
|
||||
if match:
|
||||
old_version = match.group(1)
|
||||
lines[i] = re.sub(
|
||||
r'"version":\s*".+?"', f'"version": "{new_version}"', line
|
||||
)
|
||||
updated = True
|
||||
print(
|
||||
f"🛠️ Changed version in {file_path} from {old_version} ➜ {new_version}"
|
||||
)
|
||||
break
|
||||
|
||||
if updated:
|
||||
with open(file_path, "w", encoding="utf-8") as f:
|
||||
f.writelines(lines)
|
||||
else:
|
||||
print(f"⚠️ No version line found in {file_path}.")
|
||||
|
||||
|
||||
def main():
|
||||
# Step 0: Remove all existing .xpi files upfront
|
||||
xpi_files = glob.glob("*.xpi")
|
||||
for f in xpi_files:
|
||||
try:
|
||||
os.remove(f)
|
||||
print(f"🗑️ Removed existing archive: {f}")
|
||||
except Exception as e:
|
||||
print(f"⚠️ Failed to remove {f}: {e}")
|
||||
|
||||
base_version = input("Enter the new base version (e.g., 2.0.1): ").strip()
|
||||
if not base_version:
|
||||
print("❌ No version entered. Exiting.")
|
||||
return
|
||||
|
||||
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
|
||||
if os.path.exists(manifest_path):
|
||||
update_version_line(manifest_path, base_version)
|
||||
else:
|
||||
print(f"❌ {TARGET_FILE} not found. Aborting.")
|
||||
return
|
||||
|
||||
# Step 2: Create videospeed-github.xpi (exclude script, .git, AND videospeed-github.xpi itself)
|
||||
exclude_files = [SCRIPT_NAME, "videospeed-github.xpi"]
|
||||
exclude_dirs = [".git"]
|
||||
zip_folder("videospeed-github.xpi", current_dir, exclude_files, exclude_dirs)
|
||||
print("✅ Created videospeed-github.xpi")
|
||||
|
||||
# Step 3: Re-scan for .xpi files after GitHub archive creation, exclude them for Firefox zip
|
||||
current_xpi_files = set(glob.glob("*.xpi"))
|
||||
exclude_temp_files = current_xpi_files.union({SCRIPT_NAME})
|
||||
exclude_temp_dirs = set(exclude_dirs)
|
||||
|
||||
# Step 4: Create videospeed-firefox.xpi from temp folder with version bumped to .0
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
for item in os.listdir(current_dir):
|
||||
if item in exclude_temp_files or item in exclude_temp_dirs:
|
||||
continue
|
||||
src = os.path.join(current_dir, item)
|
||||
dst = os.path.join(temp_dir, item)
|
||||
if os.path.isdir(src):
|
||||
shutil.copytree(src, dst)
|
||||
else:
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
temp_manifest = os.path.join(temp_dir, TARGET_FILE)
|
||||
if os.path.exists(temp_manifest):
|
||||
update_version_line(temp_manifest, firefox_version)
|
||||
else:
|
||||
print(f"⚠️ {TARGET_FILE} not found in temp folder.")
|
||||
|
||||
zip_folder(
|
||||
"videospeed-firefox.xpi", temp_dir, exclude_files=[], exclude_dirs=[]
|
||||
)
|
||||
print("✅ Created videospeed-firefox.xpi")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
117
inject.js
117
inject.js
@@ -160,6 +160,8 @@ function defineVideoController() {
|
||||
this.video = target;
|
||||
this.parent = target.parentElement || parent;
|
||||
this.nudgeIntervalId = null;
|
||||
|
||||
// Determine what speed to use
|
||||
let storedSpeed = tc.settings.speeds[target.currentSrc];
|
||||
if (!tc.settings.rememberSpeed) {
|
||||
if (!storedSpeed) {
|
||||
@@ -171,7 +173,15 @@ function defineVideoController() {
|
||||
if (tc.settings.forceLastSavedSpeed) {
|
||||
storedSpeed = tc.settings.lastSpeed;
|
||||
}
|
||||
target.playbackRate = storedSpeed;
|
||||
|
||||
// FIXED: Actually apply the speed to the video element
|
||||
// Use setSpeed function to properly set the speed with all the necessary logic
|
||||
setTimeout(() => {
|
||||
if (this.video && this.video.vsc) {
|
||||
setSpeed(this.video, storedSpeed, true, false);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
this.div = this.initializeControls();
|
||||
|
||||
// Make the controller visible for 5 seconds on startup
|
||||
@@ -182,38 +192,45 @@ function defineVideoController() {
|
||||
// Handle subtitle nudging based on the event type first.
|
||||
if (event.type === "play") {
|
||||
this.startSubtitleNudge();
|
||||
|
||||
// FIXED: Only reapply speed if there's a significant mismatch AND it's a new video
|
||||
const currentSpeed = event.target.playbackRate;
|
||||
const videoId =
|
||||
event.target.currentSrc || event.target.src || "default";
|
||||
|
||||
// Get the expected speed based on settings
|
||||
let expectedSpeed;
|
||||
if (tc.settings.forceLastSavedSpeed) {
|
||||
expectedSpeed = tc.settings.lastSpeed;
|
||||
} else {
|
||||
expectedSpeed = tc.settings.speeds[videoId] || tc.settings.lastSpeed;
|
||||
}
|
||||
|
||||
// Only reapply speed if:
|
||||
// 1. The current speed is 1.0 (default) AND we have a stored speed that's different
|
||||
// 2. OR if forceLastSavedSpeed is enabled and speeds don't match
|
||||
const shouldReapplySpeed =
|
||||
(Math.abs(currentSpeed - 1.0) < 0.01 &&
|
||||
Math.abs(expectedSpeed - 1.0) > 0.01) ||
|
||||
(tc.settings.forceLastSavedSpeed &&
|
||||
Math.abs(currentSpeed - expectedSpeed) > 0.01);
|
||||
|
||||
if (shouldReapplySpeed) {
|
||||
setTimeout(() => {
|
||||
if (event.target.vsc) {
|
||||
setSpeed(event.target, expectedSpeed, false, false);
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
} else if (event.type === "pause" || event.type === "ended") {
|
||||
this.stopSubtitleNudge();
|
||||
// *** THE KEY FIX ***
|
||||
// On pause or end, we do NOT want to change the speed.
|
||||
// We only stop the nudger and exit the function immediately.
|
||||
return;
|
||||
}
|
||||
|
||||
// The rest of this logic will now ONLY run for "play" and "seeked" events.
|
||||
|
||||
// If it's a seek initiated by our rewind/advance keys, do nothing.
|
||||
// For seek events, don't mess with speed
|
||||
if (event.type === "seeked" && isUserSeek) {
|
||||
isUserSeek = false; // Reset the flag for the next seek.
|
||||
isUserSeek = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the speed that *should* be applied, in case the site changed it.
|
||||
let targetSpeed;
|
||||
if (tc.settings.forceLastSavedSpeed) {
|
||||
targetSpeed = tc.settings.lastSpeed;
|
||||
} else if (tc.settings.rememberSpeed) {
|
||||
targetSpeed = tc.settings.lastSpeed;
|
||||
} else {
|
||||
// Fallback to the speed saved for this specific video, or 1.0
|
||||
targetSpeed = tc.settings.speeds[event.target.currentSrc] || 1.0;
|
||||
}
|
||||
|
||||
// Only re-apply the speed if the website has actually changed it.
|
||||
// This prevents redundant "ratechange" events.
|
||||
if (Math.abs(event.target.playbackRate - targetSpeed) > 0.01) {
|
||||
setSpeed(event.target, targetSpeed);
|
||||
}
|
||||
};
|
||||
|
||||
target.addEventListener(
|
||||
@@ -232,6 +249,32 @@ function defineVideoController() {
|
||||
"seeked",
|
||||
(this.handleSeek = mediaEventAction.bind(this))
|
||||
);
|
||||
|
||||
// ADDITIONAL FIX: Listen for loadedmetadata to reapply speed when video source changes
|
||||
target.addEventListener("loadedmetadata", () => {
|
||||
if (this.video && this.video.vsc) {
|
||||
const currentSpeed = this.video.playbackRate;
|
||||
const videoId = this.video.currentSrc || this.video.src || "default";
|
||||
|
||||
// Get expected speed
|
||||
let expectedSpeed;
|
||||
if (tc.settings.forceLastSavedSpeed) {
|
||||
expectedSpeed = tc.settings.lastSpeed;
|
||||
} else {
|
||||
expectedSpeed = tc.settings.speeds[videoId] || tc.settings.lastSpeed;
|
||||
}
|
||||
|
||||
// Only reapply if current speed is default (1.0) and we have a different stored speed
|
||||
const shouldReapplySpeed =
|
||||
Math.abs(currentSpeed - 1.0) < 0.01 &&
|
||||
Math.abs(expectedSpeed - 1.0) > 0.01;
|
||||
|
||||
if (shouldReapplySpeed) {
|
||||
setSpeed(this.video, expectedSpeed, false, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var srcObserver = new MutationObserver((mutations) => {
|
||||
mutations.forEach((mutation) => {
|
||||
if (
|
||||
@@ -245,6 +288,19 @@ function defineVideoController() {
|
||||
this.div.classList.add("vsc-nosource");
|
||||
} else {
|
||||
this.div.classList.remove("vsc-nosource");
|
||||
|
||||
// FIXED: Reapply speed when source changes (like in shorts)
|
||||
const expectedSpeed = tc.settings.forceLastSavedSpeed
|
||||
? tc.settings.lastSpeed
|
||||
: tc.settings.speeds[mutation.target.currentSrc] ||
|
||||
tc.settings.lastSpeed;
|
||||
|
||||
setTimeout(() => {
|
||||
if (mutation.target.vsc) {
|
||||
setSpeed(mutation.target, expectedSpeed, false, false);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
if (!mutation.target.paused) this.startSubtitleNudge();
|
||||
}
|
||||
}
|
||||
@@ -327,8 +383,15 @@ function defineVideoController() {
|
||||
tc.videoController.prototype.initializeControls = function () {
|
||||
const doc = this.video.ownerDocument;
|
||||
const speed = this.video.playbackRate.toFixed(2);
|
||||
var top = Math.max(this.video.offsetTop, 0) + "px",
|
||||
// Fix for videos rendered after page load - use relative positioning
|
||||
var top = "10px",
|
||||
left = "10px";
|
||||
|
||||
// Try to get actual position, but fallback to default if not available
|
||||
if (this.video.offsetTop > 0 || this.video.offsetLeft > 0) {
|
||||
top = Math.max(this.video.offsetTop, 0) + "px";
|
||||
left = Math.max(this.video.offsetLeft, 0) + "px";
|
||||
}
|
||||
var wrapper = doc.createElement("div");
|
||||
wrapper.classList.add("vsc-controller");
|
||||
if (!this.video.src && !this.video.currentSrc)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "Video Speed Controller",
|
||||
"short_name": "videospeed",
|
||||
"version": "1.2.1.0",
|
||||
"version": "1.4.5",
|
||||
"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