AutoSave Sessions Mod
-
What
Periodically (every (default 5) minutes), save the current browser session using Vivaldi's saved sessions functionality.It will keep the last (default 5) sessions.
You can them open them from the usual place:
Installation
As a custom.js mod:/* * Autosave Sessions (a mod for Vivaldi) * Written by LonM * V4.1: Attempt to retry if settings is not ready * v4 : Localise to current timezone, l10n * v3 : Has own settings section & support private windows again * v2 : Better handling of multiple windows */ (function autoSaveSessionsMod(){ "use strict"; const LANGUAGE = 'en_gb'; // en_gb or ko const l10n = { en_gb: { delay: 'Period (minutes)', restart: 'This setting requires a restart to take full effect.', maxoldsessions: 'Old Sessions Count', prefix: 'Prefix', prefixdesc: 'A unique prefix made up of the following characters: A-Z 0-9 _', saveprivate: 'Save Private Windows' }, }[LANGUAGE]; let CURRENT_SETTINGS = {}; /** * Copied from bundle.js © Vivaldi - Check if a filename is valid * @param {string} s */ function isValidName(e){ return /^[^\\/:\*\?"<>\|]+$/.test(e) && !/^\./.test(e) && !/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i.test(e); } /** * Turns a date into a string that can be used in a file name * Locale string seems to be the best at getting the correct time for any given timezone * @param {Date} date object */ function dateToFileSafeString(date){ const badChars = /[\\/:\*\?"<>\|]/gi; return date.toLocaleString().replace(badChars, '.'); } /** * Enable Autosaving sessions */ function autoSaveSession(isPrivate){ vivaldi.sessionsPrivate.getAll(allSessions => { const priv = isPrivate ? "PRIV" : ""; const prefix = CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_PREFIX"] + priv; const maxOld = CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_MAX_OLD_SESSIONS"]; const now = new Date(); const autosavesOnly = allSessions.filter(x => x.name.indexOf(prefix)===0); const oldestFirst = autosavesOnly.sort((a,b) => {return a.createDateJS - b.createDateJS;}); /* create the new session */ const name = prefix + dateToFileSafeString(now); /* final sanity check */ if (!isValidName(name)){ throw new Error("[Autosave Sessions] Cannot name a session as " + name); } const options = { saveOnlyWindowId: 0 }; vivaldi.sessionsPrivate.saveOpenTabs(name, options, () => {}); /* there is no way to tell if it failed */ /* delete older sessions */ let numberOfSessions = oldestFirst.length + 1; /* length + 1 as we have just added a new one */ let oldestIndex = 0; while(numberOfSessions > maxOld){ vivaldi.sessionsPrivate.delete(oldestFirst[oldestIndex].name,() => {}); oldestIndex++; numberOfSessions--; } }); } /** * Check if this is the most recent window, and if the most recent window is still open * if not, then stop saving the sessions */ function triggerAutosave(){ chrome.storage.local.get("LONM_SESSION_AUTOSAVE_LAST_WINDOW", data => { const lastOpenedWindow = data["LONM_SESSION_AUTOSAVE_LAST_WINDOW"]; if(window.vivaldiWindowId===lastOpenedWindow){ /* We know this window is correct, skip the checks */ autoSaveSession(); return; } chrome.windows.getAll(openWindows => { const foundLastOpen = openWindows.find(window => window.id===lastOpenedWindow); if(foundLastOpen){ /*Most recent window still active, use that one instead*/ } else { /*Most recent window was closed, revert to this one*/ chrome.storage.local.set({ "LONM_SESSION_AUTOSAVE_LAST_WINDOW": window.vivaldiWindowId }, () => { autoSaveSession(); }); } }); }); } function triggerAutosavePrivate(){ chrome.storage.local.get("LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW", data => { const lastOpenedWindow = data["LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW"]; if(window.vivaldiWindowId===lastOpenedWindow){ /* We know this window is correct, skip the checks */ autoSaveSession(true); return; } chrome.windows.getAll(openWindows => { const foundLastOpen = openWindows.find(window => window.id===lastOpenedWindow); if(foundLastOpen){ /*Most recent window still active, use that one instead*/ } else { /*Most recent window was closed, revert to this one*/ chrome.storage.local.set({ "LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW": window.vivaldiWindowId }, () => { autoSaveSession(true); }); } }); }); } /** * Mod the settings page to show settings there * Wait a little bit after a settings page has been opened and add settings in */ const SETTINGSPAGE = "chrome-extension://mpognobbkildjkofajifpdfhcoklimli/components/settings/settings.html?path=general"; function modSettingsPageListener(newTab){ if(newTab.url === SETTINGSPAGE || newTab.pendingUrl === SETTINGSPAGE){ setTimeout(modSettingsPage, 1000); } } function modSettingsPage(){ const settingSection = document.querySelector(".vivaldi-settings .settings-content section"); if(!settingSection){ setTimeout(modSettingsPage, 1000); return; } const settingsHTML = document.createElement("section"); settingsHTML.className = "setting-section"; settingsHTML.id = "lonmAutosaveSessionsSettings"; const settingsDiv = document.createElement("div"); settingsDiv.insertAdjacentHTML("beforeend", "<h2>Autosave Sessions Mod</h2>"); MOD_SETTINGS.forEach(setting => { settingsDiv.appendChild(makeSettingElement(setting)); }); settingsHTML.appendChild(settingsDiv); settingSection.insertAdjacentElement("afterbegin", settingsHTML); } /** * For a mod setting you need: * * A) Load it when the mod starts * B) Make an option for it when settings is opened * C) Change the saved and current state with new value when setting is changed * * Mod setting has: * Key: string * Default Value: string|int * Description: string */ const MOD_SETTINGS = [ { id: "LONM_SESSION_AUTOSAVE_DELAY_MINUTES", type: Number, min: 1, max: undefined, default: 5, title: l10n.delay, description: l10n.restart }, { id: "LONM_SESSION_AUTOSAVE_MAX_OLD_SESSIONS", type: Number, min: 1, max: undefined, default: 5, title: l10n.maxoldsessions }, { id: "LONM_SESSION_AUTOSAVE_PREFIX", type: String, pattern: "[\\w_]{0,20}", default: "VSESAUTOSAVE_", title: l10n.prefix, description: l10n.prefixdesc }, { id: "LONM_SESSION_SAVE_PRIVATE_WINDOWS", type: Boolean, default: false, title: l10n.saveprivate, description: l10n.restart } ]; /** * Handle a change to a setting input * Should be bound in a listener to the setting object * @param {InputEvent} input */ function settingUpdated(input){ if(input.target.type === "checkbox"){ CURRENT_SETTINGS[this.id] = input.target.checked; } else { input.target.checkValidity(); if(input.target.reportValidity() && input.target.value !== ""){ CURRENT_SETTINGS[this.id] = input.target.value; } } chrome.storage.local.set(CURRENT_SETTINGS); } /** * Create an element for the current setting * @param modSetting */ function makeSettingElement(modSetting) { const currentSettingValue = CURRENT_SETTINGS[modSetting.id]; const div = document.createElement("div"); div.className = "setting-single"; const title = document.createElement("h3"); title.innerText = modSetting.title; div.appendChild(title); if(modSetting.description){ const info = document.createElement("p"); info.className = "info"; info.innerText = modSetting.description; div.appendChild(info); } const input = document.createElement("input"); input.id = modSetting.id; input.value = currentSettingValue; input.autocomplete = "off"; input.autocapitalize = "off"; input.autocorrect = "off"; input.spellcheck = "off"; switch (modSetting.type) { case Number: input.type = "number"; break; case String: input.type = "text"; break; case Boolean: input.type = "checkbox"; if(currentSettingValue){input.checked = "checked";} break; default: throw Error("Unknown setting type!"); } if(modSetting.max){input.max = modSetting.max;} if(modSetting.min){input.min = modSetting.min;} if(modSetting.pattern){input.pattern = modSetting.pattern;} input.addEventListener("input", settingUpdated.bind(modSetting)); div.appendChild(input); return div; } /** * Init the mod, but only if we are not incognito, to maintain privacy. * Save the window id in storage, and only use the most recent window to save sessions */ function init(){ if(window.vivaldiWindowId){ chrome.windows.getCurrent(window => { if(!window.incognito){ chrome.storage.local.set({ "LONM_SESSION_AUTOSAVE_LAST_WINDOW": window.vivaldiWindowId }, () => { setInterval(triggerAutosave, CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_DELAY_MINUTES"]*60*1000); }); } if(CURRENT_SETTINGS["LONM_SESSION_SAVE_PRIVATE_WINDOWS"] && window.incognito){ chrome.storage.local.set({ "LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW": window.vivaldiWindowId }, () => { setInterval(triggerAutosavePrivate, CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_DELAY_MINUTES"]*60*1000); }); } chrome.tabs.onCreated.addListener(modSettingsPageListener); }); } else { setTimeout(init, 500); } } /** * Load the settings and call the initialiser function */ function loadSettingsAndInit(){ const keys = MOD_SETTINGS.reduce((prev, current) => { prev[current.id] = current.default; return prev; }, {}); chrome.storage.local.get(keys, value => { CURRENT_SETTINGS = value; setTimeout(init, 500); }); } loadSettingsAndInit(); })();
Remarks
When you are configuring the mod, read the comments carefully, as there are caveats to the kind of session name you can use, how often to save.If you want to keep every single old session (NOT RECOMMENDED), use
Infinity
- type it just like that, no quotes, it's a valid javascript value. This will probably break your system through filling your disk storage space up, but if you want the option - it's there.There is a feature request for session autosave, but this only refers to saving when you are about to close vivaldi. This mod is totally different, instead autosaving as you browse (e.g. if for some reason your machine is particularly crash-prone).
TIMEZONES & Daylight Saving time: Some sessions may not be created properly / older ones overwritten incorrectly if your computer switches time zone, or if daylight saving time occurs during a save period.
Some mod stuff can be configured in a settings page, but only when settings is in a tab, not in a window.
This mod supports localisation. At time of writing, only
en_gb
is supported.Changelog
- Fix timing issue when opening settings
- Timezone stuff
- Settings
- Better handling of multiple windows
- Initial release
-
wonderful, keeping it just in case
-
For whatever reason the console spits out this on my computer
Refused to load the script 'file:///I:/VIVALDI/_MOD/aoutosaveSession.js' because it violates the following Content Security Policy directive: "script-src 'self' http://localhost:35729/". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
even with a squeaky clean fresh stand alone install ...
-
@QuHno I have a couple of extra Qs:
- Which version of vivaldi are you using?
- Did this mod work on previous versions?
- How are you installing the mod?
- Do you have any local servers running at localhost:35729 ? That's the most confusing part of this...
Also, is there a typo in
aoutosaveSession
? -
- I've tried it in all versions from 2.6.1566.44 (stable) to 2.7.1593.1 (sopranos) all as stand alone in the 32 and 64 bit versions on Win 7 and 10
- Don't know about older versions, not tested yet, I just installed it yesterday because I was curious how well it works
- By adding a
<script src="..."></script>
line after the other script lines to thebrowser.html
And yes, I made sure that it is UTF-8 and has UNIX LF - Nope, only an on demand server on port 8080 and it was off at the time I tested it.
- If there is a typo, it is in the original script, I only copy pasted it unchanged - and even if there were an c/p error or a typo, it should give another error and not the CSP violation error I get
... but I wonder why you do not get the error, because I've found this specific CSP in the
[path to Vivaldi]\Application\[version number]\resources.pak
"app": { "background": { "scripts": [ "background-common-bundle.js", "background-bundle.js" ] }, "content_security_policy": "script-src 'self' http://localhost:35729/; object-src 'self'" },
PS: Basically I don't need the mod. I make daily compressed backups and I know how to save sessions if I want to make sure they don't go lost (something that should get more advertised in the UI, or else there would be less bug reports)
-
OK, problem solved:
It is only possible to "install" this mod directly in the
[path to]\Application\#.#.####.#\resources\vivaldi
folder, or in a subfolder inside of that folder.I tried it like I did with my mods some years ago in a path beneath the the Application folder, but that does not work because above mentioned CSP.
-
@LonM Hi, thanks for this awesome mod.
I have using you mod for a while now, & I think there is a glitch with multiple opened windows, which could cause creating unlimited save session at once until i run out of disk space. I have setup to only keep 2 copy of saved session & repeat every 15 mins.
Thanks again.
-
@dude99 Oh no, that sounds bad. I'll take a look at it.
-
@dude99 Hi,
Do you have any screenshots or names of the session files? That would be helpful in debugging this.
-
@LonM I will try to get it next time around, have to wait for it to happen again... LOL
-
@dude99 Thanks.
-
@LonM There, it glitched half an hour ago:
-
@dude99 OK. It looks like the interval is being triggered once every millisecond which isn't good.
Some additional Qs:
- How many windows do you have open?
- Which version of vivaldi are you in?
-
@LonM Now I have 10 windows, I don't think it happen when I only have 1 window opened. Using latest stable 2.7.1628.30, but it keep happening since I begin using it about a few weeks ago.
-
@dude99 I've updated the original post with a newer version to test. Does this work better?
-
@LonM Thanks, I'm testing it now.
will report back afterward. -
@LonM Hi, I have tested it for 48 hours & it seems you have fixed it! Congratulation & thanks a lot!
-
@dude99 Good to hear
-
@LonM Hi, sorry to bother you again... I just wonder, is it possible to drop this JS into the extension page (dev mode) & turn it into an extension?
-
@dude99 It would be easy to do that.... but not right now.
If the developers of vivaldi would open the internal API to extensions, perhaps behind some kind of 'sessions' permission, then yes, then it would be possible.