Skip to content

Instantly share code, notes, and snippets.

@krashanoff
Last active August 15, 2021 19:23
Show Gist options
  • Save krashanoff/0915baf41d58655fda1517c8578165fa to your computer and use it in GitHub Desktop.
Save krashanoff/0915baf41d58655fda1517c8578165fa to your computer and use it in GitHub Desktop.
// twdl
//
// A Deno script for downloading images you like on Twitter.
//
import { parse } from "https://deno.land/std@0.104.0/flags/mod.ts";
import { copy, readerFromStreamReader } from "https://deno.land/std@0.104.0/io/mod.ts";
const { name = null, count = 200, out = "out", fmt = "", ...args } = parse(Deno.args);
if (!name) {
console.error("--name is required!");
Deno.exit(1);
}
const fetchLikes = async (count, screenName, maxID) => {
const res = await fetch(`https://api.twitter.com/1.1/favorites/list.json?count=${count}&screen_name=${screenName}${maxID ? `&max_id=${maxID}` : ""}&include_entities=true`, {
method: "get",
headers: {
"Authorization": "Bearer [YOUR TOKEN HERE]",
},
});
const body = await res.json();
return body;
};
const downloadLikeMedia = async like => {
// Skip posts without media.
if (!like.entities?.media) {
console.info(`Nothing useful in like ${like.id}`);
return;
}
// Download media from posts that have it.
await like.entities.media.forEach(async (media, idx1) => {
if (media.type !== "photo") {
console.info(`Media id ${media.id} is not a photo, skipping...`);
return;
}
// Get URL, filename, output file.
const url = media.media_url_https;
const fileName = `./out/${url.split('/').at(-1)}`;
const outputFile = await Deno.open(fileName, { write: true, create: true });
console.info(`Downloading ${url}...`);
const mediaFile = await fetch(url);
console.info(`Downloaded ${url}, writing...`);
// Write file.
const bodyReader = mediaFile.body.getReader();
await copy(readerFromStreamReader(bodyReader), outputFile);
console.info(`Wrote ${outputFile.statSync().size} bytes to \`${fileName}\`.`);
Deno.close(outputFile.rid);
});
};
// Try to prime output directory. If we fail, we assume that it already exists.
try {
await Deno.mkdir(out, { recursive: true });
} catch { }
// Fetch our initial query, if needed.
let max_id = "";
if (!max_id) {
const body = await fetchLikes(count, name, max_id);
await body.forEach(downloadLikeMedia);
max_id = body.at(-1).id;
}
console.info(`Set max_id to ${max_id}`);
// Fetch likes recursively.
while (max_id) {
const body = await fetchLikes(count, name, max_id);
await body.forEach(downloadLikeMedia);
max_id = body.at(-1).id;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment