Compare commits

...

3 Commits

Author SHA1 Message Date
joshpatra 3f5983685f v5.2.0-beta.1 2026-04-07 15:38:54 -04:00
joshpatra 41d89e0993 Bump version to 5.2.0 2026-04-07 15:38:53 -04:00
joshpatra a424dea5ca feat: add optional volume shortcuts and control bar actions 2026-04-07 15:38:03 -04:00
5 changed files with 115 additions and 20 deletions
+56 -3
View File
@@ -130,11 +130,14 @@ var controllerButtonDefs = {
display: { label: "", className: "hideButton" },
reset: { label: "\u21BB", className: "" },
fast: { label: "", className: "" },
settings: { label: "", className: "" },
nudge: { label: "", className: "" },
pause: { label: "", className: "" },
muted: { label: "", className: "" },
louder: { label: "", className: "" },
softer: { label: "", className: "" },
mark: { label: "", className: "" },
jump: { label: "", className: "" }
jump: { label: "", className: "" },
settings: { label: "", className: "" }
};
function createDefaultBinding(action, code, value) {
@@ -2586,6 +2589,8 @@ function runAction(action, value, e) {
"advance",
"faster",
"slower",
"louder",
"softer",
"reset",
"fast",
"move",
@@ -2708,6 +2713,12 @@ function runAction(action, value, e) {
case "muted":
muted(v);
break;
case "louder":
volumeUp(v, Number.isFinite(numValue) ? numValue : 0.1);
break;
case "softer":
volumeDown(v, Number.isFinite(numValue) ? numValue : 0.1);
break;
case "mark":
setMark(v);
break;
@@ -2772,7 +2783,49 @@ function resetSpeed(v, target, isFastKey = false) {
}
function muted(v) {
v.muted = !v.muted;
var nextMuted = !v.muted;
v.muted = nextMuted;
if (!isOnYouTube()) return;
var ytApi = getYouTubePlayerApi(v);
if (!ytApi) return;
if (nextMuted && typeof ytApi.mute === "function") ytApi.mute();
if (!nextMuted && typeof ytApi.unMute === "function") ytApi.unMute();
}
function getYouTubePlayerApi(video) {
if (!isOnYouTube()) return null;
var playerEl =
(video && video.closest ? video.closest(".html5-video-player") : null) ||
document.getElementById("movie_player") ||
document.querySelector(".html5-video-player");
if (!playerEl) return null;
return playerEl.wrappedJSObject || playerEl;
}
function syncYouTubePlayerVolume(video, volume) {
var ytApi = getYouTubePlayerApi(video);
if (!ytApi || typeof ytApi.setVolume !== "function") return;
ytApi.setVolume(Math.round(volume * 100));
if (volume > 0 && typeof ytApi.unMute === "function") {
ytApi.unMute();
}
}
function setVideoVolume(video, targetVolume) {
var nextVolume = Math.max(0, Math.min(1, Number(targetVolume.toFixed(2))));
video.volume = nextVolume;
if (nextVolume > 0 && video.muted) {
video.muted = false;
}
syncYouTubePlayerVolume(video, nextVolume);
}
function volumeUp(v, value) {
setVideoVolume(v, v.volume + value);
}
function volumeDown(v, value) {
setVideoVolume(v, v.volume - value);
}
function setMark(v) {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Speeder",
"short_name": "Speeder",
"version": "5.1.9.0",
"version": "5.2.0",
"manifest_version": 2,
"description": "Speed up, slow down, advance and rewind HTML5 audio/video with shortcuts (New and improved version of \"Video Speed Controller\")",
"homepage_url": "https://github.com/SoPat712/speeder",
+50 -14
View File
@@ -74,11 +74,13 @@ var controllerButtonDefs = {
reset: { icon: "\u21BB", name: "Reset speed" },
fast: { icon: "\u2605", name: "Preferred speed" },
nudge: { icon: "\u2713", name: "Subtitle nudge" },
settings: { icon: "\u2699", name: "Settings" },
pause: { icon: "\u23EF", name: "Pause / Play" },
pause: { icon: "\u23EF", name: "Play / Pause" },
muted: { icon: "M", name: "Mute / Unmute" },
louder: { icon: "+", name: "Increase volume" },
softer: { icon: "\u2212", name: "Decrease volume" },
mark: { icon: "\u2691", name: "Set marker" },
jump: { icon: "\u21E5", name: "Jump to marker" }
jump: { icon: "\u21E5", name: "Jump to marker" },
settings: { icon: "\u2699", name: "Settings" },
};
var popupExcludedButtonIds = new Set(["settings"]);
@@ -221,14 +223,16 @@ const actionLabels = {
advance: "Advance",
reset: "Reset speed",
fast: "Preferred speed",
muted: "Mute",
pause: "Pause",
toggleSubtitleNudge: "Toggle subtitle nudge",
pause: "Play / Pause",
muted: "Mute / Unmute",
louder: "Increase volume",
softer: "Decrease volume",
mark: "Set marker",
jump: "Jump to marker",
toggleSubtitleNudge: "Toggle subtitle nudge"
jump: "Jump to marker"
};
const speedBindingActions = ["slower", "faster", "fast"];
const speedBindingActions = ["slower", "faster", "fast", "softer", "louder"];
const requiredShortcutActions = new Set(["display", "slower", "faster"]);
function formatSpeedBindingDisplay(action, value) {
@@ -242,6 +246,30 @@ function formatSpeedBindingDisplay(action, value) {
return n.toFixed(2);
}
function getDefaultShortcutValue(action) {
if (action === "louder" || action === "softer") {
return 0.1;
}
var defaultBinding = tcDefaults.keyBindings.find(function (binding) {
return binding.action === action;
});
if (defaultBinding && Number.isFinite(Number(defaultBinding.value))) {
return Number(defaultBinding.value);
}
return 0;
}
function resolveShortcutValue(action, value) {
if (value === undefined || value === null) {
return getDefaultShortcutValue(action);
}
var numericValue = Number(value);
if (Number.isFinite(numericValue)) {
return numericValue;
}
return 0;
}
const customActionsNoValues = [
"reset",
"display",
@@ -581,7 +609,10 @@ function add_shortcut(action, value) {
valueInput.value = "N/A";
valueInput.disabled = true;
} else {
valueInput.value = formatSpeedBindingDisplay(action, value || 0);
valueInput.value = formatSpeedBindingDisplay(
action,
resolveShortcutValue(action, value)
);
}
var removeButton = document.createElement("button");
@@ -927,11 +958,13 @@ function addSiteRuleShortcut(container, action, binding, value, force) {
advance: "Advance",
reset: "Reset speed",
fast: "Preferred speed",
muted: "Mute",
pause: "Pause",
toggleSubtitleNudge: "Toggle subtitle nudge",
pause: "Play / Pause",
muted: "Mute / Unmute",
louder: "Increase volume",
softer: "Decrease volume",
mark: "Set marker",
jump: "Jump to marker",
toggleSubtitleNudge: "Toggle subtitle nudge"
jump: "Jump to marker"
};
var actionLabelText = actionLabels[action] || action;
if (action === "toggleSubtitleNudge") {
@@ -959,7 +992,10 @@ function addSiteRuleShortcut(container, action, binding, value, force) {
valueInput.value = "N/A";
valueInput.disabled = true;
} else {
valueInput.value = formatSpeedBindingDisplay(action, value || 0);
valueInput.value = formatSpeedBindingDisplay(
action,
resolveShortcutValue(action, value)
);
}
var forceLabel = document.createElement("label");
+4 -2
View File
@@ -14,11 +14,13 @@ document.addEventListener("DOMContentLoaded", function () {
reset: { label: "\u21BB", className: "" },
fast: { label: "", className: "" },
nudge: { label: "", className: "" },
settings: { label: "", className: "" },
pause: { label: "", className: "" },
muted: { label: "", className: "" },
louder: { label: "", className: "" },
softer: { label: "", className: "" },
mark: { label: "", className: "" },
jump: { label: "", className: "" }
jump: { label: "", className: "" },
settings: { label: "", className: "" }
};
var defaultButtons = ["rewind", "slower", "faster", "advance", "display"];
+4
View File
@@ -16,6 +16,10 @@ var vscUiIconPaths = {
slower: '<line x1="5" y1="12" x2="19" y2="12"/>',
faster:
'<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>',
softer:
'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="16" y1="12" x2="22" y2="12"/>',
louder:
'<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="17" y1="9" x2="17" y2="15"/><line x1="14" y1="12" x2="20" y2="12"/>',
moreHorizontal:
'<circle cx="6" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="18" cy="12" r="1.5"/>',
chevronUp: '<path d="m18 15-6-6-6 6"/>',