Guest User

Untitled

a guest
Jul 25th, 2025
6
0
332 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Restore LocalStorage Across Domains
  3. // @namespace    res.ls.pb
  4. // @version      1.1.0
  5. // @description  Persists localStorage for specific sites across private browsing sessions using GM storage. Allows manual restore from other backed-up domains.
  6. // @author       Gemini
  7. // @license      UNLICENSE
  8. // @match        *://*.8chan.moe/*
  9. // @match        *://*.8chan.se/*
  10. // @match        *://*.8chan.cc/*
  11. // @match        *://alephchvkipd2houttjirmgivro5pxullvcgm4c47ptm7mhubbja6kad.onion/*
  12. // @grant        GM.setValue
  13. // @grant        GM.getValue
  14. // @grant        GM.registerMenuCommand
  15. // @run-at       document-start
  16. // ==/UserScript==
  17.  
  18. //PROBLEMS:
  19. //- Currently Tampermonkey's Permanent incognito storage option doesn't work perfectly, it only works when closing the whole browser? I think it works on violentmonkey though.
  20.  
  21.  
  22. (async function () {
  23.     "use strict";
  24.  
  25.     // == OPTIONS ==================================================================================================
  26.     // == OPTIONS ==================================================================================================
  27.     // == OPTIONS ==================================================================================================
  28.     // The following options control the behavior of the script
  29.  
  30.     // --- Toggle for automatic restore/save ---
  31.     // If true, the script will automatically restore the page's localStorage from GM storage when the page loads.
  32.     // If false, the script will not automatically restore the page's localStorage, but you can still use the menu command to manually restore.
  33.     const AUTOMATIC_RESTORE_ENABLED = false;
  34.     // =============================================================================================================
  35.     // =============================================================================================================
  36.     // =============================================================================================================
  37.  
  38.     const SCRIPT_PREFIX = "rLS_"; // General prefix for keys to avoid collisions
  39.     const PAGE_LS_FLAG_KEY_NAME = `${SCRIPT_PREFIX}flag_v1`; // Flag in page's localStorage
  40.     const GM_LS_DATA_KEY_PREFIX = `${SCRIPT_PREFIX}bkp_`; // Prefix for GM storage key for LS data
  41.     const GM_FIRSTRUN_FLAG_KEY_PREFIX = `${SCRIPT_PREFIX}firstrun_v1_`; // Prefix for GM storage key for first run flag
  42.     const GM_DEBUG_EVENTS_KEY = `${SCRIPT_PREFIX}debug_events`; // Key for GM storage to log events
  43.  
  44.     // --- Dynamic Keys based on current hostname ---
  45.     const currentHostname = window.location.hostname;
  46.     const gmStorageKeyForCurrentHost = `${GM_LS_DATA_KEY_PREFIX}${currentHostname}`;
  47.     const gmFirstRunFlagKeyForCurrentHost = `${GM_FIRSTRUN_FLAG_KEY_PREFIX}${currentHostname}`;
  48.  
  49.     // --- Helper Functions ---
  50.     function log(message, ...args) {
  51.         console.log(`[RestoreLS] (${currentHostname}) ${message}`, ...args);
  52.     }
  53.  
  54.     function error(message, ...args) {
  55.         console.error(`[RestoreLS] (${currentHostname}) ${message}`, ...args);
  56.     }
  57.  
  58.     async function savePageLSToGM(targetGmKey) {
  59.         try {
  60.             const lsData = { ...localStorage }; // Modern way to copy localStorage
  61.             await GM.setValue(targetGmKey, JSON.stringify(lsData));
  62.             log(`Page localStorage saved to GM storage key: ${targetGmKey}`);
  63.         } catch (e) {
  64.             error("Failed to save page localStorage to GM storage:", e);
  65.         }
  66.     }
  67.  
  68.     async function restoreLSFromGM(sourceGmKey, isManualRestore = false, sourceDomainForManualRestore = "") {
  69.         try {
  70.             const storedDataString = await GM.getValue(sourceGmKey, null);
  71.             if (storedDataString) {
  72.                 const dataToRestore = JSON.parse(storedDataString);
  73.  
  74.                 // Clear current LS before restoring to handle deletions properly
  75.                 // Preserve our flag if it was set by the first-run logic on THIS page load,
  76.                 // otherwise, the restore operation itself will set a new appropriate flag.
  77.                 const flagExistedBeforeClear = localStorage.getItem(PAGE_LS_FLAG_KEY_NAME);
  78.                 localStorage.clear();
  79.                 if (flagExistedBeforeClear && !isManualRestore && flagExistedBeforeClear.startsWith("initial_")) {
  80.                     localStorage.setItem(PAGE_LS_FLAG_KEY_NAME, flagExistedBeforeClear);
  81.                 }
  82.  
  83.                 for (const key in dataToRestore) {
  84.                     if (Object.prototype.hasOwnProperty.call(dataToRestore, key)) {
  85.                         localStorage.setItem(key, dataToRestore[key]);
  86.                     }
  87.                 }
  88.  
  89.                 let flagValue;
  90.                 if (isManualRestore) {
  91.                     flagValue = `manually_restored_from_${sourceDomainForManualRestore}_at_${new Date().toISOString()}`;
  92.                     log(`Page localStorage manually restored from GM key: ${sourceGmKey} (origin: ${sourceDomainForManualRestore}).`, Object.keys(dataToRestore).length, "items.");
  93.                     alert(`Restored ${Object.keys(dataToRestore).length} items from ${sourceDomainForManualRestore} to the current page.`);
  94.                 } else {
  95.                     flagValue = `restored_from_gm_at_${new Date().toISOString()}`;
  96.                     log("Page localStorage automatically restored from GM storage.", Object.keys(dataToRestore).length, "items.");
  97.                 }
  98.                 localStorage.setItem(PAGE_LS_FLAG_KEY_NAME, flagValue);
  99.             } else {
  100.                 if (isManualRestore) {
  101.                     log(`No data found in GM storage for key: ${sourceGmKey} (for domain ${sourceDomainForManualRestore})`);
  102.                     alert(`No localStorage backup found for domain: ${sourceDomainForManualRestore}`);
  103.                 } else {
  104.                     log("No data found in GM storage to restore. Page localStorage remains as is (likely empty or new session).");
  105.                     localStorage.setItem(PAGE_LS_FLAG_KEY_NAME, `no_gm_data_to_restore_at_${new Date().toISOString()}`);
  106.                 }
  107.             }
  108.         } catch (e) {
  109.             error("Failed to restore page localStorage from GM storage:", e);
  110.             localStorage.setItem(PAGE_LS_FLAG_KEY_NAME, `restore_failed_at_${new Date().toISOString()}`);
  111.             if (isManualRestore) {
  112.                 alert(`Error restoring data from ${sourceDomainForManualRestore}: ${e.message}`);
  113.             }
  114.         }
  115.     }
  116.  
  117.     // async function logEventToGMStorage(eventName) {
  118.     //     try {
  119.     //         const timestamp = new Date().toISOString();
  120.     //         const eventData = { event: eventName, time: timestamp };
  121.  
  122.     //         // Get existing events or create new array
  123.     //         const existingEvents = JSON.parse(await GM.getValue(GM_DEBUG_EVENTS_KEY, '[]'));
  124.     //         existingEvents.push(eventData);
  125.  
  126.     //         // Keep only last 20 events
  127.     //         const trimmedEvents = existingEvents.slice(-20);
  128.     //         await GM.setValue(GM_DEBUG_EVENTS_KEY, JSON.stringify(trimmedEvents));
  129.     //         log(`Event logged: ${eventName} at ${timestamp}`);
  130.     //     } catch (e) {
  131.     //         error("Failed to log event to GM storage:", e);
  132.     //     }
  133.     // }
  134.  
  135.     // --- Main Logic ---
  136.     // log("Script starting.");
  137.  
  138.     if (AUTOMATIC_RESTORE_ENABLED) {
  139.         // Check for userscript's first run for THIS specific domain
  140.         const isScriptFirstRunForThisDomain = (await GM.getValue(gmFirstRunFlagKeyForCurrentHost, null)) === null;
  141.  
  142.         if (isScriptFirstRunForThisDomain) {
  143.             log("Userscript first-run detected for this domain.");
  144.             // On the very first run for this domain, set the flag in page LS.
  145.             // This signifies that the current page LS is the "source of truth" for the first save.
  146.             try {
  147.                 localStorage.setItem(PAGE_LS_FLAG_KEY_NAME, `initial_userscript_setup_at_${new Date().toISOString()}`);
  148.                 log("Page LS flag set due to userscript first run for this domain.");
  149.                 // And immediately save the current (potentially empty or pre-existing) LS to GM
  150.                 await savePageLSToGM(gmStorageKeyForCurrentHost);
  151.             } catch (e) {
  152.                 error("Could not set page LS flag or save initial LS (localStorage might be disabled/full):", e);
  153.             }
  154.             await GM.setValue(gmFirstRunFlagKeyForCurrentHost, true); // Mark first run for this domain as completed
  155.         } else {
  156.             // Not the first run for this domain. Check if page LS needs restoration.
  157.             const pageLSFlagExists = localStorage.getItem(PAGE_LS_FLAG_KEY_NAME) !== null;
  158.  
  159.             if (!pageLSFlagExists) {
  160.                 log("Page LS flag not found. Attempting to restore from GM storage for this domain.");
  161.                 await restoreLSFromGM(gmStorageKeyForCurrentHost);
  162.             } else {
  163.                 log("Page LS flag found. Page localStorage assumed to be initialized or already restored for this session.");
  164.             }
  165.         }
  166.     } else {
  167.         log("AUTOMATIC_RESTORE_ENABLED is false. Skipping automatic restore/save logic.");
  168.     }
  169.  
  170.     if (AUTOMATIC_RESTORE_ENABLED) {
  171.         // window.addEventListener('beforeunload', async () => {
  172.         //     log("beforeunload: Saving current page LS to GM storage for this domain.");
  173.         //     await logEventToGMStorage('beforeunload');
  174.         //     await savePageLSToGM(gmStorageKeyForCurrentHost);
  175.         // });
  176.         window.addEventListener('visibilitychange', async () => {
  177.             if (document.hidden) {
  178.                 log("visibilitychange: Saving current page LS to GM storage for this domain.");
  179.                 // await logEventToGMStorage('visibilitychange');
  180.                 await savePageLSToGM(gmStorageKeyForCurrentHost);
  181.             }
  182.         });
  183.         window.addEventListener('pagehide', async () => {
  184.             log("pagehide: Saving current page LS to GM storage for this domain.");
  185.             // await logEventToGMStorage('pagehide');
  186.             await savePageLSToGM(gmStorageKeyForCurrentHost);
  187.         });
  188.     }
  189.  
  190.     // Register Menu Command for manual restore
  191.     GM.registerMenuCommand("Restore LS from a domain's backup", async () => {
  192.         const sourceDomain = prompt(
  193.             "Enter the domain name (e.g., example.com) whose localStorage backup you want to restore to THIS page:",
  194.             currentHostname // default to current domain
  195.         );
  196.         if (sourceDomain && sourceDomain.trim() !== "") {
  197.             const trimmedSourceDomain = sourceDomain.trim();
  198.             log(`Manual restore initiated. Source domain: ${trimmedSourceDomain}`);
  199.             const sourceGmKey = `${GM_LS_DATA_KEY_PREFIX}${trimmedSourceDomain}`;
  200.             await restoreLSFromGM(sourceGmKey, true, trimmedSourceDomain);
  201.             // After a manual restore, we also want to ensure this newly restored state is saved for the *current* domain when the page unloads.
  202.             // The 'beforeunload' or 'visibilitychange' handler will take care of this naturally.
  203.         } else if (sourceDomain !== null) {
  204.             // Not null means user didn't press Cancel
  205.             alert("Invalid or empty domain entered.");
  206.         }
  207.     });
  208.  
  209.     // Register Menu Command for manual save
  210.     GM.registerMenuCommand("Save current domain's LS to GM storage", async () => {
  211.         log("Manual save triggered via menu command.");
  212.         await savePageLSToGM(gmStorageKeyForCurrentHost);
  213.         alert("Current page localStorage has been saved to GM storage for this domain.");
  214.     });
  215.  
  216.     // Add a menu command to view the logged events
  217.     GM.registerMenuCommand("View Debug Event Logs", async () => {
  218.         const events = JSON.parse(await GM.getValue(GM_DEBUG_EVENTS_KEY, '[]'));
  219.         if (events.length === 0) {
  220.             alert("No events have been logged yet.");
  221.             return;
  222.         }
  223.  
  224.         const formattedEvents = events.map(e => `${e.event}: ${e.time}`).join('\n');
  225.         alert(`Last ${events.length} events:\n\n${formattedEvents}`);
  226.     });
  227.  
  228.     // log("Script initialized.");
  229. })();
  230.  
  231.  
Add Comment
Please, Sign In to add comment