Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions plugin/js/parsers/TranscendentalTlsParser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
parserFactory.register("transcendentaltls.com", () => new TranscendentalTlsParser());

class TranscendentalTlsParser extends Parser {
constructor() {
super();
this.minimumThrottle = 500;
}

async getChapterUrls(dom) {
// The table of contents uses tailscale css class names based on height,
// so using them as css selectors will most likely break quickly. For
// better stability, I'm creating the chapter urls from the api instead.
const urlObj = new URL(dom.baseURI);
const novelTag = urlObj.searchParams.get("novelTag");
if (!novelTag) return [];

const apiUrl = `https://transcendentaltls.com/api/general/novelDetails?novelTag=${novelTag}`;

try {
const response = await HttpClient.fetchJson(apiUrl);
const { chapters = [] } = response.json;

return chapters
.filter(ch => {
// Since the API highlights which chapters are available or
// locked, I chose to just filter the ones we can actually
// read
return ch.status?.type === "PUBLISHED" && !ch.isLocked;
})
.map(ch => {
const chapterUrl = `https://transcendentaltls.com/novelDetails/chapter?novelTag=${novelTag}&chapterNumber=${ch.chapterNumber}`;

const cleanTitle = ch.title && ch.title.trim() !== ""
? `Chapter ${ch.chapterNumber} - ${ch.title.replace(/^—\s*/, "").trim()}`
: `Chapter ${ch.chapterNumber}`;

return {
sourceUrl: chapterUrl,
title: cleanTitle,
newArc: null
};
})
// The API payload returns chapters inside reverse order (e.g.
// 315 down to 1) so I flipped it
.reverse();

} catch (err) {
throw new Error(`TranscendentTLSParser aborted while generating TOC from API: ${err.message}`);
}
}

async fetchChapter(url) {
// Since the site uses CSR, I use the api to get the chapter content
// instead
const urlObj = new URL(url);
const novelTag = urlObj.searchParams.get("novelTag");
const chapterNumber = urlObj.searchParams.get("chapterNumber");

if (!novelTag || !chapterNumber) {
return super.fetchChapter(url);
}

const apiUrl = `https://transcendentaltls.com/api/general/chapter?novelTag=${novelTag}&chapterNumber=${chapterNumber}`;

try {
const response = await HttpClient.fetchJson(apiUrl);
const { chapter, novel } = response.json;

const chapterTitle = chapter.title || `${novel.name} - Chapter ${chapter.chapterNumber}`;
const storyHtml = chapter.content?.html || "Failed to find content";

const virtualHtml = `
<html>
<body>
<h1 class="chapter-title">${chapterTitle}</h1>
<div class="chapter-content">${storyHtml}</div>
</body>
</html>
`;

return util.sanitize(virtualHtml);
} catch (err) {
throw new Error(`TranscendentTLSParser aborted while fetching chapter: ${err.message}`);
}
}

findContent(dom) {
return dom.querySelector(".chapter-content");
}

extractTitleImpl(dom) {
return dom.querySelector(".text-3xl");
}

findCoverImageUrl(dom) {
return util.getFirstImgSrc(dom, ".md\\:w-1\\/3 > div:nth-child(1)");
}
}
1 change: 1 addition & 0 deletions plugin/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ <h3>Instructions</h3>
<script src="js/parsers/ToctruyenParser.js"></script>
<script src="js/parsers/TomotranslationsParser.js"></script>
<script src="js/parsers/TongrenquanParser.js"></script>
<script src="js/parsers/TranscendentalTlsParser.js"></script>
<script src="js/parsers/TravistranslationsParser.js"></script>
<script src="js/parsers/TruyenFullVisionParser.js"></script>
<script src="js/parsers/TruyenmoikkParser.js"></script>
Expand Down
Loading