You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
4.0 KiB

import Chapter from "./Chapter.ts";
import { DOMParser, Element, HTMLDocument, ID } from "../types.d.ts";
export default class Work {
#session: {
get: (path: string) => Promise<Response>;
};
/**
* ID of the work
*/
id: ID;
#document: HTMLDocument;
#DOMParser: DOMParser;
/**
* the name of the work
*/
name = "";
/**
* a list of Chapters in the work
*/
chapters: Chapter[] = [];
/**
* a list of tags in the work
*/
tags: string[] = [];
/**
* the approximate date the work was published
*/
published?: Date;
/**
* the approximate date the work was last updated
*/
updated?: Date;
/**
* represents a work on AO3
* @param id the ID of the work
* @param body the HTML body of the work (in text)
* @param session an axiod session (used for fetching additional details)
*/
constructor(
id: ID,
body: string,
session: {
get: (path: string) => Promise<Response>;
},
DOMParser: DOMParser,
) {
this.#session = session;
this.id = id;
this.#document = DOMParser.parseFromString(
body,
"text/html",
) as HTMLDocument;
this.#DOMParser = DOMParser;
}
//jank incarnate
async init() {
this.populateMetadata();
this.populateTags();
this.populateDates();
await this.populateChapters();
}
// deletes our #document if we want to clean up memory and not use a shitton
freeDocument() {
//completely sane behaviour
this.#document = undefined as unknown as HTMLDocument;
}
populateMetadata() {
this.name = (this.#document.querySelector("h2.title") as Element)
.innerText.trim();
}
populateTags() {
const elements = this.#document.querySelectorAll("dd > ul.commas > li");
for (let i = 0; i < elements.length; i++) {
this.tags.push((elements[i] as Element)?.innerText);
}
}
populateDates() {
this.published = new Date(
this.#document.querySelector("dd.published")?.innerText as string,
);
this.updated = new Date(
this.#document.querySelector("dd.status")?.innerText as string,
);
}
//CW: horrifying jank
async populateChapters() {
let firstChapterUrl = ""; //satisfy the typescript gods
try {
firstChapterUrl =
(this.#document.querySelector("li.chapter > a") as Element)
.getAttribute(
"href",
) as string + "?view_adult=true";
} catch {
//assume single chapter work
const newChapter = new Chapter(
this.id,
this.id,
this.#session,
this.#DOMParser,
{},
);
this.chapters.push(
newChapter,
);
return;
}
const res = await this.#session.get(firstChapterUrl);
const document = this.#DOMParser.parseFromString(
await res.text(),
"text/html",
) as HTMLDocument;
const elements = (Array.from(
document.querySelectorAll("#selected_id > option"),
) as Element[])
.sort(
(a: Element, b: Element) => {
return Number(a.getAttribute("value")) -
Number(b.getAttribute("value"));
},
);
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const newChapter = new Chapter(
this.id,
element.getAttribute("value") as string,
this.#session,
this.#DOMParser,
{ name: element.innerText },
);
this.chapters.push(
newChapter,
);
}
}
}