Last active
July 12, 2020 10:36
-
-
Save cocoabox/a8416de35fd86263497d61f110235691 to your computer and use it in GitHub Desktop.
batch convert all FetLife photos to private
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
// fetLife batch change all photos to private ("friends only") | |
// | |
// usage: | |
// | |
// 1. open your first photo , URL should be https://fetlife.com/users/xxxxx/pictures/yyyyy | |
// 2. copy paste this code to your Javascript console and hit Return | |
// - a new tab (window) will open; do not close this tab | |
// - to terminate the process, close the newly-opened tab, then reload the current window/tab | |
// 3. this script doesn't work all the time; to make sure every pic is now private, repeat 1~2 again | |
// | |
(function(){ | |
const doAndWait = function (doWhat, waitSec, confirmFunc, maxRetry) { | |
if (! maxRetry) maxRetry = 10; | |
return new Promise((resolve, reject) => { | |
let attempt = 1; | |
let timer = win.setInterval(()=>{ | |
doWhat(); | |
let confirmed = ! confirmFunc || (typeof confirmFunc == 'function' && confirmFunc()); | |
if (confirmed) { | |
win.clearInterval(timer); | |
resolve(); | |
} | |
else { | |
if (++attempt > maxRetry) { | |
win.clearInterval(timer); | |
console.error(`max attempt reached while doing :`, doWhat.toString()); | |
reject(); | |
} | |
else { | |
// console.warn(`retrying ${attempt}/${maxRetry} :`, doWhat.toString()); | |
} | |
} | |
}, waitSec*1000); | |
}); | |
}; | |
const sleep = function(ms) { | |
return new Promise(reso => { | |
window.setTimeout(reso, ms); | |
}); | |
}; | |
const changeToFriendsOnly = function(win_){ | |
return new Promise((res, rej) => { | |
let win = win_ ? win_ : window; | |
let $ = win_.jQuery; | |
function gotoNext() { | |
let $nextButton = $('a[data-next-button=""]'); | |
win.location = $nextButton.attr("href"); | |
} | |
let isFriendsOnly = $("span[data-privacy-status='']").text().trim() == "Just Friends"; | |
if (isFriendsOnly) { | |
gotoNext(); | |
res({done: "already friends only"}); | |
return; | |
} | |
// first action | |
doAndWait(async ()=>{ | |
// click [edit privacy] | |
if ($('a[href="#cancel_edit_privacy"]').is(":visible")) { | |
$('a[href="#cancel_edit_privacy"]').trigger("click"); | |
await sleep(500); | |
} | |
$("a[href='#edit_privacy']").trigger("click"); | |
}, 0.5, // wait 0.5 sec | |
()=>{ | |
// then test if the "edit privacy" combobox is visible | |
return $('#picture_only_friends').length == 1 | |
&& $('#picture_only_friends:first').is(":visible") | |
}).then(()=>{ | |
// next action : | |
let $comboBox = $('#picture_only_friends'); | |
doAndWait(()=>{ | |
// set the "edit privacy" combobox value to "true" | |
$comboBox.val("true").trigger("change"); | |
}, 2, // wait 2 seconds | |
() => { | |
// confirm it's now just friends | |
let isVisible = $("span[data-privacy-status='']").is(":visible"); | |
// TODO : isVisible ^ is not always set to True; WHY? | |
let isFriendsOnly = $("span[data-privacy-status='']").text().trim() == "Just Friends"; | |
return isVisible && isFriendsOnly; | |
}).then(async ()=>{ | |
// next action: changed to just friends; wait 1 sec then go to next page | |
console.log("zzZ... before done"); | |
await sleep(1000); | |
gotoNext(); | |
res({done: "changed"}); | |
}, () => { | |
// failure | |
rej({error: "couldnt confirm it's now just friends"}); | |
}); | |
}, ()=> { | |
rej({error: "couldnt confirm combo box visibility"}); | |
}); | |
}); // Promise(res, rej) | |
}; | |
const windowIsReady = function(win) { | |
return win | |
&& win.document | |
&& (win.document.readyState === "complete" || win.document.readyState === "interactive"); | |
}; | |
const waitUntilWindowIsReady = function (win, maxTime) { | |
if (! maxTime) maxTime = 10; | |
return new Promise((reso, reje) => { | |
let elasped = 0; | |
let timer = setInterval(()=>{ | |
if (++elasped > maxTime) { | |
clearInterval(timer); | |
reje({error:"timeout"}); | |
} | |
if (windowIsReady(win)) { | |
clearInterval(timer); | |
reso({done: true}); | |
} | |
}, 1000); | |
}); | |
}; | |
const waitUntilLocationChanged = function (win, maxTime) { | |
if (! maxTime) maxTime = 10; | |
let oldLocation = window.location.href; | |
return new Promise((reso, reje) => { | |
let elasped = 0; | |
let timer = setInterval(()=>{ | |
if (++elasped > maxTime) { | |
clearInterval(timer); | |
reje({error:"timeout"}); | |
} | |
let isReady = windowIsReady(win); | |
let locationChanged = win && win.location | |
&& win.location.href !== oldLocation; | |
if (isReady && locationChanged) { | |
clearInterval(timer); | |
reso({done: true}); | |
} | |
}, 1000); | |
}); | |
}; | |
// | |
// MAIN | |
// | |
let win = window.open(window.location.href); | |
waitUntilWindowIsReady(win).then(async function () { | |
while(true) { | |
try { | |
let res = await changeToFriendsOnly(win); | |
console.log("✔", win.location.href, ":", res); | |
try { | |
await waitUntilLocationChanged(win); | |
} | |
catch (e) { | |
alert("page took forever to load"); | |
break; | |
} | |
} catch(e) { | |
alert(e.error); | |
break; | |
} | |
} | |
}, function() { | |
alert("new window never loaded ; exiting"); | |
}); | |
})(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment