Skip to content

Instantly share code, notes, and snippets.

@pierrel
Forked from daniele-rapagnani/dump_google_spaces.js
Last active March 19, 2017 23:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pierrel/53d5dd1f23517f01a0337651f7fb751e to your computer and use it in GitHub Desktop.
Save pierrel/53d5dd1f23517f01a0337651f7fb751e to your computer and use it in GitHub Desktop.
Quick and dirty snippet for exporting links from Google Spaces

TODO

  1. merge posts with resolution of urls
  2. write times param into wait function (to test only some posts)
/**
* This dirty, dirty script, allows you to export links from google spaces
* so that you don't have to do it manually before April 17th.
* I could not find any official way to do this so I quickly threw this script
* together.
*
* USAGE:
* 1. Place yourself on the space page you want to export
* (such as: https://spaces.google.com/space/XXXXXXXXXXXXXXX)
*
* 2. Refresh the page (don't forget to do this)
*
* 3. Paste the following script in the console run it
*
* 4. Choose a location for the resulting file
*
* I've only tested this in Chrome 50 only.
* It probably needs a modern browser given that I've used ES6 Promises.
*/
function waitForCb(selector, resolve, action, times) {
console.log("waiting");
const newTimes = times != null ? times-1 : null;
if (newTimes == -1) {
resolve();
} else if (!document.querySelector(selector)) {
if (action) { action(selector); };
setTimeout(waitForCb.bind(this, selector, resolve, action, newTimes), 500);
} else {
console.log("Selector detected", selector);
resolve();
}
}
function waitFor(selector, action, times) {
console.log("Waiting for", selector);
return new Promise(function(resolve) {
waitForCb(selector, resolve, action, times);
});
}
function nodesToArray(nodes) {
return Array.prototype.slice.call(nodes);
}
function getUrl(url) {
console.log("getting ", url);
return new Promise(function(resolve, reject) {
const oReq = new XMLHttpRequest();
oReq.addEventListener("load", function() {
console.log('done loading', url);
const text = this.responseText;
const fragment = document.createRange().createContextualFragment(text);
resolve(fragment);
});
oReq.addEventListener("error", function() {
reject();
});
oReq.open("GET", url);
oReq.send();
});
}
function base64Image(imageElem) {
try {
imageElem.setAttribute('crossOrigin', 'Anonymous');
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = imageElem.height;
canvas.width = imageElem.width;
ctx.drawImage(imageElem, 0, 0);
const dataUrl = canvas.toDataURL();
canvas.remove();
return dataUrl;
} catch (e) {
return imageElem.getAttribute('src'); // fuck it
}
}
function getImageData(url) {
return new Promise(function(resolve, reject) {
const oReq = new XMLHttpRequest();
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
resolve(base64Image(img));
};
img.src = url;
});
}
function commentsFromPersonDateElem(personPosts) {
const commentElems = nodesToArray(personPosts.querySelectorAll('.BFKlMc'));
return commentElems.map(function(commentElem) {
const textCommentElem = commentElem.querySelector('.li9sW');
const textComment = textCommentElem ? textCommentElem.innerText : null;
const linkCommentElem = commentElem.querySelector('c-wiz a.bC1hrb');
const linkComment = linkCommentElem ? linkCommentElem.getAttribute('href') : null;
const imageCommentElem = commentElem.querySelector('c-wiz .icqj1d img.JZUAbb');
const imageComment = imageCommentElem ? base64Image(imageCommentElem) : null;
const stickerCommentElem = commentElem.querySelector('.EjqmXc c-wiz img');
const stickerComment = stickerCommentElem ? base64Image(stickerCommentElem) : null;
let commentData = {content: null, type: null};
if (linkCommentElem) {
commentData.content = linkComment;
commentData.type = 'link';
} else if (textCommentElem) {
commentData.content = textComment;
commentData.type = 'text';
} else if (imageCommentElem){
commentData.content = imageComment;
commentData.type = 'image';
} else if (stickerCommentElem) {
commentData.content = stickerComment;
commentData.type = 'sticker';
} else {
console.log('could not extract data from comment ', commentElem);
commentData.type = 'unknown';
}
return commentData;
});
}
function commentsPromise(url) {
return getUrl(url)
.then(function(node) {
const peoplePosts = nodesToArray(node.querySelectorAll('.Y7XRIb'));
const multiDimension = peoplePosts.map(function(personPosts) {
const personDateElem = personPosts.querySelector('.m2QyPe');
if (personDateElem) {
const personDate = personDateElem.innerText.split(' • ');
const person = personDate[0];
const date = personDate[1];
const comments = commentsFromPersonDateElem(personPosts);
return comments.map(function(comment) {
return {
author: person,
date: date,
comment: comment
}
});
} else {
return false;
}
});
return all_comments = [].concat.apply([], multiDimension);
});
}
function dumpTextPost(item) {
const mainElem = item.querySelector('.jYqqT');
if (mainElem) {
const url = mainElem.getAttribute('data-lightboxlink');
const matches = url.match(/space\/(\d+)\/post\/([^/]+)\/content/);
const text = mainElem.innerText;
return {
postId: matches[2],
spaceId: matches[1],
type: 'text',
content: text
};
} else {
return false;
}
}
function dumpLinkPost(item) {
const metaElem = item.querySelector("meta");
if (metaElem) {
const baseData = JSON.parse(metaElem.getAttribute('content'));
return {
postId: baseData.postId,
spaceId: baseData.spaceId,
type: 'link',
content: baseData.link
};
} else {
return false;
}
}
function dumpVideoLinkPost(item) {
const mainElem = item.querySelector('c-wiz .NMDTWd.CCxxof');
if (mainElem) {
const url = mainElem.getAttribute('data-lightboxlink');
const urlMatches = url.match(/space\/(\d+)\/post\/([^/]+)\/content/);
const linkMatches = mainElem.getAttribute('jsdata').match(/;([^;]+);/);
return {
postId: urlMatches[2],
spaceId: urlMatches[1],
type: 'link',
content: linkMatches[1]
};
} else {
return false;
}
}
function dumpImagePost(item) {
const mainElem = item.querySelector('c-wiz .UXPon');
if (mainElem) {
const url = mainElem.getAttribute('data-lightboxlink');
const urlMatches = url.match(/space\/(\d+)\/post\/([^/]+)\/content/);
const imageUrl = mainElem.getAttribute('data-imageurl');
return getImageData(imageUrl).then(function(imageData) {
return {
postId: urlMatches[2],
spaceId: urlMatches[1],
type: 'image',
content: imageData
};
});
} else {
return false;
}
}
function dumpPost(item) {
const linkPost = dumpLinkPost(item);
const textPost = dumpTextPost(item);
const videoLinkPost = dumpVideoLinkPost(item);
const imagePost = dumpImagePost(item);
let result = false;
if (linkPost) {
result = linkPost;
} else if (textPost) {
result = textPost;
} else if (videoLinkPost) {
result = videoLinkPost;
} else if (imagePost) {
result = imagePost;
} else {
// debugger; maybe you'll come across something I didn't
}
return Promise.resolve(result);
}
function dumpPosts() {
const posts = nodesToArray(document.querySelectorAll(".c1tmJe"));
const mapped = posts.map(dumpPost);
return Promise.all(mapped).then(function(resolved) {
return resolved.filter(function(item) { return Boolean(item) });
});
}
function loadMore() {
console.log("loading more");
document.querySelector(".CtH2pf.eejsDc").scrollTop =
document.querySelector(".CtH2pf.eejsDc").scrollHeight
;
}
function loadAllContent() {
return waitFor(".kGA74e", loadMore).then(function() {
console.log("Dumping all posts");
return dumpPosts();
});
}
function postDataToUrl(postData) {
return `https://spaces.google.com/space/${postData.spaceId}/post/${postData.postId}`;
}
function dumpComments(posts) {
const commentPromises = posts.map(function(post) {
return commentsPromise(postDataToUrl(post));
});
return Promise.all(commentPromises);
}
function dumpCurrentSpace() {
waitFor(".OlExvc.W8JrHd").then(function() {
var name = document.querySelector(".OlExvc.W8JrHd").innerHTML;
loadAllContent().then(function(data) {
dumpComments(data).then(function (comments) {
const dataAndComments = data.map(function(post, index) {
return {
post: post,
comments: comments[index]
};
});
console.log(JSON.stringify(dataAndComments));
});
});
});
}
dumpCurrentSpace();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment