// ==UserScript==
// @name 0xFFFE.Genfluence.Archivist (prod)
// @match https://www.genfluence.ai/*
// @description Add Download All and Delete All features to Genfluence.
// @license MIT
// @version 1.7
// @namespace https://greasyfork.org/users/1309423
// ==/UserScript==
(function() {
const BASE_URL = "https://www.genfluence.ai";
const NAVBAR_ELEM_SELECTOR = ".drawer-content > .navbar";
const CASH_ELEM_SELECTOR = ".drawer-content .text-xl";
const MAIN_ELEM_SELECTOR = "main#skip";
const TOGGLE_ELEM_ID = "xga-settings-toggle";
const PANEL_ELEM_ID = "xga-settings-panel";
const LOGS_ELEM_ID = "xga-settings-logs";
const TOGGLE_ELEM_SELECTOR = "#" + TOGGLE_ELEM_ID;
const SLEEP_TIME_SETUP = 1000;
const SLEEP_TIME_IMAGE = 100;
const SLEEP_TIME_PAGINATION = 500;
let setupTimeout = 60;
let navBarElem = null;
let toggleButtonElem = null;
let downloadButtonElem = null;
let cashElem = null;
let logsElem = null;
let deleteButtonElem = null;
let mainElem = null;
let panelElem = null;
let requestStop = false;
async function sleep(duration) {
await new Promise(r => setTimeout(r, duration));
}
async function deleteImage(imageId, retry) {
if (!retry) {
retry = 0;
}
if (retry > 2) {
return null;
}
try {
let result = await fetch(BASE_URL + "/api/images/delete_images", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3",
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
},
"referrer": BASE_URL + "/create",
"body": "{\"imageIds\":[\"" + imageId + "\"]}",
"method": "POST",
"mode": "cors"
});
return result;
} catch (error) {
await sleep(2000);
return deleteImage(imageId, retry + 1);
}
}
async function downloadImage(url, name) {
let link = document.createElement("a");
let blob = await fetch(url).then(r => r.blob());
let file = new Blob([blob], {
type: 'application/octet-stream'
});
link.href = URL.createObjectURL(file);
link.download = name;
link.click();
URL.revokeObjectURL(link.href);
}
async function fetchImages(position, retry) {
if (!retry) {
retry = 0;
}
if (retry > 2) {
return null;
}
let positionStart = position;
let positionEnd = position + 19;
try {
let result = await fetch(BASE_URL + "/api/images/get_images", {
"credentials": "include",
"headers": {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0",
"Accept": "application/json, text/plain, */*",
"Accept-Language": "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3",
"Content-Type": "application/json",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-origin",
"Pragma": "no-cache",
"Cache-Control": "no-cache"
},
"referrer": BASE_URL + "/create",
"body": "{\"by\":\"history\",\"projectId\":\"\",\"from\":" + positionStart + ",\"to\":" + positionEnd + "}",
"method": "POST",
"mode": "cors"
});
if (result.ok) {
// If the response is successful, process the JSON
return result.json();
} else {
console.error(`Received error: ${result.status}. Retrying...`);
await sleep(3000); // Wait before retrying
return fetchImages(position, retry + 1);
}
} catch (error) {
await sleep(3000);
return fetchImages(position, retry + 1);
}
}
function toggleSettings() {
// if settings panel is visible => hide
// if settings panel is invisible => show
if (panelElem.dataset.enabled === "true") {
panelElem.dataset.enabled = "false";
panelElem.style.top = "-50vh";
} else {
panelElem.dataset.enabled = "true";
panelElem.style.top = "50vh";
}
}
async function cancelAction() {
console.log("cancelAction-begin");
requestStop = true;
console.log("cancelAction-end");
}
async function runDelete() {
console.log("runDelete-begin");
let initRes = await fetchImages(0);
let imagesTotal = initRes.totalRecords;
let deleteButtonValueBackup = deleteButtonElem.value;
requestStop = false;
downloadButtonElem.disabled = true;
deleteButtonElem.value = "❌ Cancel delete";
deleteButtonElem.removeEventListener('click', runDelete);
deleteButtonElem.addEventListener('click', cancelAction);
mainElem.style.pointerEvents = "none";
mainElem.style.opacity = "25%";
for (let offsetCur = 0; offsetCur < imagesTotal; offsetCur += 20) {
let offsetRes = await fetchImages(0); //
if (!offsetRes) { break; }
if (!offsetRes.images) { break; }
if (requestStop) { break; }
await sleep(SLEEP_TIME_PAGINATION);
for (let imageCur = 0; imageCur < 20; imageCur += 1) {
let image = offsetRes.images[imageCur];
if (!image) { continue; }
if (requestStop) { break; }
deleteImage(image.id);
let pcent = Math.floor(10000 * (imageCur + offsetCur) / imagesTotal) / 100;
logsElem.textContent = `🗑️ Deleting... (${pcent.toFixed(2)}%)`;
await sleep(SLEEP_TIME_IMAGE);
}
}
deleteButtonElem.value = deleteButtonValueBackup;
logsElem.textContent = "";
deleteButtonElem.removeEventListener('click', cancelAction);
deleteButtonElem.addEventListener('click', runDelete);
mainElem.style.pointerEvents = "auto";
mainElem.style.opacity = "100%";
downloadButtonElem.disabled = false;
console.log("runDelete-end");
}
async function runDownload() {
console.log("runDownload-begin");
let initRes = await fetchImages(0);
let imagesTotal = initRes.totalRecords;
let downloadButtonValueBackup = downloadButtonElem.value;
requestStop = false;
deleteButtonElem.disabled = true;
downloadButtonElem.value = "❌ Cancel download";
downloadButtonElem.removeEventListener('click', runDownload);
downloadButtonElem.addEventListener('click', cancelAction);
mainElem.style.pointerEvents = "none";
mainElem.style.opacity = "25%";
for (let offsetCur = 0; offsetCur < imagesTotal; offsetCur += 20) {
let offsetRes = await fetchImages(offsetCur); //
if (!offsetRes) { break; }
if (!offsetRes.images) { break; }
if (requestStop) { break; }
for (let imageCur = 0; imageCur < 20; imageCur += 1) {
if (imageCur >= offsetRes.images.length) { break; }
if (requestStop) { break; }
let pcent = Math.floor(10000 * (imageCur + offsetCur) / imagesTotal) / 100;
// let globalIndex = (imageCur + offsetCur);
// console.log(`offset = ${globalIndex} / ${imagesTotal} (${pcent.toFixed(2)}%)`);
let image = offsetRes.images[imageCur];
if (!image) { continue; }
let imageUrl = image.imgUrl;
let imageTimestamp = image.createdAt.substring(0, 19).replace(/[:-]/g,"");
let imageName = `anydream--${imageTimestamp}--${image.id}.${image.extention}`;
downloadImage(imageUrl, imageName);
logsElem.textContent = `💾 Downloading... (${pcent.toFixed(2)}%)`;
await sleep(SLEEP_TIME_IMAGE);
}
await sleep(SLEEP_TIME_PAGINATION);
}
downloadButtonElem.value = downloadButtonValueBackup;
logsElem.textContent = "";
downloadButtonElem.removeEventListener('click', cancelAction);
downloadButtonElem.addEventListener('click', runDownload);
mainElem.style.pointerEvents = "auto";
mainElem.style.opacity = "100%";
deleteButtonElem.disabled = false;
console.log("runDownload-end");
}
function setup() {
console.log("setup-begin");
let bodyElem = document.querySelector("body");
navBarElem = document.querySelector(NAVBAR_ELEM_SELECTOR);
mainElem = document.querySelector(MAIN_ELEM_SELECTOR);
cashElem = document.querySelector(CASH_ELEM_SELECTOR);
console.log(navBarElem);
console.log(mainElem);
console.log(cashElem);
// Add transition on elements
mainElem.style.transition = "all 0.25s ease-in-out";
toggleButtonElem = document.createElement('input');
toggleButtonElem.id = TOGGLE_ELEM_ID;
toggleButtonElem.type = "button";
toggleButtonElem.value = "🛠️";
toggleButtonElem.style.marginRight = "0.5em";
toggleButtonElem.addEventListener('click', toggleSettings);
panelElem = document.createElement('div');
panelElem.id = PANEL_ELEM_ID;
panelElem.style.boxShadow = "0px 0px 50px rgb(20,184,166,0.25)";
panelElem.style.width = "min(80vw, 400px)";
panelElem.dataset.enabled = "false";
panelElem.style.height = "min(30vh, 400px)";
panelElem.style.position = "absolute";
panelElem.style.top = "-50vh";
panelElem.style.left = "50vw";
panelElem.style.backgroundColor = "#0f172a";
panelElem.style.transform = "translate(-50%,-50%)";
panelElem.style.display = "flex";
panelElem.style.justifyContent= "center";
panelElem.style.flexDirection= "column";
panelElem.style.alignItems= "stretch";
panelElem.style.gap = "1em";
panelElem.style.padding = "60px 20px 20px 20px";
panelElem.style.transition = "all 0.25s ease-in-out";
let panelCloseElem = document.createElement('input');
panelCloseElem.type = "button";
panelCloseElem.value = "[Close]";
panelCloseElem.style.position = "absolute";
panelCloseElem.style.top = "10px";
panelCloseElem.style.right = "10px";
panelCloseElem.addEventListener('click', toggleSettings);
panelElem.appendChild(panelCloseElem);
logsElem = document.createElement('div');
logsElem.id = LOGS_ELEM_ID;
logsElem.dataset.enabled = "false";
logsElem.style.display = "block";
//logsElem.style.border = "1px solid white";
logsElem.style.backgroundColor = "rgb(55 65 81/var(--tw-bg-opacity))";
logsElem.style.flexBasis = "200px";
logsElem.style.padding = "5px";
panelElem.appendChild(logsElem);
downloadButtonElem = document.createElement('input');
downloadButtonElem.type = "button";
downloadButtonElem.id = "xga-download-all";
downloadButtonElem.value = "💾 Download all";
downloadButtonElem.style.border = "2px solid #14B8A6";
downloadButtonElem.style.backgroundColor = "#333";
downloadButtonElem.style.borderRadius = "5px";
downloadButtonElem.style.padding = "0.25em 1em";
downloadButtonElem.style.marginRight = "0.5em";
downloadButtonElem.style.width = "100%";
downloadButtonElem.addEventListener('click', runDownload);
panelElem.appendChild(downloadButtonElem);
deleteButtonElem = document.createElement('input');
deleteButtonElem.type = "button";
deleteButtonElem.id = "xga-delete-all";
deleteButtonElem.value = "🗑️ Delete all";
deleteButtonElem.style.border = "2px solid darkred";
deleteButtonElem.style.backgroundColor = "#333";
deleteButtonElem.style.borderRadius = "5px";
deleteButtonElem.style.padding = "0.25em 1em";
deleteButtonElem.style.width = "100%";
deleteButtonElem.addEventListener('click', runDelete);
panelElem.appendChild(deleteButtonElem);
bodyElem.appendChild(panelElem);
bodyElem.style.position = "relative";
cashElem.parentNode.parentNode.insertBefore( toggleButtonElem, cashElem.parentNode);
console.log("setup-end");
}
function trySetup() {
console.log("trysetup-begin " + setupTimeout);
navBarElem = document.querySelector(NAVBAR_ELEM_SELECTOR);
toggleButtonElem = document.querySelector(TOGGLE_ELEM_SELECTOR);
// console.log(navBarElem);
// console.log(downloadButtonElem);
if (navBarElem && !toggleButtonElem) {
// ready for setup
setup();
} else if (setupTimeout > 0) {
setupTimeout = setupTimeout - 1;
setTimeout(trySetup, SLEEP_TIME_SETUP);
}
console.log("trysetup-end");
}
setTimeout(trySetup, SLEEP_TIME_SETUP);
}());