chore(release): release v5.3.0

This commit is contained in:
joshpatra committed 2026-05-24 23:34:23 -04:00
1 parent 7e1678ec4b
commit e3895c10ef
9 files changed
+125 -4

No files matched your search

+12 -2
View File
@@ -39,6 +39,7 @@ var tc = {
defaultLogLevel: 3,
logLevel: 3,
enableSubtitleNudge: false,
subtitleNudgeEnabledByDefault: true,
subtitleNudgeInterval: 50, // Default 50ms balances subtitle tracking with CPU cost
subtitleNudgeAmount: 0.001,
customButtonIcons: {}
@@ -384,6 +385,7 @@ function captureSiteRuleBase() {
controllerMarginTop: tc.settings.controllerMarginTop,
controllerMarginBottom: tc.settings.controllerMarginBottom,
enableSubtitleNudge: tc.settings.enableSubtitleNudge,
subtitleNudgeEnabledByDefault: tc.settings.subtitleNudgeEnabledByDefault,
subtitleNudgeInterval: tc.settings.subtitleNudgeInterval,
controllerButtons: Array.isArray(tc.settings.controllerButtons)
? tc.settings.controllerButtons.slice()
@@ -410,6 +412,7 @@ function resetSettingsFromSiteRuleBase() {
tc.settings.controllerMarginTop = base.controllerMarginTop;
tc.settings.controllerMarginBottom = base.controllerMarginBottom;
tc.settings.enableSubtitleNudge = base.enableSubtitleNudge;
tc.settings.subtitleNudgeEnabledByDefault = base.subtitleNudgeEnabledByDefault;
tc.settings.subtitleNudgeInterval = base.subtitleNudgeInterval;
tc.settings.controllerButtons = Array.isArray(base.controllerButtons)
? base.controllerButtons.slice()
@@ -659,13 +662,15 @@ function isSubtitleNudgeAvailableForVideo(video) {
function isSubtitleNudgeEnabledForVideo(video) {
if (!isSubtitleNudgeAvailableForVideo(video)) return false;
if (!video || !video.vsc) return true;
if (!video || !video.vsc) {
return Boolean(tc.settings.subtitleNudgeEnabledByDefault);
}
if (typeof video.vsc.subtitleNudgeEnabledOverride === "boolean") {
return video.vsc.subtitleNudgeEnabledOverride;
}
return true;
return Boolean(tc.settings.subtitleNudgeEnabledByDefault);
}
function setSubtitleNudgeEnabledForVideo(video, enabled) {
@@ -1195,6 +1200,10 @@ chrome.storage.sync.get(tc.settings, function(storage) {
typeof storage.enableSubtitleNudge !== "undefined"
? Boolean(storage.enableSubtitleNudge)
: tc.settings.enableSubtitleNudge;
tc.settings.subtitleNudgeEnabledByDefault =
typeof storage.subtitleNudgeEnabledByDefault !== "undefined"
? Boolean(storage.subtitleNudgeEnabledByDefault)
: tc.settings.subtitleNudgeEnabledByDefault;
tc.settings.subtitleNudgeInterval = Math.min(
1000,
Math.max(10, Number(storage.subtitleNudgeInterval) || 50)
@@ -2051,6 +2060,7 @@ function applySiteRuleOverrides() {
"controllerMarginTop",
"controllerMarginBottom",
"enableSubtitleNudge",
"subtitleNudgeEnabledByDefault",
"subtitleNudgeInterval"
];
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "Speeder",
"short_name": "Speeder",
"version": "5.2.8",
"version": "5.3.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",
+20 -1
View File
@@ -394,11 +394,21 @@
<div class="row row-checkbox">
<label for="enableSubtitleNudge"
>Enable subtitle nudge<br /><em
>Makes tiny playback changes to help keep subtitles aligned.</em
>Makes tiny playback changes to help keep subtitles aligned and
allows nudge toggle controls.</em
>
</label>
<input id="enableSubtitleNudge" type="checkbox" />
</div>
<div class="row row-checkbox">
<label for="subtitleNudgeEnabledByDefault"
>Start subtitle nudge enabled<br /><em
>When off, each video starts with nudging disabled until you
toggle it.</em
>
</label>
<input id="subtitleNudgeEnabledByDefault" type="checkbox" />
</div>
<div class="row">
<label for="subtitleNudgeInterval"
>Nudge interval (milliseconds)<br /><em
@@ -827,6 +837,15 @@
>
<input type="checkbox" class="site-enableSubtitleNudge" />
</div>
<div class="site-rule-option site-rule-option-checkbox">
<label
>Start subtitle nudge enabled:<br /><em
>When off, matching videos start with nudging disabled
until you toggle it.</em
></label
>
<input type="checkbox" class="site-subtitleNudgeEnabledByDefault" />
</div>
<div class="site-rule-option site-rule-option-field">
<label
>Nudge interval (10&ndash;1000ms):<br /><em
+9
View File
@@ -210,6 +210,7 @@ var tcDefaults = {
popupMatchHoverControls: true,
popupControllerButtons: ["rewind", "slower", "faster", "advance", "display"],
enableSubtitleNudge: false,
subtitleNudgeEnabledByDefault: true,
subtitleNudgeInterval: 50,
subtitleNudgeAmount: 0.001
};
@@ -823,6 +824,8 @@ function save_options() {
settings.keyBindings = keyBindings;
settings.enableSubtitleNudge =
document.getElementById("enableSubtitleNudge").checked;
settings.subtitleNudgeEnabledByDefault =
document.getElementById("subtitleNudgeEnabledByDefault").checked;
settings.subtitleNudgeInterval =
parseInt(document.getElementById("subtitleNudgeInterval").value, 10) ||
tcDefaults.subtitleNudgeInterval;
@@ -906,6 +909,8 @@ function save_options() {
if (ruleEl.querySelector(".override-subtitleNudge").checked) {
rule.enableSubtitleNudge =
ruleEl.querySelector(".site-enableSubtitleNudge").checked;
rule.subtitleNudgeEnabledByDefault =
ruleEl.querySelector(".site-subtitleNudgeEnabledByDefault").checked;
var nudgeIv = parseInt(
ruleEl.querySelector(".site-subtitleNudgeInterval").value,
10
@@ -1162,10 +1167,12 @@ function createSiteRule(rule) {
var hasSubtitleNudgeOverride = Boolean(
rule &&
(rule.enableSubtitleNudge !== undefined ||
rule.subtitleNudgeEnabledByDefault !== undefined ||
rule.subtitleNudgeInterval !== undefined)
);
ruleEl.querySelector(".override-subtitleNudge").checked = hasSubtitleNudgeOverride;
syncSiteRuleField(ruleEl, rule, "enableSubtitleNudge", true);
syncSiteRuleField(ruleEl, rule, "subtitleNudgeEnabledByDefault", true);
syncSiteRuleField(ruleEl, rule, "subtitleNudgeInterval", false);
applySiteRuleOverrideState(
ruleEl,
@@ -1650,6 +1657,8 @@ function restore_options() {
storage.showPopupControlBar !== false;
document.getElementById("enableSubtitleNudge").checked =
storage.enableSubtitleNudge;
document.getElementById("subtitleNudgeEnabledByDefault").checked =
storage.subtitleNudgeEnabledByDefault;
document.getElementById("subtitleNudgeInterval").value =
storage.subtitleNudgeInterval;
+3
View File
@@ -15,6 +15,7 @@
"audioBoolean",
"controllerOpacity",
"enableSubtitleNudge",
"subtitleNudgeEnabledByDefault",
"subtitleNudgeInterval",
"controllerButtons",
"showPopupControlBar",
@@ -43,6 +44,7 @@
"popupMatchHoverControls",
"popupControllerButtons",
"enableSubtitleNudge",
"subtitleNudgeEnabledByDefault",
"subtitleNudgeInterval",
"subtitleNudgeAmount"
];
@@ -180,6 +182,7 @@
popupMatchHoverControls: true,
popupControllerButtons: DEFAULT_BUTTONS.slice(),
enableSubtitleNudge: false,
subtitleNudgeEnabledByDefault: true,
subtitleNudgeInterval: 50,
subtitleNudgeAmount: 0.001
};
+1
View File
@@ -33,6 +33,7 @@
"speed",
"startHidden",
"subtitleNudgeAmount",
"subtitleNudgeEnabledByDefault",
"subtitleNudgeInterval"
]);
+48
View File
@@ -112,4 +112,52 @@ describe("inject runtime", () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
it("uses the configured subtitle nudge default until a video is toggled", async () => {
await bootInject({
sync: {
enableSubtitleNudge: true,
subtitleNudgeEnabledByDefault: false
}
});
const startSubtitleNudge = vi.fn();
const video = {
paused: false,
playbackRate: 1.5,
vsc: {
startSubtitleNudge,
stopSubtitleNudge: vi.fn(),
subtitleNudgeEnabledOverride: null,
subtitleNudgeIndicator: null,
nudgeFlashIndicator: document.createElement("span")
}
};
expect(window.isSubtitleNudgeEnabledForVideo(video)).toBe(false);
expect(window.setSubtitleNudgeEnabledForVideo(video, true)).toBe(true);
expect(video.vsc.subtitleNudgeEnabledOverride).toBe(true);
expect(window.isSubtitleNudgeEnabledForVideo(video)).toBe(true);
expect(startSubtitleNudge).toHaveBeenCalledTimes(1);
});
it("applies subtitle nudge default state from matching site rules", async () => {
await bootInject();
window.tc.settings.enableSubtitleNudge = true;
window.tc.settings.subtitleNudgeEnabledByDefault = true;
window.tc.settings.siteRules = [
{
pattern: "example.org",
subtitleNudgeEnabledByDefault: false
}
];
window.captureSiteRuleBase();
expect(window.applySiteRuleOverrides()).toBe(false);
expect(window.tc.settings.subtitleNudgeEnabledByDefault).toBe(false);
window.resetSettingsFromSiteRuleBase();
expect(window.tc.settings.subtitleNudgeEnabledByDefault).toBe(true);
});
});
+21
View File
@@ -29,6 +29,7 @@ describe("options page", () => {
sync: {
rememberSpeed: true,
enabled: false,
subtitleNudgeEnabledByDefault: false,
popupMatchHoverControls: false,
popupControllerButtons: ["rewind", "settings", "advance", "advance"],
keyBindings: [
@@ -39,6 +40,7 @@ describe("options page", () => {
{
pattern: "youtube.com",
enabled: true,
subtitleNudgeEnabledByDefault: false,
showPopupControlBar: false,
popupControllerButtons: ["advance", "settings", "advance"]
}
@@ -49,12 +51,22 @@ describe("options page", () => {
expect(document.getElementById("app-version").textContent).toBe("5.1.7.0");
expect(document.getElementById("rememberSpeed").checked).toBe(true);
expect(document.getElementById("enabled").checked).toBe(false);
expect(document.getElementById("subtitleNudgeEnabledByDefault").checked).toBe(
false
);
expect(document.querySelector('.shortcut-row[data-action="pause"]')).not.toBe(
null
);
expect(document.getElementById("siteRulesContainer").children.length).toBe(
1
);
expect(document.querySelector(".site-rule .override-subtitleNudge").checked).toBe(
true
);
expect(
document.querySelector(".site-rule .site-subtitleNudgeEnabledByDefault")
.checked
).toBe(false);
expect(globalThis.getPopupControlBarOrder()).toEqual(["rewind", "advance"]);
});
@@ -159,6 +171,7 @@ describe("options page", () => {
document.getElementById("controllerMarginTop").value = "250";
document.getElementById("controllerMarginBottom").value = "-4";
document.getElementById("enableSubtitleNudge").checked = true;
document.getElementById("subtitleNudgeEnabledByDefault").checked = false;
document.getElementById("subtitleNudgeInterval").value = "5";
document.getElementById("popupMatchHoverControls").checked = false;
document.getElementById("showPopupControlBar").checked = false;
@@ -177,6 +190,10 @@ describe("options page", () => {
rule.querySelector(".site-rememberSpeed").checked = true;
rule.querySelector(".override-opacity").checked = true;
rule.querySelector(".site-controllerOpacity").value = "0";
rule.querySelector(".override-subtitleNudge").checked = true;
rule.querySelector(".site-enableSubtitleNudge").checked = true;
rule.querySelector(".site-subtitleNudgeEnabledByDefault").checked = false;
rule.querySelector(".site-subtitleNudgeInterval").value = "75";
rule.querySelector(".override-popup-controlbar").checked = true;
rule.querySelector(".site-showPopupControlBar").checked = false;
globalThis.populateControlBarZones(
@@ -201,6 +218,7 @@ describe("options page", () => {
expect(savedSettings.controllerOpacity).toBe(0);
expect(savedSettings.controllerMarginTop).toBe(200);
expect(savedSettings.controllerMarginBottom).toBe(0);
expect(savedSettings.subtitleNudgeEnabledByDefault).toBe(false);
expect(savedSettings.subtitleNudgeInterval).toBe(10);
expect(savedSettings.showPopupControlBar).toBe(false);
expect(savedSettings.popupMatchHoverControls).toBe(false);
@@ -211,6 +229,9 @@ describe("options page", () => {
pattern: "youtube.com",
rememberSpeed: true,
controllerOpacity: 0,
enableSubtitleNudge: true,
subtitleNudgeEnabledByDefault: false,
subtitleNudgeInterval: 75,
showPopupControlBar: false,
popupControllerButtons: ["advance"]
})
+10
View File
@@ -152,6 +152,16 @@ describe("shared helpers", () => {
localSettings: null
});
expect(
importExportUtils.extractImportSettings({
subtitleNudgeEnabledByDefault: false
})
).toEqual({
isWrappedBackup: false,
settings: { subtitleNudgeEnabledByDefault: false },
localSettings: null
});
expect(
importExportUtils.extractImportSettings({ enabled: true })
).toEqual({