idle: initial add

This commit is contained in:
CanadaHonk 2022-12-20 21:32:24 +00:00
parent 007cf2d032
commit fb99d0e987
2 changed files with 120 additions and 1 deletions

View File

@ -10,6 +10,8 @@ import { fileURLToPath } from 'url';
import Chromium from './browser/chromium.js';
import Firefox from './browser/firefox.js';
import IdleAPI from './lib/idle.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
@ -94,7 +96,7 @@ const startBrowser = async (url, { windowSize, forceBrowser }) => {
const browserType = browserName.startsWith('firefox') ? 'firefox' : 'chromium';
return await (browserType === 'firefox' ? Firefox : Chromium)({
const Browser = await (browserType === 'firefox' ? Firefox : Chromium)({
browserName: browserFriendlyName,
dataPath,
browserPath
@ -102,6 +104,10 @@ const startBrowser = async (url, { windowSize, forceBrowser }) => {
url,
windowSize
});
Browser.idle = await IdleAPI(Browser.cdp, { browserType, dataPath });
return Browser;
};
export const open = async (url, { windowSize, onLoad, forceBrowser } = {}) => {

113
src/lib/idle.js Normal file
View File

@ -0,0 +1,113 @@
import { exec } from 'child_process';
const getProcesses = async containing => process.platform !== 'win32' ? Promise.resolve([]) : new Promise(resolve => exec(`wmic process get Commandline,ProcessID /format:csv`, (e, out) => {
resolve(out.toString().split('\r\n').slice(2).map(x => {
const parsed = x.trim().split(',').slice(1).reverse();
return [
parseInt(parsed[0]) || parsed[0], // pid to int
parsed.slice(1).join(',')
];
}).filter(x => x[1] && x[1].includes(containing)));
}));
const killProcesses = async pids => process.platform !== 'win32' ? Promise.resolve('') : new Promise(resolve => exec(`taskkill /F ${pids.map(x => `/PID ${x}`).join(' ')}`, (e, out) => resolve(out)));
export default async (CDP, { browserType, dataPath }) => {
if (browserType !== 'chromium') { // current implementation is for chromium-based only
const warning = () => log(`Warning: Idle API is currently only for Chromium (running on ${browserType})`);
return {
hibernate: warning,
sleep: warning,
wake: warning,
auto: warning
};
};
const killNonCrit = async () => {
const procs = await getProcesses(dataPath);
const nonCriticalProcs = procs.filter(x => x[1].includes('type='));
await killProcesses(nonCriticalProcs.map(x => x[0]));
log(`killed ${nonCriticalProcs.length} processes`);
};
let hibernating = false;
const hibernate = async () => {
hibernating = true;
const startTime = performance.now();
await killNonCrit();
// await killNonCrit();
log(`hibernated in ${(performance.now() - startTime).toFixed(2)}ms`);
};
const wake = async () => {
const startTime = performance.now();
await CDP.send('Page.reload');
log(`began wake in ${(performance.now() - startTime).toFixed(2)}ms`);
hibernating = false;
};
const { windowId } = await CDP.send('Browser.getWindowForTarget');
let autoEnabled = process.argv.includes('--force-auto-idle'), autoOptions = {
timeMinimizedToHibernate: 5
};
let autoInterval;
const startAuto = () => {
if (autoInterval) return; // already started
let lastState = '', lastStateWhen = performance.now();
autoInterval = setInterval(async () => {
const { bounds: { windowState } } = await CDP.send('Browser.getWindowBounds', { windowId });
if (windowState !== lastState) {
lastState = windowState;
lastStateWhen = performance.now();
}
if (!hibernating && windowState === 'minimized' && performance.now() - lastStateWhen > autoOptions.timeMinimizedToHibernate * 1000) await hibernate();
else if (hibernating && windowState !== 'minimized') await wake();
}, 200);
log('started auto idle');
};
const stopAuto = () => {
if (!autoInterval) return; // already stopped
clearInterval(autoInterval);
autoInterval = null;
log('stopped auto idle');
};
log(`idle API active (window id: ${windowId})`);
if (autoEnabled) startAuto();
return {
hibernate,
sleep: () => {},
wake,
auto: (enabled, options) => {
autoEnabled = enabled;
autoOptions = {
...options,
...autoOptions
};
if (enabled) startAuto();
else stopAuto();
}
};
};