// ==UserScript==
// @name GeForceNOW Smart Refresh Script by Mohi
// @name:ar برنامج تحديث صفحة جيفورس ناو تلقائي بواسطة Mohi
// @namespace http://tampermonkey.net/
// @version 5.0
// @description Auto-refresh GeForce Now page & notifies the user if free capacity is available. Fully optimized for accurate countdown even in the background, with a full range of settings to customize your experience as you'd like.
// @description:ar يقوم بتحديث صفحة GeForce Now تلقائيًا ويقوم بإعلامك إذا كان هناك سعة مجانية متاحة. تم تصميم السكربت لتحسين الأداء مع عدادات دقيقة، حتى عندما تكون الصفحة في الخلفية. يقدم السكربت مجموعة من الإعدادات القابلة للتخصيص، بما في ذلك النقر التلقائي، وإشعارات الصوت، وأساليب الكشف، لتعزيز تجربتك.
// @author Mohi
// @license GNU GPLv3
// @match https://www.nvidia.com/gfn/product-matrix/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_notification
// ==/UserScript==
(function() {
'use strict';
const defaultSettings = {
AUTO_CLICK_BUTTON: true,
refreshInterval: 15,
freeCapacitySoundURL: 'https://www.myinstants.com/media/sounds/airhorn.mp3',
freeCapacitySoundVolume: 0.3,
notificationTimeout: 5000,
autoReload: true,
detectionMethod: 'both' // "button", "text", or "both"
};
let AUTO_CLICK_BUTTON = GM_getValue("AUTO_CLICK_BUTTON", defaultSettings.AUTO_CLICK_BUTTON);
let refreshInterval = GM_getValue("refreshInterval", defaultSettings.refreshInterval);
let freeCapacitySoundURL = GM_getValue("freeCapacitySoundURL", defaultSettings.freeCapacitySoundURL);
let freeCapacitySoundVolume = GM_getValue("freeCapacitySoundVolume", defaultSettings.freeCapacitySoundVolume);
let notificationTimeout = GM_getValue("notificationTimeout", defaultSettings.notificationTimeout);
let autoReload = GM_getValue("autoReload", defaultSettings.autoReload);
let detectionMethod = GM_getValue("detectionMethod", defaultSettings.detectionMethod);
let countdownTime = refreshInterval;
let menuCommandIDs = [];
let capacityFound = false;
// Web Worker for countdown
let countdownWorker;
// Handle background/foreground visibility
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
console.log("Page is in the background. Reducing resource usage.");
} else {
console.log("Page is in the foreground. Restoring full performance.");
}
});
function playFreeCapacitySound() {
if (freeCapacitySoundURL) {
const audio = new Audio(freeCapacitySoundURL);
audio.volume = freeCapacitySoundVolume;
audio.play();
}
}
function showNotification(title, message) {
GM_notification({
text: message,
title: title,
timeout: notificationTimeout
});
}
function updateTabTitle() {
document.title = !capacityFound ? `Refreshing in ${Math.round(countdownTime)}s | GFN` : "Free Capacity Found | GFN";
}
function handleCountdown(data) {
if (!capacityFound && autoReload) {
countdownTime = data.countdownTime;
updateTabTitle();
if (countdownTime <= 0) {
triggerReload();
}
}
}
function triggerReload() {
checkCapacityMessage(); // Check capacity before deciding to reload
if (!capacityFound) { // Only reload if free capacity has not been found
countdownTime = refreshInterval;
location.reload();
}
}
function checkCapacityMessage() {
const capacityMessage = document.body.innerText.includes("GeForce NOW is currently at capacity.");
const playButton = document.querySelector('button[data-qa-id="play-button"]');
if (detectionMethod === 'text' && !capacityMessage && !capacityFound) {
capacityFound = true;
notifyUser();
}
if (detectionMethod === 'button' && playButton && !capacityFound) {
capacityFound = true;
clickPlayButton();
}
if (detectionMethod === 'both' && (!capacityMessage || playButton) && !capacityFound) {
capacityFound = true;
notifyUser();
if (playButton) clickPlayButton();
}
}
function notifyUser() {
playFreeCapacitySound();
showNotification("GeForce NOW Free Capacity", "Free capacity found on GeForce NOW!");
updateTabTitle();
}
function clickPlayButton() {
const playButton = document.querySelector('button[data-qa-id="play-button"]');
if (playButton && !capacityFound) {
capacityFound = true;
playButton.click();
playFreeCapacitySound();
updateTabTitle();
}
}
// Create Web Worker for countdown
function createCountdownWorker() {
const blob = new Blob([`
let countdownTime = ${refreshInterval};
function countdownStep() {
countdownTime -= 1;
postMessage({ countdownTime });
if (countdownTime > 0) {
setTimeout(countdownStep, 1000);
}
}
countdownStep();
`], { type: 'application/javascript' });
const workerURL = URL.createObjectURL(blob);
return new Worker(workerURL);
}
function startCountdownWorker() {
if (countdownWorker) {
countdownWorker.terminate();
}
countdownWorker = createCountdownWorker();
countdownWorker.onmessage = function(event) {
handleCountdown(event.data);
};
}
// Keep-Alive Ping
function keepAlivePing() {
fetch('https://example.com/ping', { method: 'GET' })
.catch(err => console.error('Ping failed', err));
setTimeout(keepAlivePing, 30000); // Ping every 30 seconds
}
function updateMenuCommands() {
for (let id of menuCommandIDs) {
GM_unregisterMenuCommand(id);
}
menuCommandIDs = [];
// Detection Method Menu
menuCommandIDs.push(GM_registerMenuCommand(`Set Detection Method (current: ${detectionMethod})`, setDetectionMethod));
// Refresh Interval
menuCommandIDs.push(GM_registerMenuCommand(`Set Refresh Interval (current: ${refreshInterval}s)`, setRefreshInterval));
// Free Capacity Sound URL
menuCommandIDs.push(GM_registerMenuCommand(`Set Free Capacity Sound URL (current: ${freeCapacitySoundURL})`, setFreeCapacitySoundURL));
// Free Capacity Sound Volume
menuCommandIDs.push(GM_registerMenuCommand(`Set Free Capacity Sound Volume (current: ${freeCapacitySoundVolume * 100}%)`, setFreeCapacitySoundVolume));
// Notification Timeout
menuCommandIDs.push(GM_registerMenuCommand(`Set Notification Timeout (current: ${notificationTimeout / 1000}s)`, setNotificationTimeout));
// Auto Click Button (disable if text detection is selected)
if (detectionMethod === 'text') {
menuCommandIDs.push(GM_registerMenuCommand("Auto Click Button (disabled due to text detection)", () => {
alert("Auto Click Button is disabled because detection method is set to 'text'. Change detection method to enable this feature.");
}));
} else if (AUTO_CLICK_BUTTON) {
menuCommandIDs.push(GM_registerMenuCommand("Turn Auto Click Button OFF", () => toggleAutoClickButton(false)));
} else {
menuCommandIDs.push(GM_registerMenuCommand("Turn Auto Click Button ON", () => toggleAutoClickButton(true)));
}
menuCommandIDs.push(GM_registerMenuCommand("Reset to Default Settings", resetToDefaultSettings));
}
function setDetectionMethod() {
const newMethod = prompt("Enter detection method (button, text, both):", detectionMethod);
if (newMethod === 'button' || newMethod === 'text' || newMethod === 'both') {
detectionMethod = newMethod;
GM_setValue("detectionMethod", detectionMethod);
updateMenuCommands();
} else {
alert("Invalid detection method. Please enter 'button', 'text', or 'both'.");
}
}
function resetToDefaultSettings() {
const confirmation = confirm(
`Are you sure you want to reset settings to default? \n` +
`Default Settings:\n` +
`- Auto Click Button: ${defaultSettings.AUTO_CLICK_BUTTON ? "ON" : "OFF"}\n` +
`- Refresh Interval: ${defaultSettings.refreshInterval}s\n` +
`- Free Capacity Sound URL: ${defaultSettings.freeCapacitySoundURL}\n` +
`- Free Capacity Sound Volume: ${defaultSettings.freeCapacitySoundVolume * 100}%\n` +
`- Notification Timeout: ${defaultSettings.notificationTimeout / 1000}s\n` +
`- Detection Method: ${defaultSettings.detectionMethod}`
);
if (confirmation) {
AUTO_CLICK_BUTTON = defaultSettings.AUTO_CLICK_BUTTON;
refreshInterval = defaultSettings.refreshInterval;
freeCapacitySoundURL = defaultSettings.freeCapacitySoundURL;
freeCapacitySoundVolume = defaultSettings.freeCapacitySoundVolume;
notificationTimeout = defaultSettings.notificationTimeout;
detectionMethod = defaultSettings.detectionMethod;
GM_setValue("AUTO_CLICK_BUTTON", AUTO_CLICK_BUTTON);
GM_setValue("refreshInterval", refreshInterval);
GM_setValue("freeCapacitySoundURL", freeCapacitySoundURL);
GM_setValue("freeCapacitySoundVolume", freeCapacitySoundVolume);
GM_setValue("notificationTimeout", notificationTimeout);
GM_setValue("detectionMethod", detectionMethod);
countdownTime = refreshInterval;
updateMenuCommands();
showNotification("Settings Reset", "All settings have been reset to default values.");
}
}
function setRefreshInterval() {
const newInterval = prompt("Enter new refresh interval in seconds:", refreshInterval);
if (newInterval !== null) {
refreshInterval = parseInt(newInterval);
GM_setValue("refreshInterval", refreshInterval);
countdownTime = refreshInterval;
updateMenuCommands();
}
}
function setFreeCapacitySoundURL() {
const newURL = prompt("Enter new Free Capacity Sound URL:", freeCapacitySoundURL);
if (newURL !== null) {
freeCapacitySoundURL = newURL;
GM_setValue("freeCapacitySoundURL", freeCapacitySoundURL);
updateMenuCommands();
}
}
function setFreeCapacitySoundVolume() {
const newVolume = prompt("Enter Free Capacity Sound Volume (0-100):", freeCapacitySoundVolume * 100);
if (newVolume !== null) {
freeCapacitySoundVolume = parseInt(newVolume) / 100;
GM_setValue("freeCapacitySoundVolume", freeCapacitySoundVolume);
updateMenuCommands();
}
}
function setNotificationTimeout() {
const newTimeout = prompt("Enter new Notification Timeout in seconds:", notificationTimeout / 1000);
if (newTimeout !== null) {
notificationTimeout = parseInt(newTimeout) * 1000;
GM_setValue("notificationTimeout", notificationTimeout);
updateMenuCommands();
}
}
function toggleAutoClickButton(turnOn) {
AUTO_CLICK_BUTTON = turnOn;
GM_setValue("AUTO_CLICK_BUTTON", turnOn);
showNotification("Auto Click Button Toggled", `Auto Click Button is now ${turnOn ? "ON" : "OFF"}`);
updateMenuCommands();
}
startCountdownWorker();
keepAlivePing();
updateMenuCommands();
})();