mirror of
https://github.com/SoPat712/videospeed.git
synced 2026-04-26 22:23:09 -04:00
feat: hide with controls timer for non-youtube websites, site-specific rules for it too
This commit is contained in:
+4
-2
@@ -24,14 +24,16 @@
|
||||
|
||||
/* YouTube auto-hide feature: fade controller with YouTube's controls */
|
||||
/* When the wrapper has ytp-autohide class, hide it (unless vsc-hidden overrides) */
|
||||
.vsc-controller.ytp-autohide:not(.vsc-hidden) {
|
||||
.vsc-controller.ytp-autohide:not(.vsc-hidden),
|
||||
.vsc-controller.vsc-idle-hidden:not(.vsc-hidden) {
|
||||
visibility: hidden;
|
||||
transition: opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Show it temporarily when it has vsc-show class */
|
||||
.vsc-controller.ytp-autohide.vsc-show:not(.vsc-hidden) {
|
||||
.vsc-controller.ytp-autohide.vsc-show:not(.vsc-hidden),
|
||||
.vsc-controller.vsc-idle-hidden.vsc-show:not(.vsc-hidden) {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ var tc = {
|
||||
audioBoolean: false,
|
||||
startHidden: false,
|
||||
hideWithYouTubeControls: false,
|
||||
hideWithControls: false,
|
||||
hideWithControlsTimer: 2.0,
|
||||
controllerLocation: "top-left",
|
||||
controllerOpacity: 0.3,
|
||||
keyBindings: [],
|
||||
@@ -952,7 +954,13 @@ chrome.storage.sync.get(tc.settings, function (storage) {
|
||||
tc.settings.audioBoolean = Boolean(storage.audioBoolean);
|
||||
tc.settings.enabled = Boolean(storage.enabled);
|
||||
tc.settings.startHidden = Boolean(storage.startHidden);
|
||||
tc.settings.hideWithYouTubeControls = Boolean(storage.hideWithYouTubeControls);
|
||||
tc.settings.hideWithControls =
|
||||
typeof storage.hideWithControls !== "undefined"
|
||||
? Boolean(storage.hideWithControls)
|
||||
: Boolean(storage.hideWithYouTubeControls);
|
||||
tc.settings.hideWithControlsTimer =
|
||||
Math.min(15, Math.max(0.1, Number(storage.hideWithControlsTimer) || 2.0));
|
||||
tc.settings.hideWithYouTubeControls = tc.settings.hideWithControls;
|
||||
tc.settings.controllerLocation = normalizeControllerLocation(
|
||||
storage.controllerLocation
|
||||
);
|
||||
@@ -1195,6 +1203,10 @@ function defineVideoController() {
|
||||
this.youTubeAutoHideObserver.disconnect();
|
||||
this.youTubeAutoHideObserver = null;
|
||||
}
|
||||
if (this.genericAutoHideCleanup) {
|
||||
this.genericAutoHideCleanup();
|
||||
this.genericAutoHideCleanup = null;
|
||||
}
|
||||
if (this.div) this.div.remove();
|
||||
if (this.restoreSpeedTimer) clearTimeout(this.restoreSpeedTimer);
|
||||
if (this.video) {
|
||||
@@ -1374,6 +1386,57 @@ function defineVideoController() {
|
||||
log("YouTube auto-hide observer setup complete", 4);
|
||||
};
|
||||
|
||||
tc.videoController.prototype.setupGenericAutoHide = function (wrapper) {
|
||||
if (!wrapper) return;
|
||||
|
||||
const video = this.video;
|
||||
let timer = null;
|
||||
|
||||
const resetTimer = () => {
|
||||
wrapper.classList.remove("vsc-idle-hidden");
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
// Only hide if the video is not paused
|
||||
// (Many players keep controls visible while paused)
|
||||
// However, the user said "Reveal on every mouse and keyboard input"
|
||||
// and "auto-hidden after timespan".
|
||||
// We'll follow the timer strictly.
|
||||
wrapper.classList.add("vsc-idle-hidden");
|
||||
log("Generic hide: controller hidden due to inactivity", 5);
|
||||
}, tc.settings.hideWithControlsTimer * 1000);
|
||||
};
|
||||
|
||||
// Initial show/timer
|
||||
resetTimer();
|
||||
|
||||
// The wrapper covers the player area on most sites due to inject.css styles,
|
||||
// but we listen on both the video and the wrapper for maximum coverage.
|
||||
const activityEvents = ["mousemove", "mousedown", "keydown", "touchstart"];
|
||||
activityEvents.forEach((type) => {
|
||||
video.addEventListener(type, resetTimer, { passive: true });
|
||||
wrapper.addEventListener(type, resetTimer, { passive: true });
|
||||
});
|
||||
|
||||
// Also reset timer on play/pause events to ensure sync when player state changes
|
||||
video.addEventListener("play", resetTimer, { passive: true });
|
||||
video.addEventListener("pause", resetTimer, { passive: true });
|
||||
|
||||
// Store a cleanup function
|
||||
this.genericAutoHideCleanup = () => {
|
||||
if (timer) clearTimeout(timer);
|
||||
activityEvents.forEach((type) => {
|
||||
video.removeEventListener(type, resetTimer);
|
||||
wrapper.removeEventListener(type, resetTimer);
|
||||
});
|
||||
video.removeEventListener("play", resetTimer);
|
||||
video.removeEventListener("pause", resetTimer);
|
||||
};
|
||||
|
||||
log(`Generic auto-hide setup complete with ${tc.settings.hideWithControlsTimer}s timer`, 4);
|
||||
};
|
||||
|
||||
tc.videoController.prototype.initializeControls = function () {
|
||||
const doc = this.video.ownerDocument;
|
||||
const speed = this.video.playbackRate.toFixed(2);
|
||||
@@ -1476,9 +1539,13 @@ function defineVideoController() {
|
||||
controller.addEventListener("click", (e) => e.stopPropagation(), false);
|
||||
controller.addEventListener("mousedown", (e) => e.stopPropagation(), false);
|
||||
|
||||
// Setup YouTube auto-hide observer if enabled
|
||||
if (tc.settings.hideWithYouTubeControls && isOnYouTube()) {
|
||||
this.setupYouTubeAutoHide(wrapper);
|
||||
// Setup auto-hide observers if enabled
|
||||
if (tc.settings.hideWithControls) {
|
||||
if (isOnYouTube()) {
|
||||
this.setupYouTubeAutoHide(wrapper);
|
||||
} else {
|
||||
this.setupGenericAutoHide(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
var fragment = doc.createDocumentFragment();
|
||||
@@ -1607,7 +1674,9 @@ function applySiteRuleOverrides() {
|
||||
"rememberSpeed",
|
||||
"forceLastSavedSpeed",
|
||||
"audioBoolean",
|
||||
"controllerOpacity"
|
||||
"controllerOpacity",
|
||||
"hideWithControls",
|
||||
"hideWithControlsTimer"
|
||||
];
|
||||
|
||||
siteSettings.forEach((key) => {
|
||||
|
||||
+19
-3
@@ -141,10 +141,16 @@
|
||||
<input id="startHidden" type="checkbox" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="hideWithYouTubeControls">Hide with controls (YouTube)<br />
|
||||
<em>Fade controller in/out with YouTube's video interface</em>
|
||||
<label for="hideWithControls">Hide with controls (All sites)<br />
|
||||
<em>Fade controller in/out with video interface (perfect sync on YouTube; idle-based elsewhere)</em>
|
||||
</label>
|
||||
<input id="hideWithYouTubeControls" type="checkbox" />
|
||||
<input id="hideWithControls" type="checkbox" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="hideWithControlsTimer">Auto-hide timer (seconds)<br />
|
||||
<em>Seconds of inactivity before hiding (0.1 - 15). Used for non-YouTube sites.</em>
|
||||
</label>
|
||||
<input id="hideWithControlsTimer" type="text" placeholder="2" />
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="controllerLocation">Default controller location</label>
|
||||
@@ -248,6 +254,16 @@
|
||||
<label>Controller opacity:</label>
|
||||
<input type="text" class="site-controllerOpacity" />
|
||||
</div>
|
||||
<div class="site-rule-option">
|
||||
<label>
|
||||
<input type="checkbox" class="site-hideWithControls" />
|
||||
Hide with controls (idle-based)
|
||||
</label>
|
||||
</div>
|
||||
<div class="site-rule-option">
|
||||
<label>Auto-hide timer (0.1 - 15s):</label>
|
||||
<input type="text" class="site-hideWithControlsTimer" />
|
||||
</div>
|
||||
<div class="site-rule-shortcuts">
|
||||
<label>
|
||||
<input type="checkbox" class="override-shortcuts" />
|
||||
|
||||
+37
-8
@@ -151,6 +151,8 @@ var tcDefaults = {
|
||||
audioBoolean: false,
|
||||
startHidden: false,
|
||||
hideWithYouTubeControls: false,
|
||||
hideWithControls: false,
|
||||
hideWithControlsTimer: 2.0,
|
||||
controllerLocation: "top-left",
|
||||
forceLastSavedSpeed: false,
|
||||
enabled: true,
|
||||
@@ -575,7 +577,16 @@ function save_options() {
|
||||
settings.audioBoolean = document.getElementById("audioBoolean").checked;
|
||||
settings.enabled = document.getElementById("enabled").checked;
|
||||
settings.startHidden = document.getElementById("startHidden").checked;
|
||||
settings.hideWithYouTubeControls = document.getElementById("hideWithYouTubeControls").checked;
|
||||
settings.hideWithControls = document.getElementById("hideWithControls").checked;
|
||||
settings.hideWithControlsTimer =
|
||||
Math.min(15, Math.max(0.1, parseFloat(document.getElementById("hideWithControlsTimer").value) || tcDefaults.hideWithControlsTimer));
|
||||
|
||||
// Sync back to the legacy key if it exists, for backward compatibility
|
||||
settings.hideWithYouTubeControls = settings.hideWithControls;
|
||||
|
||||
if (settings.hideWithControlsTimer < 0.1) settings.hideWithControlsTimer = 0.1;
|
||||
if (settings.hideWithControlsTimer > 15) settings.hideWithControlsTimer = 15;
|
||||
|
||||
settings.controllerLocation = normalizeControllerLocation(
|
||||
document.getElementById("controllerLocation").value
|
||||
);
|
||||
@@ -615,7 +626,9 @@ function save_options() {
|
||||
{ key: "rememberSpeed", type: "checkbox" },
|
||||
{ key: "forceLastSavedSpeed", type: "checkbox" },
|
||||
{ key: "audioBoolean", type: "checkbox" },
|
||||
{ key: "controllerOpacity", type: "text" }
|
||||
{ key: "controllerOpacity", type: "text" },
|
||||
{ key: "hideWithControls", type: "checkbox" },
|
||||
{ key: "hideWithControlsTimer", type: "text" }
|
||||
];
|
||||
|
||||
siteSettings.forEach((s) => {
|
||||
@@ -829,20 +842,27 @@ function createSiteRule(rule) {
|
||||
{ key: "rememberSpeed", type: "checkbox" },
|
||||
{ key: "forceLastSavedSpeed", type: "checkbox" },
|
||||
{ key: "audioBoolean", type: "checkbox" },
|
||||
{ key: "controllerOpacity", type: "text" }
|
||||
{ key: "controllerOpacity", type: "text" },
|
||||
{ key: "hideWithControls", type: "checkbox" },
|
||||
{ key: "hideWithControlsTimer", type: "text" }
|
||||
];
|
||||
|
||||
settings.forEach((s) => {
|
||||
var input = ruleEl.querySelector(`.site-${s.key}`);
|
||||
if (!input) return;
|
||||
|
||||
var value;
|
||||
if (rule && rule[s.key] !== undefined) {
|
||||
value = rule[s.key];
|
||||
} else {
|
||||
// Initialize with current global value
|
||||
if (s.type === "checkbox") {
|
||||
value = document.getElementById(s.key).checked;
|
||||
} else {
|
||||
value = document.getElementById(s.key).value;
|
||||
var globalInput = document.getElementById(s.key);
|
||||
if (globalInput) {
|
||||
if (s.type === "checkbox") {
|
||||
value = globalInput.checked;
|
||||
} else {
|
||||
value = globalInput.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -886,7 +906,16 @@ function restore_options() {
|
||||
document.getElementById("audioBoolean").checked = storage.audioBoolean;
|
||||
document.getElementById("enabled").checked = storage.enabled;
|
||||
document.getElementById("startHidden").checked = storage.startHidden;
|
||||
document.getElementById("hideWithYouTubeControls").checked = storage.hideWithYouTubeControls;
|
||||
|
||||
// Migration/Normalization for hideWithControls
|
||||
const hideWithControls = typeof storage.hideWithControls !== "undefined"
|
||||
? storage.hideWithControls
|
||||
: storage.hideWithYouTubeControls;
|
||||
|
||||
document.getElementById("hideWithControls").checked = hideWithControls;
|
||||
document.getElementById("hideWithControlsTimer").value =
|
||||
storage.hideWithControlsTimer || tcDefaults.hideWithControlsTimer;
|
||||
|
||||
document.getElementById("controllerLocation").value =
|
||||
normalizeControllerLocation(storage.controllerLocation);
|
||||
document.getElementById("controllerOpacity").value =
|
||||
|
||||
Reference in New Issue
Block a user