-
-
Save NHZEX/b273163f6981c452f72bbb06be3efd77 to your computer and use it in GitHub Desktop.
Tabs Outliner: kill duplicate windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function scanDupes() { | |
let dupes = {}; | |
for (const nod of treeView.treeModel.currentSession_rootNode.subnodes) { // only check root level nodes, don't do complex recursive checks for "sub-roots" | |
const strbld = []; | |
nodeterate(nod, strbld); | |
const crashDetectedDate = nod.chromeWindowObj.crashDetectedDate | |
? new Date(nod.chromeWindowObj.crashDetectedDate).toLocaleString() | |
: 'not crash'; | |
console.log(nod.id, crashDetectedDate, nod); | |
} | |
} | |
await scanDupes(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// cyrb53 stolen from https://stackoverflow.com/a/52171480 | |
const cyrb53 = function(str, seed = 0) { | |
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed; | |
for (let i = 0, ch; i < str.length; i++) { | |
ch = str.charCodeAt(i); | |
h1 = Math.imul(h1 ^ ch, 2654435761); | |
h2 = Math.imul(h2 ^ ch, 1597334677); | |
} | |
h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909); | |
h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909); | |
return 4294967296 * (2097151 & h2) + (h1>>>0); | |
}; | |
async function digestMessage(message) { | |
const msgUint8 = new TextEncoder().encode(message); // 编码为(utf-8)Uint8Array | |
const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // 计算消息的哈希值 | |
const hashArray = Array.from(new Uint8Array(hashBuffer)); // 将缓冲区转换为字节数组 | |
const hashHex = hashArray | |
.map((b) => b.toString(16).padStart(2, "0")) | |
.join(""); // 将字节数组转换为十六进制字符串 | |
return hashHex; | |
} | |
function nodeterate(currentNode, stringBuilder) { | |
if (currentNode.type == "separatorline") stringBuilder.push("separatorline"); | |
else if (currentNode.type == "textnote") stringBuilder.push(`textnote ${currentNode?.persistentData?.note}`); | |
else if (currentNode.type == "win" || currentNode.type == "savedwin") stringBuilder.push("window"); | |
else stringBuilder.push(currentNode.getHref()); | |
for (const sn of currentNode.subnodes) { | |
nodeterate(sn, stringBuilder); | |
} | |
} | |
function nukeNodeArray(nodeArray) { | |
const DUMMY = false; | |
for (const currentNode of nodeArray) { | |
if (currentNode.type !== "win" && currentNode.type !== "savedwin") { | |
console.log("this node should not have almost died:"); | |
console.log(currentNode); | |
continue; | |
} | |
if (DUMMY) { | |
console.log("would now kill:"); | |
console.log(currentNode); | |
} else { | |
console.log("removing node:"); | |
const crashDetectedDate = currentNode.chromeWindowObj.crashDetectedDate | |
? new Date(currentNode.chromeWindowObj.crashDetectedDate).toLocaleString() | |
: 'not crash'; | |
console.log(currentNode.id, crashDetectedDate, currentNode); | |
currentNode.removeOwnTreeFromParent(); | |
} | |
} | |
} | |
async function killDupes(killActive = false) { | |
let dupes = {}; | |
for (const nod of treeView.treeModel.currentSession_rootNode.subnodes) { // only check root level nodes, don't do complex recursive checks for "sub-roots" | |
const strbld = []; | |
nodeterate(nod, strbld); | |
// const hash = cyrb53(strbld.join()); | |
const hash = await digestMessage(strbld.join()) | |
if (!dupes.hasOwnProperty(hash)) { | |
dupes[hash] = [] | |
} | |
dupes[hash].push(nod); | |
} | |
console.log('total nodes', treeView.treeModel.currentSession_rootNode.subnodes.length); | |
console.log('total hash', Object.keys(dupes).length) | |
const summary = Object.keys(dupes).map((key) => [key, dupes[key].length, dupes[key]]); | |
console.log('hash nodes size: ', summary); | |
// summary.forEach((v) => { | |
// console.log(v[0], v[1]) | |
// }) | |
for (const duperino of Object.values(dupes)) { | |
if (duperino.length < 2) continue; | |
const inactiveNodes = duperino.filter(x => x.type !== "win"); // keep loaded window(s) alive | |
if (inactiveNodes.length < duperino.length) { | |
if ((duperino.length - inactiveNodes.length) > 1) { | |
console.log("WARNING: duplicate active windows ->"); | |
for (const loadedWindow of duperino.filter(x => x.type == "win")) console.log(loadedWindow); | |
} | |
nukeNodeArray(inactiveNodes); | |
if (killActive) nukeNodeArray(duperino.filter(x => x.type == "win").slice(1)); | |
} | |
else { // keep oldest | |
nukeNodeArray(duperino.slice(1)); | |
} | |
} | |
} | |
await killDupes(); | |
// to also prune duplicate *loaded* windows down to a single one, try: killDupes(true); | |
// note that this will disregard how many subnodes are loaded! |
Tabs Outliner 在哪里存储自己的数据以及如何从备份恢复或将树移动到新电脑。
https://groups.google.com/g/tabs-outliner-support-group/c/eKubL9Iw230
AppData\Local\Google\Chrome\User Data\Default\IndexedDB\chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb\
原始内容
It is store own data in:
Windows Vista, Windows 7 (Chrome Dev, Beta, Prod Channels):
C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb\
Windows Vista, Windows 7 (Chrome Canary Build):
C:\Users\%USERNAME%\AppData\Local\Google\Chrome SxS\User Data\Default\IndexedDB\chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb\
OSX:
/Users/<username>/Library/Application Support/Google/Chrome/Default/IndexedDB/chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb/
Ubuntu - Chrome:
/home/<username>/.config/google-chrome/Default/IndexedDB/chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb/
Ubuntu - Chromium:
/home/<username>/.config/chromium/Default/IndexedDB/chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb
Take note the /Default/ subfolder, if you have more then one Chrome profile and use it with TabsOutliner there will be different folder name. Typically Chrome name all the others profiles folders as: "Profile 1", "Profile 3", "Profile 9" .....
An easy way to know the correct path to the profile of the current Chrome window is to enter in its address-bar next url:
chrome://version/
The path will be displayed in the "Profile path:" section
Or you can search for a "chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb" in a filesystem to find all the relevant folders in all the profiles.
I highly recommend backup every day this folder and keep several older versions!
Use the standard system backup, or better install some of the backup or synchronize service to copy this folder in to the Cloud. For example SugarSync can backup any folder on a PC to a cloud, not only own default folder, and it is goes with a completely enough free plan to backup all the Tabs Outliner data.
You can also delete this folder if Tabs Outliner does not start for some reason - for example when database become corrupted after some bluescreen (of course you will lose all you data from tree in this case, this is why i recommend keep several older backups - that's not true anymore), by deleting files int TO IndexedDb folder you will actually force restoration of last good known saved data from the emergence backup (Tabs Outliner perform and maintain several backups of the main database using completely different technology), yet maintain several older backups of the IndexedDB folder is strongly advised anyway. As this is the main recommended way how you can restore you tree to other pc or after serious crash.
UPDATE:
IndexedDB storage mentioned in this post continue to be a primary data storage. Yet in additional to it there is also file backups of data for 9 last days (every day a backup) and for 6 last hours (a backup for every hour). Unfortunately - not in Chrome 18 (default Ubunta browser) - please upgrade you Chromium to latest stable build!
There was several really strange incidents with data lost, mostly on unofficial Chrome builds - portable or from third parties (Co0lNova browser) and several incidents of database corruptions, and one reaffirmed incident when database was sometime stop work at all, until Chrome restart. So, to minimize data loss in future on such events (really rare, yet anyway) latest version not only make periodic automatic backups but also duplicate all data on every save in one additional place. If data from Database is corrupted or not look like the latest saved data the data from this emergency place will be utilized upon restart.
This might affect your ability to restore your tree using IndexedDB files backup - as the data from IndexedDB will be ignored (as they will not look like something that TO successfully saved last time), and instead data from emergency source will be utilized.
So to restore you tree from backup, or to move you data to an other PC you must first clear this emergency storage in a freshly installed version of the TO. The simplest way to do this is by uninstalling the Tabs Outliner from a PC where you plan to restore you tree. Note that you must uninstall the TO, not simple disable.
The procedure to transfer you tree (to a new PC for example, or to other profile on a same PC) is next:
0. Save the Tabs Outliner window on a PC where you plan to restore your tree to a html file (by Ctrl-S), if you already have some useful data in it.
1. Delete (uninstall) the Tabs Outliner from a target Chrome (use this link: chrome://chrome/extensions to open list of extensions)
Note that you must delete the Tabs Outliner by delete icon, not simple disable it, it is important!
2. You need to find this directory in you old backup (in the Chrome installation from which you plan to copy the data):
C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb\
(if you have only one Chrome profile)
3. And copy its content to a similar directory in the target Chrome.
Basicaly you must copy the "chrome-extension_eggkanocgddhmamlbiijnphhppkpkmkl_0.indexeddb.leveldb" subfolder to the
C:\Users\%USERNAME%\AppData\Local\Google\Chrome\User Data\Default\IndexedDB\
on the target PC (or target profile, but in this case there will be not the "Default" name).
4. Install again Tabs Outliner from the Chrome Web Store - it will utilize the existed in profile database
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
引用: https://tabsoutliner.userecho.com/communities/1/topics/77-request-option-to-remove-all-crashed-sessions-windows-and-tabs-in-one-click