mirror of
https://github.com/SoPat712/videospeed.git
synced 2025-08-21 09:58:45 -04:00
Compare commits
14 Commits
v1.0.0
...
1277750716
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1277750716 | ||
![]() |
247a46d430 | ||
![]() |
703658335c | ||
![]() |
b2ed0fcb41 | ||
![]() |
3dfee251ec | ||
![]() |
8e0183d8af | ||
![]() |
73827b5ee0 | ||
![]() |
b07e7cb394 | ||
![]() |
c3166cf347 | ||
![]() |
77a25c4f1e | ||
![]() |
3fee61d2b6 | ||
![]() |
b7684aad09 | ||
![]() |
43dc8b773b | ||
![]() |
2d8a4fc25f |
@@ -1,3 +1,7 @@
|
||||
[](https://addons.mozilla.org/en-US/firefox/addon/video-speed-controller-v1/)
|
||||
|
||||
|
||||
|
||||
# The science of accelerated playback
|
||||
|
||||
**TL;DR: faster playback translates to better engagement and retention.**
|
||||
@@ -74,10 +78,10 @@ You can try manually disabling Flash from the browser.
|
||||
[`igrigorik/videospeed`](https://github.com/igrigorik/videospeed) repository
|
||||
is a port of [`igrigorik`](https://github.com/igrigorik)'s videospeed Chrome
|
||||
add-on for Firefox. This fork modifies the Chrome add-on code so that it works
|
||||
in Firefox. This repo is the code behind the [Firefox Extension](https://addons.mozilla.org/en-us/firefox/addon/videospeed/)
|
||||
in Firefox. This repo is the code behind the [Firefox Extension](https://addons.mozilla.org/en-US/firefox/addon/video-speed-controller-v1/)
|
||||
whereas the [`igrigorik/videospeed`](https://github.com/igrigorik/videospeed)
|
||||
repository contains the code behind the [Chrome Extension](https://chrome.google.com/webstore/detail/video-speed-controller/nffaoalbilbmmfgbnbgppjihopabppdk).
|
||||
|
||||
### License
|
||||
|
||||
(MIT License) - Copyright (c) 2014 Ilya Grigorik
|
||||
(MIT License) - Copyright (c) 2025 Josh Patra
|
||||
|
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()
|
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "Video Speed Controller",
|
||||
"short_name": "videospeed",
|
||||
"version": "0.6.3.3",
|
||||
"version": "1.4.1",
|
||||
"manifest_version": 2,
|
||||
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts",
|
||||
"homepage_url": "https://github.com/codebicycle/videospeed",
|
||||
"homepage_url": "https://github.com/SoPat712/videospeed",
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "{7be2ba16-0f1e-4d93-9ebc-5164397477a9}"
|
||||
"id": "{ed860648-f54f-4dc9-9a0d-501aec4313f5}"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
|
64
options.html
64
options.html
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Video Speed Controller: Options</title>
|
||||
@@ -148,8 +148,13 @@
|
||||
<input id="rememberSpeed" type="checkbox" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="forceLastSavedSpeed">Force last saved speed<br />
|
||||
<em>Useful for video players that override the speeds set by VideoSpeed</em></label>
|
||||
<label for="forceLastSavedSpeed"
|
||||
>Force last saved speed<br />
|
||||
<em
|
||||
>Useful for video players that override the speeds set by
|
||||
VideoSpeed</em
|
||||
></label
|
||||
>
|
||||
<input id="forceLastSavedSpeed" type="checkbox" />
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -175,6 +180,47 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="nudgeSettings">
|
||||
<h3>Subtitle Nudge Settings (Experimental - YouTube Only)</h3>
|
||||
<div class="row">
|
||||
<label for="enableSubtitleNudge"
|
||||
>Enable Subtitle Nudge <br /><em
|
||||
>Periodically 'nudges' video speed by a tiny amount to help keep
|
||||
subtitles in sync on some sites (e.g. YouTube).</em
|
||||
>
|
||||
</label>
|
||||
<input id="enableSubtitleNudge" type="checkbox" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="subtitleNudgeInterval"
|
||||
>Nudge Interval (milliseconds) <br /><em
|
||||
>How often to nudge (e.g., 25-1000). Smaller values are more
|
||||
frequent. Default: 25.</em
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
id="subtitleNudgeInterval"
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="25"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="subtitleNudgeAmount"
|
||||
>Nudge Amount (decimal) <br /><em
|
||||
>How much to change speed by (e.g., 0.001). Very small values
|
||||
recommended. Default: 0.001.</em
|
||||
>
|
||||
</label>
|
||||
<input
|
||||
id="subtitleNudgeAmount"
|
||||
type="text"
|
||||
value=""
|
||||
placeholder="0.001"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<button id="save">Save</button>
|
||||
<button id="restore">Restore Defaults</button>
|
||||
<button id="experimental">Show Experimental Features</button>
|
||||
@@ -186,12 +232,12 @@
|
||||
|
||||
<h4>Extension controls not appearing?</h4>
|
||||
<p>
|
||||
This extension is only compatible with HTML5 audio and video. If you don't
|
||||
see the controls showing up, chances are you are viewing a Flash content.
|
||||
If you want to confirm, try right-clicking on the content and inspect the
|
||||
menu: if it mentions flash, then that's the issue. That said, <b>most sites
|
||||
will fallback to HTML5</b> if they detect that Flash is not available. You
|
||||
can try manually disabling Flash from the browser.
|
||||
This extension is only compatible with HTML5 audio and video. If you
|
||||
don't see the controls showing up, chances are you are viewing a Flash
|
||||
content. If you want to confirm, try right-clicking on the content and
|
||||
inspect the menu: if it mentions flash, then that's the issue. That
|
||||
said, <b>most sites will fallback to HTML5</b> if they detect that Flash
|
||||
is not available. You can try manually disabling Flash from the browser.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
|
278
options.js
278
options.js
@@ -22,13 +22,17 @@ var tcDefaults = {
|
||||
twitter.com
|
||||
imgur.com
|
||||
teams.microsoft.com
|
||||
`.replace(regStrip, "")
|
||||
`.replace(regStrip, ""),
|
||||
// ADDED: Nudge defaults
|
||||
enableSubtitleNudge: true,
|
||||
subtitleNudgeInterval: 25,
|
||||
subtitleNudgeAmount: 0.001
|
||||
};
|
||||
|
||||
var keyBindings = [];
|
||||
var keyBindings = []; // This is populated during save/restore
|
||||
|
||||
var keyCodeAliases = {
|
||||
0: "null",
|
||||
/* ... same as your original ... */ 0: "null",
|
||||
null: "null",
|
||||
undefined: "null",
|
||||
32: "Space",
|
||||
@@ -74,85 +78,55 @@ var keyCodeAliases = {
|
||||
220: "\\",
|
||||
221: "]",
|
||||
222: "'",
|
||||
59: ";",
|
||||
61: "+",
|
||||
173: "-",
|
||||
59: ";",
|
||||
61: "+",
|
||||
173: "-"
|
||||
};
|
||||
|
||||
function recordKeyPress(e) {
|
||||
/* ... same as your original ... */
|
||||
if (
|
||||
(e.keyCode >= 48 && e.keyCode <= 57) || // Numbers 0-9
|
||||
(e.keyCode >= 65 && e.keyCode <= 90) || // Letters A-Z
|
||||
keyCodeAliases[e.keyCode] // Other character keys
|
||||
(e.keyCode >= 48 && e.keyCode <= 57) ||
|
||||
(e.keyCode >= 65 && e.keyCode <= 90) ||
|
||||
keyCodeAliases[e.keyCode]
|
||||
) {
|
||||
e.target.value =
|
||||
keyCodeAliases[e.keyCode] || String.fromCharCode(e.keyCode);
|
||||
e.target.keyCode = e.keyCode;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
} else if (e.keyCode === 8) {
|
||||
// Clear input when backspace pressed
|
||||
e.target.value = "";
|
||||
} else if (e.keyCode === 27) {
|
||||
// When esc clicked, clear input
|
||||
e.target.value = "null";
|
||||
e.target.keyCode = null;
|
||||
}
|
||||
}
|
||||
|
||||
function inputFilterNumbersOnly(e) {
|
||||
/* ... same as your original ... */
|
||||
var char = String.fromCharCode(e.keyCode);
|
||||
if (!/[\d\.]$/.test(char) || !/^\d+(\.\d*)?$/.test(e.target.value + char)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
function inputFocus(e) {
|
||||
e.target.value = "";
|
||||
/* ... same as your original ... */ e.target.value = "";
|
||||
}
|
||||
|
||||
function inputBlur(e) {
|
||||
e.target.value =
|
||||
/* ... same as your original ... */ e.target.value =
|
||||
keyCodeAliases[e.target.keyCode] || String.fromCharCode(e.target.keyCode);
|
||||
}
|
||||
|
||||
function updateShortcutInputText(inputId, keyCode) {
|
||||
document.getElementById(inputId).value =
|
||||
keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
|
||||
document.getElementById(inputId).keyCode = keyCode;
|
||||
}
|
||||
|
||||
// function updateShortcutInputText(inputId, keyCode) { /* ... same as your original ... */ } // Not directly used in provided options.js logic flow
|
||||
function updateCustomShortcutInputText(inputItem, keyCode) {
|
||||
inputItem.value = keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
|
||||
/* ... same as your original ... */ inputItem.value =
|
||||
keyCodeAliases[keyCode] || String.fromCharCode(keyCode);
|
||||
inputItem.keyCode = keyCode;
|
||||
}
|
||||
|
||||
// List of custom actions for which customValue should be disabled
|
||||
var customActionsNoValues = ["pause", "muted", "mark", "jump", "display"];
|
||||
|
||||
var customActionsNoValues = ["pause", "muted", "mark", "jump", "display"]; // Original
|
||||
function add_shortcut() {
|
||||
var html = `<select class="customDo">
|
||||
<option value="slower">Decrease speed</option>
|
||||
<option value="faster">Increase speed</option>
|
||||
<option value="rewind">Rewind</option>
|
||||
<option value="advance">Advance</option>
|
||||
<option value="reset">Reset speed</option>
|
||||
<option value="fast">Preferred speed</option>
|
||||
<option value="muted">Mute</option>
|
||||
<option value="pause">Pause</option>
|
||||
<option value="mark">Set marker</option>
|
||||
<option value="jump">Jump to marker</option>
|
||||
<option value="display">Show/hide controller</option>
|
||||
</select>
|
||||
<input class="customKey" type="text" placeholder="press a key"/>
|
||||
<input class="customValue" type="text" placeholder="value (0.10)"/>
|
||||
<select class="customForce">
|
||||
<option value="false">Do not disable website key bindings</option>
|
||||
<option value="true">Disable website key bindings</option>
|
||||
</select>
|
||||
<button class="removeParent">X</button>`;
|
||||
/* ... same as your original ... */
|
||||
var html = `<select class="customDo"><option value="slower">Decrease speed</option><option value="faster">Increase speed</option><option value="rewind">Rewind</option><option value="advance">Advance</option><option value="reset">Reset speed</option><option value="fast">Preferred speed</option><option value="muted">Mute</option><option value="pause">Pause</option><option value="mark">Set marker</option><option value="jump">Jump to marker</option><option value="display">Show/hide controller</option></select><input class="customKey" type="text" placeholder="press a key"/><input class="customValue" type="text" placeholder="value (0.10)"/><select class="customForce"><option value="false">Do not disable website key bindings</option><option value="true">Disable website key bindings</option></select><button class="removeParent">X</button>`;
|
||||
var div = document.createElement("div");
|
||||
div.setAttribute("class", "row customs");
|
||||
div.innerHTML = html;
|
||||
@@ -162,14 +136,13 @@ function add_shortcut() {
|
||||
customs_element.children[customs_element.childElementCount - 1]
|
||||
);
|
||||
}
|
||||
|
||||
function createKeyBindings(item) {
|
||||
/* ... same as your original ... */
|
||||
const action = item.querySelector(".customDo").value;
|
||||
const key = item.querySelector(".customKey").keyCode;
|
||||
const value = Number(item.querySelector(".customValue").value);
|
||||
const force = item.querySelector(".customForce").value;
|
||||
const predefined = !!item.id; //item.id ? true : false;
|
||||
|
||||
const predefined = !!item.id;
|
||||
keyBindings.push({
|
||||
action: action,
|
||||
key: key,
|
||||
@@ -178,9 +151,8 @@ function createKeyBindings(item) {
|
||||
predefined: predefined
|
||||
});
|
||||
}
|
||||
|
||||
// Validates settings before saving
|
||||
function validate() {
|
||||
/* ... same as your original ... */
|
||||
var valid = true;
|
||||
var status = document.getElementById("status");
|
||||
document
|
||||
@@ -190,7 +162,7 @@ function validate() {
|
||||
match = match.replace(regStrip, "");
|
||||
if (match.startsWith("/")) {
|
||||
try {
|
||||
var regexp = new RegExp(match);
|
||||
new RegExp(match);
|
||||
} catch (err) {
|
||||
status.textContent =
|
||||
"Error: Invalid blacklist regex: " + match + ". Unable to save";
|
||||
@@ -202,24 +174,45 @@ function validate() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
// Saves options to chrome.storage
|
||||
// MODIFIED: save_options to include nudge settings
|
||||
function save_options() {
|
||||
if (validate() === false) {
|
||||
return;
|
||||
}
|
||||
keyBindings = [];
|
||||
if (validate() === false) return;
|
||||
|
||||
keyBindings = []; // Reset global keyBindings before populating from DOM
|
||||
Array.from(document.querySelectorAll(".customs")).forEach((item) =>
|
||||
createKeyBindings(item)
|
||||
); // Remove added shortcuts
|
||||
);
|
||||
|
||||
var rememberSpeed = document.getElementById("rememberSpeed").checked;
|
||||
var forceLastSavedSpeed = document.getElementById("forceLastSavedSpeed").checked;
|
||||
var audioBoolean = document.getElementById("audioBoolean").checked;
|
||||
var enabled = document.getElementById("enabled").checked;
|
||||
var startHidden = document.getElementById("startHidden").checked;
|
||||
var controllerOpacity = document.getElementById("controllerOpacity").value;
|
||||
var blacklist = document.getElementById("blacklist").value;
|
||||
var s = {}; // Object to hold all settings to be saved
|
||||
s.rememberSpeed = document.getElementById("rememberSpeed").checked;
|
||||
s.forceLastSavedSpeed = document.getElementById(
|
||||
"forceLastSavedSpeed"
|
||||
).checked;
|
||||
s.audioBoolean = document.getElementById("audioBoolean").checked;
|
||||
s.enabled = document.getElementById("enabled").checked;
|
||||
s.startHidden = document.getElementById("startHidden").checked;
|
||||
s.controllerOpacity = document.getElementById("controllerOpacity").value;
|
||||
s.blacklist = document
|
||||
.getElementById("blacklist")
|
||||
.value.replace(regStrip, "");
|
||||
s.keyBindings = keyBindings; // Use the populated global keyBindings
|
||||
|
||||
// ADDED: Save nudge settings
|
||||
s.enableSubtitleNudge = document.getElementById(
|
||||
"enableSubtitleNudge"
|
||||
).checked;
|
||||
s.subtitleNudgeInterval =
|
||||
parseInt(document.getElementById("subtitleNudgeInterval").value, 10) ||
|
||||
tcDefaults.subtitleNudgeInterval;
|
||||
s.subtitleNudgeAmount =
|
||||
parseFloat(document.getElementById("subtitleNudgeAmount").value) ||
|
||||
tcDefaults.subtitleNudgeAmount;
|
||||
// Basic validation for nudge interval and amount
|
||||
if (s.subtitleNudgeInterval < 10) s.subtitleNudgeInterval = 10; // Min 10ms
|
||||
if (s.subtitleNudgeAmount <= 0 || s.subtitleNudgeAmount > 0.1)
|
||||
s.subtitleNudgeAmount = tcDefaults.subtitleNudgeAmount;
|
||||
|
||||
// Remove old flat settings (original logic)
|
||||
chrome.storage.sync.remove([
|
||||
"resetSpeed",
|
||||
"speedStep",
|
||||
@@ -233,33 +226,22 @@ function save_options() {
|
||||
"advanceKeyCode",
|
||||
"fastKeyCode"
|
||||
]);
|
||||
chrome.storage.sync.set(
|
||||
{
|
||||
rememberSpeed: rememberSpeed,
|
||||
forceLastSavedSpeed: forceLastSavedSpeed,
|
||||
audioBoolean: audioBoolean,
|
||||
enabled: enabled,
|
||||
startHidden: startHidden,
|
||||
controllerOpacity: controllerOpacity,
|
||||
keyBindings: keyBindings,
|
||||
blacklist: blacklist.replace(regStrip, "")
|
||||
},
|
||||
function () {
|
||||
// Update status to let user know options were saved.
|
||||
var status = document.getElementById("status");
|
||||
status.textContent = "Options saved";
|
||||
setTimeout(function () {
|
||||
status.textContent = "";
|
||||
}, 1000);
|
||||
}
|
||||
);
|
||||
|
||||
chrome.storage.sync.set(s, function () {
|
||||
var status = document.getElementById("status");
|
||||
status.textContent = "Options saved";
|
||||
setTimeout(function () {
|
||||
status.textContent = "";
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
// Restores options from chrome.storage
|
||||
// MODIFIED: restore_options to include nudge settings
|
||||
function restore_options() {
|
||||
chrome.storage.sync.get(tcDefaults, function (storage) {
|
||||
document.getElementById("rememberSpeed").checked = storage.rememberSpeed;
|
||||
document.getElementById("forceLastSavedSpeed").checked = storage.forceLastSavedSpeed;
|
||||
document.getElementById("forceLastSavedSpeed").checked =
|
||||
storage.forceLastSavedSpeed;
|
||||
document.getElementById("audioBoolean").checked = storage.audioBoolean;
|
||||
document.getElementById("enabled").checked = storage.enabled;
|
||||
document.getElementById("startHidden").checked = storage.startHidden;
|
||||
@@ -267,65 +249,86 @@ function restore_options() {
|
||||
storage.controllerOpacity;
|
||||
document.getElementById("blacklist").value = storage.blacklist;
|
||||
|
||||
// ensure that there is a "display" binding for upgrades from versions that had it as a separate binding
|
||||
// ADDED: Restore nudge settings
|
||||
document.getElementById("enableSubtitleNudge").checked =
|
||||
storage.enableSubtitleNudge;
|
||||
document.getElementById("subtitleNudgeInterval").value =
|
||||
storage.subtitleNudgeInterval;
|
||||
document.getElementById("subtitleNudgeAmount").value =
|
||||
storage.subtitleNudgeAmount;
|
||||
|
||||
// Original key binding restoration logic
|
||||
if (
|
||||
!Array.isArray(storage.keyBindings) ||
|
||||
storage.keyBindings.length === 0
|
||||
) {
|
||||
// If keyBindings missing or not an array, use defaults from tcDefaults
|
||||
storage.keyBindings = tcDefaults.keyBindings;
|
||||
}
|
||||
if (storage.keyBindings.filter((x) => x.action == "display").length == 0) {
|
||||
storage.keyBindings.push({
|
||||
action: "display",
|
||||
value: 0,
|
||||
force: false,
|
||||
predefined: true
|
||||
predefined: true,
|
||||
key: storage.displayKeyCode || tcDefaults.displayKeyCode
|
||||
});
|
||||
}
|
||||
|
||||
// Clear existing dynamic shortcuts before restoring (if any were added by mistake)
|
||||
const dynamicShortcuts = document.querySelectorAll(".customs:not([id])");
|
||||
dynamicShortcuts.forEach((sc) => sc.remove());
|
||||
|
||||
for (let i in storage.keyBindings) {
|
||||
var item = storage.keyBindings[i];
|
||||
if (item.predefined) {
|
||||
//do predefined ones because their value needed for overlay
|
||||
// document.querySelector("#" + item["action"] + " .customDo").value = item["action"];
|
||||
if (item["action"] == "display" && typeof item["key"] === "undefined") {
|
||||
item["key"] = storage.displayKeyCode || tcDefaults.displayKeyCode; // V
|
||||
item["key"] = storage.displayKeyCode || tcDefaults.displayKeyCode;
|
||||
}
|
||||
|
||||
if (customActionsNoValues.includes(item["action"]))
|
||||
document.querySelector(
|
||||
if (customActionsNoValues.includes(item["action"])) {
|
||||
const el = document.querySelector(
|
||||
"#" + item["action"] + " .customValue"
|
||||
).disabled = true;
|
||||
|
||||
updateCustomShortcutInputText(
|
||||
document.querySelector("#" + item["action"] + " .customKey"),
|
||||
item["key"]
|
||||
);
|
||||
if (el) el.disabled = true;
|
||||
}
|
||||
const keyEl = document.querySelector(
|
||||
"#" + item["action"] + " .customKey"
|
||||
);
|
||||
document.querySelector("#" + item["action"] + " .customValue").value =
|
||||
item["value"];
|
||||
document.querySelector("#" + item["action"] + " .customForce").value =
|
||||
item["force"];
|
||||
const valEl = document.querySelector(
|
||||
"#" + item["action"] + " .customValue"
|
||||
);
|
||||
const forceEl = document.querySelector(
|
||||
"#" + item["action"] + " .customForce"
|
||||
);
|
||||
if (keyEl) updateCustomShortcutInputText(keyEl, item["key"]);
|
||||
if (valEl) valEl.value = item["value"];
|
||||
if (forceEl) forceEl.value = String(item["force"]); // Ensure string for select value
|
||||
} else {
|
||||
// new ones
|
||||
// Non-predefined, dynamically added shortcuts
|
||||
add_shortcut();
|
||||
const dom = document.querySelector(".customs:last-of-type");
|
||||
const dom = document.querySelector(".customs:last-of-type"); // Gets the newly added one
|
||||
dom.querySelector(".customDo").value = item["action"];
|
||||
|
||||
if (customActionsNoValues.includes(item["action"]))
|
||||
if (customActionsNoValues.includes(item["action"])) {
|
||||
dom.querySelector(".customValue").disabled = true;
|
||||
|
||||
}
|
||||
updateCustomShortcutInputText(
|
||||
dom.querySelector(".customKey"),
|
||||
item["key"]
|
||||
);
|
||||
dom.querySelector(".customValue").value = item["value"];
|
||||
dom.querySelector(".customForce").value = item["force"];
|
||||
dom.querySelector(".customForce").value = String(item["force"]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function restore_defaults() {
|
||||
/* ... same as your original, tcDefaults now includes nudge defaults ... */
|
||||
// Remove all dynamically added shortcuts first
|
||||
document.querySelectorAll(".customs:not([id])").forEach((el) => el.remove());
|
||||
// Then set defaults and restore options, which will re-add predefined ones correctly
|
||||
chrome.storage.sync.set(tcDefaults, function () {
|
||||
restore_options();
|
||||
document
|
||||
.querySelectorAll(".removeParent")
|
||||
.forEach((button) => button.click()); // Remove added shortcuts
|
||||
// Update status to let user know options were saved.
|
||||
restore_options(); // This will populate based on tcDefaults
|
||||
var status = document.getElementById("status");
|
||||
status.textContent = "Default options restored";
|
||||
setTimeout(function () {
|
||||
@@ -335,14 +338,15 @@ function restore_defaults() {
|
||||
}
|
||||
|
||||
function show_experimental() {
|
||||
/* ... same as your original ... */
|
||||
document
|
||||
.querySelectorAll(".customForce")
|
||||
.forEach((item) => (item.style.display = "inline-block"));
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
/* ... same as your original event listeners setup ... */
|
||||
restore_options();
|
||||
|
||||
document.getElementById("save").addEventListener("click", save_options);
|
||||
document.getElementById("add").addEventListener("click", add_shortcut);
|
||||
document
|
||||
@@ -353,34 +357,32 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
.addEventListener("click", show_experimental);
|
||||
|
||||
function eventCaller(event, className, funcName) {
|
||||
if (!event.target.classList || !event.target.classList.contains(className)) {
|
||||
if (!event.target.classList || !event.target.classList.contains(className))
|
||||
return;
|
||||
}
|
||||
funcName(event);
|
||||
}
|
||||
|
||||
document.addEventListener("keypress", (event) => {
|
||||
eventCaller(event, "customValue", inputFilterNumbersOnly);
|
||||
});
|
||||
document.addEventListener("focus", (event) => {
|
||||
eventCaller(event, "customKey", inputFocus);
|
||||
});
|
||||
document.addEventListener("blur", (event) => {
|
||||
eventCaller(event, "customKey", inputBlur);
|
||||
});
|
||||
document.addEventListener("keydown", (event) => {
|
||||
eventCaller(event, "customKey", recordKeyPress);
|
||||
});
|
||||
document.addEventListener("click", (event) => {
|
||||
document.addEventListener("keypress", (event) =>
|
||||
eventCaller(event, "customValue", inputFilterNumbersOnly)
|
||||
);
|
||||
document.addEventListener("focus", (event) =>
|
||||
eventCaller(event, "customKey", inputFocus)
|
||||
);
|
||||
document.addEventListener("blur", (event) =>
|
||||
eventCaller(event, "customKey", inputBlur)
|
||||
);
|
||||
document.addEventListener("keydown", (event) =>
|
||||
eventCaller(event, "customKey", recordKeyPress)
|
||||
);
|
||||
document.addEventListener("click", (event) =>
|
||||
eventCaller(event, "removeParent", function () {
|
||||
event.target.parentNode.remove();
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
document.addEventListener("change", (event) => {
|
||||
eventCaller(event, "customDo", function () {
|
||||
if (customActionsNoValues.includes(event.target.value)) {
|
||||
event.target.nextElementSibling.nextElementSibling.disabled = true;
|
||||
event.target.nextElementSibling.nextElementSibling.value = 0;
|
||||
event.target.nextElementSibling.nextElementSibling.value = 0; // Or "" if placeholder is preferred
|
||||
} else {
|
||||
event.target.nextElementSibling.nextElementSibling.disabled = false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user