Skip to content

Instantly share code, notes, and snippets.

@Pash237
Last active June 27, 2020 13:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Pash237/e08712b7faa8e32519e7677d8a81e2bc to your computer and use it in GitHub Desktop.
Save Pash237/e08712b7faa8e32519e7677d8a81e2bc to your computer and use it in GitHub Desktop.
Script to remove Nikon SnapBridge images from Google Photos
#!/usr/bin/env node
var fs = require("fs");
var argv = process.argv.slice(2);
var exec = require('await-exec');
class Photo {
constructor(id) {
this.id = id;
this.filename = null;
this.dimensions = null;
this.deleteCode = null;
}
async delete() {
console.log(" Deleting photo " + this + "...");
var command = "curl 'https://photos.google.com/_/PhotosUi/data/batchexecute?f.sid=-2686231850946325861&hl=en&soc-app=165&soc-platform=1&soc-device=1&_reqid=80372788&rt=c' -H 'x-same-domain: 1' -H 'origin: https://photos.google.com' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9,ru-RU;q=0.8,ru;q=0.7' -H 'x-goog-ext-176109192-jspb: [null,null,[[\"EzwWhf\",1,1,[0.20000001043081284],null,[]]]]' -H 'cookie: CONSENT=YES+FR.en+20151011-09-0; HSID=AF5kEZip3UacS4fNo; SSID=Ajji32Jam3WKoRPa3; APISID=M32_NqQ73O_pSEjB/AQWSkBYOl_FBhVCWe; SAPISID=h6IKYTp000m0PG0B/AmSjlvtqhdioliMEn; OTZ=4355764_44_44_123780_40_436260; SID=AwbTSPKFrOrwQswuCOzXLHLzPgCp6bR0LpiFJlJP_wOE9kWP-BjrX_Kl-YmRUUsbKNZdFg.; NID=129=R_lh8fwsmdNhQTyP0temtnfGbdu11mUPYYkMlC_nnSszqp8qgHbPfVqqfEUJv3uBaVIzsPylNwlr4U0uOw7zIvQ5f3ZfuO9-jrfvrpqSPHCOzIxq8qrJE_7aIbX38RYePG59g49aafMr9-VuP8zhXbwvHPU5VBwyIMjfwriNLYGUqkgMnUVZEWBprsFUWz9CS5bOBETLqOThL6VUG6z-zhmzAhJY9a_dh0dQNL4qYSUsNenj4FIARLrSvzsyBMlmhianARxWXnUyHfkmpCh9xG-WI3vUN4NMtS3_Qfc5OjAhiPWb_1BICFwLpnBg2nhK4gNykQ1ZeaRbO8nSELEVNmlHSpVS23KX4q6NuSxnTHZApbnzm-QHxvbdVRUIxE9FBYChliYjEnSnyUzQK0Dj_At0Xc3T98ueTG1wccYRlHH7MYhLRg32wMQk; 1P_JAR=2018-4-30-18; SIDCC=AEfoLeYFFw-lQUNc7KI0oHyacSZAcfORSETjg4qvvhd3GqTuiLDkZK1YuvjROqKUBd-pyOfCLik' -H 'x-client-data: CI62yQEIpLbJAQjBtskBCKmdygEIuZ3KAQien8oBCKijygEYkqPKAQ==' -H 'x-compress: null' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' -H 'content-type: application/x-www-form-urlencoded;charset=UTF-8' -H 'accept: */*' -H 'referer: https://photos.google.com/' -H 'authority: photos.google.com' --data 'f.req=%5B%5B%5B%22XwAOJf%22%2C%22%5B%5B%5D%2C1%2C%5B%5C%22AWXhZcYaI-rilyLqt422BDVDI_0%5C%22%5D%2C3%2Cnull%2C%5B%5D%2C%5B%5D%5D%22%2Cnull%2C%22generic%22%5D%5D%5D&at=AI4RNLNRGKIvrfDp5re0DWJLmVYJ%3A1525108386206&' --compressed";
command = command.replace("AWXhZcYaI-rilyLqt422BDVDI_0", this.deleteCode);
await exec(command);
}
toString() {
return "" + this.id + " " + this.dimensions.width + "×" + this.dimensions.height + " delete code: " + this.deleteCode;
}
isNikonSnapBridgePhoto() {
return (this.dimensions.width == 1620 && this.dimensions.height == 1080) || (this.dimensions.width == 1080 && this.dimensions.height == 1620);
}
}
function lastMatches(regex, string)
{
var lastMatches = null;
while ((matches = regex.exec(string)) !== null) {
lastMatches = matches
}
return lastMatches
}
async function getPhoto(photoId) {
console.log("Getting photo " + photoId + "...");
try {
var command = "curl 'https://photos.google.com/_/PhotosUi/data/batchexecute?f.sid=-2686231850946325861&hl=en&soc-app=165&soc-platform=1&soc-device=1&_reqid=78972788&rt=c' -H 'x-same-domain: 1' -H 'origin: https://photos.google.com' -H 'accept-encoding: gzip, deflate, br' -H 'accept-language: en-US,en;q=0.9,ru-RU;q=0.8,ru;q=0.7' -H 'x-goog-ext-176109192-jspb: [null,null,[[\"EzwWhf\",1,1,[0.5000000232830644],1,[]]]]' -H 'cookie: CONSENT=YES+FR.en+20151011-09-0; HSID=AF5kEZip3UacS4fNo; SSID=Ajji32Jam3WKoRPa3; APISID=M32_NqQ73O_pSEjB/AQWSkBYOl_FBhVCWe; SAPISID=h6IKYTp000m0PG0B/AmSjlvtqhdioliMEn; OTZ=4355764_44_44_123780_40_436260; SID=AwbTSPKFrOrwQswuCOzXLHLzPgCp6bR0LpiFJlJP_wOE9kWP-BjrX_Kl-YmRUUsbKNZdFg.; NID=129=R_lh8fwsmdNhQTyP0temtnfGbdu11mUPYYkMlC_nnSszqp8qgHbPfVqqfEUJv3uBaVIzsPylNwlr4U0uOw7zIvQ5f3ZfuO9-jrfvrpqSPHCOzIxq8qrJE_7aIbX38RYePG59g49aafMr9-VuP8zhXbwvHPU5VBwyIMjfwriNLYGUqkgMnUVZEWBprsFUWz9CS5bOBETLqOThL6VUG6z-zhmzAhJY9a_dh0dQNL4qYSUsNenj4FIARLrSvzsyBMlmhianARxWXnUyHfkmpCh9xG-WI3vUN4NMtS3_Qfc5OjAhiPWb_1BICFwLpnBg2nhK4gNykQ1ZeaRbO8nSELEVNmlHSpVS23KX4q6NuSxnTHZApbnzm-QHxvbdVRUIxE9FBYChliYjEnSnyUzQK0Dj_At0Xc3T98ueTG1wccYRlHH7MYhLRg32wMQk; 1P_JAR=2018-4-30-17; SIDCC=AEfoLeZlj_PqMX931Cp_HtZTzs2YG8Pjhf8WfX_dGmHwB-c8fqGMVyf2gdJkQtbOTX0fjKw_HVg' -H 'x-client-data: CI62yQEIpLbJAQjBtskBCKmdygEIuZ3KAQien8oBCKijygEYkqPKAQ==' -H 'x-compress: null' -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36' -H 'content-type: application/x-www-form-urlencoded;charset=UTF-8' -H 'accept: */*' -H 'referer: https://photos.google.com/' -H 'authority: photos.google.com' --data 'f.req=%5B%5B%5B%22fDcn4b%22%2C%22%5B%5C%22AF1QipPhOtviTxZigZwseI_vISdYynYKjtZBF_ABHYjT%5C%22%2C2%5D%22%2Cnull%2C%22DMzJyf%3A0%7Cresponse%22%5D%2C%5B%22EX1ySd%22%2C%22%5B%5D%22%2Cnull%2C%22DMzJyf%3A0%7CDm%22%5D%5D%5D&at=AI4RNLNRGKIvrfDp5re0DWJLmVYJ%3A1525108386206&' --compressed";
command = command.replace("AF1QipPhOtviTxZigZwseI_vISdYynYKjtZBF_ABHYjT", photoId);
var stdout = (await exec(command)).stdout;
//console.log(stdout);
} catch(e) {
console.log(" Error getting photo: " + e);
return null;
}
try {
if (stdout.includes("NotFoundException") || stdout.includes("Code: NOT_FOUND") || stdout.includes("\"wrb.fr\",\"fDcn4b\",null,null,null,[5]")) {
console.log(" Photo not found.");
return null;
}
var dimensionMatches = lastMatches(/\[\[\\".+?,.+?,.+?,.+?,.+?,.+?,(\d+),(\d+).+/g, stdout);
var deleteCode = lastMatches(/,\\"([\w-]{11,40}?)\\"/g, stdout)[1];
var filename = lastMatches(/\[\[\\".+?,.+?,\\"(.+?)\\",.+/g, stdout)[1];
var photo = new Photo(photoId);
photo.dimensions = {
width: dimensionMatches[1],
height: dimensionMatches[2]
};
photo.deleteCode = deleteCode;
photo.filename = filename;
return photo
} catch(e) {
console.log(" Error getting photo: " + e);
console.log(stdout);
return null;
}
}
function getPhotos() {
//file with photo ids — content returned by Google Photo's batchexecute "lcxiM" requests or just HTML
var photosFile = fs.readFileSync("photos.txt", "utf8");
var regex = /(AF[\w-]{42}?)"/g;
var match;
var photos = [];
while (match = regex.exec(photosFile)) {
photos.push(match[1])
}
photos = Array.from(new Set(photos));
return photos;
}
async function main() {
var photos = getPhotos();
console.log("Found " + photos.length + " photos")
for (let photoId of photos) {
photo = await getPhoto(photoId);
if (photo == null) {
continue;
}
console.log(" " + photo.filename + " " + photo.dimensions.width + "×" + photo.dimensions.height);
if (photo.isNikonSnapBridgePhoto()) {
photo.delete();
}
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment