Skip to content

Instantly share code, notes, and snippets.

@misaalanshori
Last active January 11, 2023 07:59
Show Gist options
  • Save misaalanshori/bfca28b1833d26b06d3547a647294b5a to your computer and use it in GitHub Desktop.
Save misaalanshori/bfca28b1833d26b06d3547a647294b5a to your computer and use it in GitHub Desktop.
Archive of Our Own Scripts

Archive of Our Own Scripts

Some scripts to do some stuff on AO3. These are not very good, but it works good enough for me, so whatever.

Also for these you will need to open AO3, make sure you're logged in, open a specific page (the history page for example), and then open and run the script in the Developer Tools console.

Scripts

• History Scraper

Scrapes a bunch of data from works in your history. You need to be in the history page

scrapped = (await Promise.all(Array.apply(undefined, Array(+Array.from(document.querySelectorAll("ol.pagination>li")).slice(-2,-1)[0].textContent)).map((c,i) => new Promise((rs, rej) => fetch(`https://archiveofourown.org/users/${document.querySelector("a.dropdown-toggle").textContent.split(" ")[1].slice(0, -1)}/readings?page=${i+1}`).then(
    (res) => res.text().then((text) => {
        let aopage = document.createElement("div")
        aopage.innerHTML = text
        rs(Array.from(aopage.querySelectorAll("ol.reading.work.index.group > li")).map(e => {return {
            id: e.id.split('_')[1],
            title: Array.from(e.querySelectorAll("h4.heading > a")).map(e=>e.textContent)[0],
            authors: Array.from(e.querySelectorAll('h4.heading > a[rel~="author"]')).map(el => {return {text: el.textContent.trim(), href: el.attributes.href.value}}),
            language: Array.from(e.querySelectorAll("dl.stats > dd.language")).map(v => v.textContent)[0],
            wordcount: +(Array.from(e.querySelectorAll("dl.stats > dd.words")).map(v => v.textContent)[0] || "").replace(",", ""),
            chapters: (Array.from(e.querySelectorAll("dl.stats > dd.chapters")).map(v => v.textContent)[0] || "?/?").split("/"),
            visited: e.querySelector("h4.viewed").innerText.trim().split(" ").at(-2) == "Visited" ? 1 : parseInt(e.querySelector("h4.viewed").innerText.trim().split(" ").at(-2)),
            lastvisit: e.querySelector("h4.viewed").innerText,
            rating: Array.from(e.querySelectorAll("span.rating")).map(v => v.classList[0])[0],
            tags: {
                warnings: Array.from(e.querySelectorAll("li.warnings")).map(e=>e.textContent),
                relationships: Array.from(e.querySelectorAll("li.relationships")).map(e=>e.textContent),
                characters: Array.from(e.querySelectorAll("li.characters")).map(e=>e.textContent),
                freeforms: Array.from(e.querySelectorAll("li.freeforms")).map(e=>e.textContent)
            },
            fandom: Array.from(e.querySelectorAll("h5.fandoms.heading > a.tag")).map(e=>e.textContent),
            updated: Array.from(e.querySelectorAll("p.datetime")).map(v => v.textContent)[0],
            kudos: +Array.from(e.querySelectorAll("dd.kudos")).map(v => v.textContent)[0],
            onpage: i + 1,
            summary: Array.from(e.querySelectorAll("blockquote.userstuff.summary > p")).map(v => v.textContent)
        }}))
    })
))))).flat()

Some code snippets/scripts using the scraped data

Filter works with updates available:

scrapped.filter(e => e.lastvisit.includes("Update"))

Sort by how many times you have re-read a work:

scrapped.sort((a,b) => (b.visited/+b.chapters[0])-(a.visited/+a.chapters[0]))

Counts all the tags from your history and sorts them:

// tags = scrapped.reduce((t,v) => {Object.values(v.tags).reduce((t,v) => t.concat(v),[]).forEach(v => t[v] = (t[v] || 0) + 1); return t},{}) // Combine all tags
tags = scrapped.reduce((t,v) => {v.tags.freeforms.forEach(v => t[v] = (t[v] || 0) + 1); return t},{}) // Freeform tags
tlist = Object.keys(tags).map((key) => [key, tags[key]])
tlist.sort((a,b) => b[1] - a[1])

Word Counter:

console.log("%c Words: " + Math.floor(scrapped.reduce((t, c) => t + (((+c.wordcount || 0) / (+c.chapters[0] || 1)) * (+c.visited || 1)), 0)), "font-size: 4rem")

Simple Filter and Sort Combination:

scrapped.filter(e=> +e.chapters[0] < 3 && e.kudos > 1000 && e.fandom.includes("The Owl House (Cartoon)")).sort((a,b) => b.kudos-a.kudos)

Render the results back to the page (also clears the original page)

document.body.innerHTML = ""
filtered.forEach(e=> document.body.innerHTML += `
<h2><a href="https://archiveofourown.org/works/${e.id}">${e.title}</a> by ${e.authors.map(author => `<a href="https://archiveofourown.org${author.href}"> ${author.text}</a>`).join(" ")}</h2>

<p><strong>(${e.fandom.join(", ")})</strong></p>
<p><strong>${e.tags.warnings.join(", ")}</strong></p>
<p>${e.tags.relationships.join(", ")}</p>
<p>${e.tags.characters.join(", ")}</p>
<p><span style="font-size:9px">${e.tags.freeforms.join(", ")}</span></p>

<blockquote>
<p>${e.summary.join("</br>")}</p>
</blockquote>

<p><strong>Kudos:</strong> ${e.kudos} | <strong>Chapters: </strong>${e.chapters.join("/")} | <strong>Words: </strong>${e.wordcount} | Updated: ${e.updated} | Visited: ${e.visited}x | Read Count: ${e.visited/+e.chapters[0]}</p>

<hr />
`)

• Word Counter

Scrapes your history and estimates how many words you have read. You need to be in the history page.
I don't think Archive of Our Own has any Reading Statistics feature that can show how many words you have read. So this is useful to calculate an estimate of how many words you have read.

console.log("%c Words: " + Math.floor((await Promise.all(Array.apply(undefined, Array(+Array.from(document.querySelectorAll("ol.pagination>li")).slice(-2,-1)[0].textContent)).map((c,i) => new Promise((rs, rej) => fetch(`https://archiveofourown.org/users/${document.querySelector("a.dropdown-toggle").textContent.split(" ")[1].slice(0, -1)}/readings?page=${i+1}`).then(
    (res) => res.text().then((text) => {
        let aopage = document.createElement("div")
        aopage.innerHTML = text
        rs(Array.from(aopage.querySelectorAll("ol.reading.work.index.group > li")).map(e => {return {
            wordcount: +(Array.from(e.querySelectorAll("dl.stats > dd.words")).map(v => v.textContent)[0] || "").replace(",", ""),
            chapters: (Array.from(e.querySelectorAll("dl.stats > dd.chapters")).map(v => v.textContent)[0] || "?/?").split("/"),
            visited: e.querySelector("h4.viewed").innerText.trim().split(" ").at(-2) == "Visited" ? 1 : parseInt(e.querySelector("h4.viewed").innerText.trim().split(" ").at(-2))
        }}))
    })
))))).flat().reduce((t, c) => t + (((+c.wordcount || 0) / (+c.chapters[0] || 1)) * (+c.visited || 1)), 0)), "font-size: 4rem")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment