Skip to content

Commit 87bac50

Browse files
committed
pinterest auto save all board
1 parent 8ef17e4 commit 87bac50

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
console.log("pinterest-auto-save loaded");
2+
3+
const cached = getCache();
4+
let allBoardBtn,
5+
isAutoClick = false;
6+
7+
const saveAllBtn = document.createElement("button");
8+
saveAllBtn.innerText = "Save to All boards";
9+
saveAllBtn.onclick = async () => {
10+
if (!isAutoClick) setCache("lastSaveIndex", 0);
11+
12+
savingText.innerText =
13+
"Auto Saving to board " + ((cached?.lastSaveIndex || 0) + 1) + " ...";
14+
document.body.appendChild(overlay);
15+
16+
allBoardBtn?.click?.();
17+
18+
const selector = '[data-test-id*="board-row-"]';
19+
20+
await waitForElements(selector);
21+
const boardRows = Array.from(document.querySelectorAll(selector));
22+
23+
// remove all btns before "All boards" div
24+
const beforeAllBoard = [];
25+
while (boardRows.length > 0) {
26+
try {
27+
const next =
28+
boardRows[0].parentElement.parentElement.parentElement
29+
.nextElementSibling;
30+
const nextIsAllboard = next.textContent == "All boards";
31+
beforeAllBoard.push(boardRows.shift());
32+
if (nextIsAllboard) break;
33+
} catch (e) {
34+
console.log(e);
35+
}
36+
}
37+
38+
onElementsAdded(selector, (nodes) => {
39+
for (let node of nodes) {
40+
if (boardRows.includes(node) || beforeAllBoard.includes(node)) continue;
41+
boardRows.push(node);
42+
}
43+
});
44+
45+
const targetIndex = cached?.lastSaveIndex || 0;
46+
47+
while (!boardRows[targetIndex]) {
48+
if (targetIndex > boardRows.length - 1) {
49+
focusTo(boardRows[boardRows.length - 1]);
50+
console.log("wait for load more...", boardRows.length, targetIndex);
51+
await sleep(1000);
52+
continue;
53+
}
54+
}
55+
56+
const cur = boardRows[targetIndex];
57+
focusTo(cur);
58+
await sleep(1000);
59+
console.log("row to click", cur);
60+
61+
// if save button appear -> click it
62+
const saveBtn = cur.querySelector('button[aria-label="save button"]');
63+
if (saveBtn) {
64+
clickSave(saveBtn, targetIndex);
65+
}
66+
// else -> click row
67+
else {
68+
cur.click();
69+
70+
let done = false;
71+
while (!done) {
72+
const selector = `[data-test-id="${cur
73+
.getAttribute("data-test-id")
74+
.replaceAll('"', '\\"')}"]`;
75+
console.log("selector", selector);
76+
const nodes = await waitForElements(selector);
77+
const target = Array.from(nodes).find(
78+
(e) => cur != e && !beforeAllBoard.includes(e)
79+
);
80+
if (!target) {
81+
console.log("target not found, wait for load more...");
82+
await sleep(1000);
83+
continue;
84+
}
85+
focusTo(target);
86+
await sleep(1000);
87+
const saveBtn = target.querySelector('button[aria-label="save button"]');
88+
if (saveBtn) {
89+
clickSave(saveBtn, targetIndex);
90+
done = true;
91+
}
92+
}
93+
}
94+
};
95+
96+
const overlay = document.createElement("div");
97+
overlay.id = "pinterest-auto-save-overlay";
98+
99+
const savingText = document.createElement("h1");
100+
101+
const stopBtn = document.createElement("button");
102+
stopBtn.innerText = "Stop auto save";
103+
stopBtn.onclick = () => {
104+
isAutoClick = false;
105+
setCache("lastSaveIndex", 0);
106+
location.reload();
107+
};
108+
overlay.appendChild(savingText);
109+
overlay.appendChild(stopBtn);
110+
111+
window.onload = async () => {
112+
console.log("cached", cached);
113+
// auto click if have last save
114+
if (cached?.lastSaveIndex) {
115+
await sleep(2000);
116+
isAutoClick = true;
117+
saveAllBtn.click();
118+
}
119+
};
120+
121+
onElementsAdded(
122+
'[data-test-id="closeup-body"] button[aria-label="Select a board you want to save to"]',
123+
(nodes) => {
124+
if (nodes[0]) {
125+
console.log("add save all btn", nodes[0]);
126+
allBoardBtn = nodes[0];
127+
saveAllBtn.parentElement?.removeChild?.(saveAllBtn);
128+
allBoardBtn.parentElement.appendChild(saveAllBtn);
129+
}
130+
}
131+
);
132+
133+
const cacheKey = "pinterest-auto-save";
134+
function setCache(key, value) {
135+
const cached = JSON.parse(localStorage.getItem(cacheKey) || "{}");
136+
cached[key] = value;
137+
localStorage.setItem(cacheKey, JSON.stringify(cached));
138+
return cached;
139+
}
140+
141+
function getCache(key) {
142+
const cached = JSON.parse(
143+
localStorage.getItem("pinterest-auto-save") || "{}"
144+
);
145+
if (key) return cached[key];
146+
return cached;
147+
}
148+
149+
async function clickSave(btn, curIndex) {
150+
btn.click();
151+
152+
// save progress to local storage
153+
setCache("lastSaveIndex", curIndex + 1);
154+
155+
// reload page
156+
await sleep(3000);
157+
location.reload();
158+
}
159+
160+
function focusTo(ele) {
161+
console.log("focus to", ele);
162+
if (!ele) return;
163+
ele.dispatchEvent(
164+
new MouseEvent("mouseover", {
165+
view: window,
166+
bubbles: true,
167+
cancelable: true,
168+
})
169+
);
170+
ele.scrollIntoView({ behavior: "smooth", block: "center" });
171+
}
172+
173+
function sleep(ms) {
174+
return new Promise((resolve) => setTimeout(resolve, ms));
175+
}
176+
177+
function waitForElements(selector) {
178+
return new Promise((resolve, reject) => {
179+
onElementsAdded(selector, resolve, true);
180+
});
181+
}
182+
183+
function onElementsAdded(selector, callback, once) {
184+
let nodes = document.querySelectorAll(selector);
185+
if (nodes?.length) {
186+
callback(nodes);
187+
if (once) return;
188+
}
189+
190+
const observer = new MutationObserver((mutations) => {
191+
mutations.forEach((mutation) => {
192+
if (!mutation.addedNodes) return;
193+
194+
for (let node of mutation.addedNodes) {
195+
if (node.nodeType != 1) continue; // only process Node.ELEMENT_NODE
196+
197+
let n = node.matches(selector)
198+
? [node]
199+
: Array.from(node.querySelectorAll(selector));
200+
201+
if (n?.length) {
202+
callback(n);
203+
if (once) observer.disconnect();
204+
}
205+
}
206+
});
207+
});
208+
209+
observer.observe(document, {
210+
childList: true,
211+
subtree: true,
212+
attributes: false,
213+
characterData: false,
214+
});
215+
216+
// return disconnect function
217+
return () => observer.disconnect();
218+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Pinterest Auto Save",
4+
"version": "1.0",
5+
"permissions": ["tabs", "scripting"],
6+
"host_permissions": ["https://www.pinterest.com/*"],
7+
"content_scripts": [
8+
{
9+
"matches": ["https://www.pinterest.com/*"],
10+
"js": ["content.js"],
11+
"css": ["style.css"],
12+
"run_at": "document_start"
13+
}
14+
]
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pinterest-auto-save-overlay {
2+
display: flex;
3+
flex-direction: column;
4+
justify-content: center;
5+
align-items: center;
6+
7+
position: fixed;
8+
width: 100vw;
9+
height: 100vh;
10+
top: 0;
11+
left: 0;
12+
background: #eee6;
13+
14+
z-index: 999999999999;
15+
}

0 commit comments

Comments
 (0)