Update Mastodon.ts

Cleaned up code a bit.
master
Nes370 2026-10-11 16:41:41 -07:00
parent a6a4308276
commit 8004d0a183
1 changed files with 44 additions and 64 deletions

View File

@ -1,77 +1,45 @@
import { import { ScrapeEngineBase, ScrapeResult, ScrapedPost, ScrapeEngineFeature, ContentType} from "../ScrapeEngine.js";
ScrapeEngineBase,
ScrapeResult,
ScrapedPost,
ScrapeEngineFeature,
ContentType,
} from "../ScrapeEngine.js";
interface Attachment { interface Attachment {
url: string; // original media URL, ?sensitive=true stripped url: string; // original media URL
contentType: ContentType; contentType: ContentType;
} }
/**
* Handles Mastodon-compatible instances, including forks such as baraag.net.
*
* Mastodon renders media in three different shapes:
*
* 1. Images:
* <a class="media-gallery__item-thumbnail" href=".../original/....png">
* <img src=".../small/....png">
* </a>
* → read the anchor href.
*
* 2. GIFVs:
* <div class="media-gallery__gifv autoplay">
* <video class="media-gallery__item-gifv-thumbnail"
* src=".../original/....mp4" autoplay loop></video>
* </div>
* → read the <video> src (already the original .mp4).
*
* 3. Videos:
* <div role="menuitem" class="video-player">
* <video src=".../original/....mp4" poster=".../small/....png"></video>
* </div>
* → read the <video> src and use the poster attribute for extraContentUrl.
*/
export default class Mastodon extends ScrapeEngineBase { export default class Mastodon extends ScrapeEngineBase {
name = "mastodon"; name = "mastodon";
features: ScrapeEngineFeature[] = ["content", "source"]; features: ScrapeEngineFeature[] = ["content"];
notes = [ notes = [];
"Rating is assumed to be safe." supportedHosts = [
"aethy.com",
"baraag.net",
"mastodon.online",
"mastodon.social",
"mastodon.world",
"mstdn.jp",
"mstdn.social",
"pawoo.net"
]; ];
/**
supportedHosts = ["baraag.net"]; * Enabled for any other instances matching the pattern: .../@user/<postID>
*/
canImport(url: Location): boolean { canImport(url: Location): boolean {
if (this.supportedHosts.indexOf(url.host.replace("www.", "")) !== -1) { return this.supportedHosts.indexOf(url.host.replace("www.", "")) !== -1
return true; || /^\/@[^/]+\/\d+\/?$/.test(url.pathname);
}
// Generic Mastodon status URL shape: /@user/<numeric id>
return /^\/@[^/]+\/\d+\/?$/.test(url.pathname);
} }
scrapeDocument(document: Document): ScrapeResult { scrapeDocument(document: Document): ScrapeResult {
const result = new ScrapeResult(this.name); const result = new ScrapeResult(this.name);
const attachments = this.findAttachments(document); const attachments = this.findAttachments(document);
this.log(`Found ${attachments.length} attachment(s).`);
for (const att of attachments) { for (const att of attachments) {
const post = new ScrapedPost(); const post = new ScrapedPost();
post.pageUrl = document.location.href; post.pageUrl = document.location.href;
post.contentUrl = att.url; post.contentUrl = att.url;
post.contentType = att.contentType; post.contentType = att.contentType;
post.referrer = document.location.origin; post.referrer = document.location.origin;
result.tryAddPost(post); result.tryAddPost(post);
} }
if (attachments.length === 0) {
this.log("No media attachments found on this page.");
}
return result; return result;
} }
@ -85,7 +53,13 @@ export default class Mastodon extends ScrapeEngineBase {
out.push(att); out.push(att);
}; };
// 1. Images: anchor href points at the original. /**
* Images -> a[href]
*
* <a class="media-gallery__item-thumbnail" href=".../original/....png">
* <img src=".../small/....png">
* </a>
*/
let anchors = Array.from( let anchors = Array.from(
document.querySelectorAll<HTMLAnchorElement>("a.media-gallery__item-thumbnail[href]") document.querySelectorAll<HTMLAnchorElement>("a.media-gallery__item-thumbnail[href]")
); );
@ -100,7 +74,7 @@ export default class Mastodon extends ScrapeEngineBase {
const img = a.querySelector("img"); const img = a.querySelector("img");
const url = this.normalizeUrl(rawHref) || this.largestFromSrcset(img); const url = this.resolveUrl(rawHref) || this.largestFromSrcset(img);
if (!url) continue; if (!url) continue;
add({ add({
@ -109,13 +83,23 @@ export default class Mastodon extends ScrapeEngineBase {
}); });
} }
// 2 & 3. Videos and GIFVs: <video> elements carry the original .mp4 in src. /**
* GIFs and Videos -> video[src]
*
* <div class="media-gallery__gifv autoplay">
* <video class="media-gallery__item-gifv-thumbnail" src=".../original/....mp4" autoplay loop></video>
* </div>
*
* <div role="menuitem" class="video-player">
* <video src=".../original/....mp4" poster=".../small/....png"></video>
* </div>
*/
const videos = Array.from(document.querySelectorAll<HTMLVideoElement>("video[src]")); const videos = Array.from(document.querySelectorAll<HTMLVideoElement>("video[src]"));
for (const v of videos) { for (const v of videos) {
const rawSrc = v.getAttribute("src"); const rawSrc = v.getAttribute("src") || v.currentSrc;
if (!rawSrc) continue; if (!rawSrc) continue;
const url = this.normalizeUrl(rawSrc); const url = this.resolveUrl(rawSrc);
if (!url) continue; if (!url) continue;
add({ add({
@ -128,15 +112,11 @@ export default class Mastodon extends ScrapeEngineBase {
} }
/** /**
* Strip Mastodon's ?sensitive=true marker. The marker is metadata, not * Resolve possible relative media URLs against current page.
* part of the media URL, and leaving it in can confuse booru deduplication.
* Returns an empty string if the URL is unusable.
*/ */
private normalizeUrl(href: string): string { private resolveUrl(href: string): string {
try { try {
const u = new URL(href, document.location.href); return new URL(href, document.location.href).toString();
u.searchParams.delete("sensitive");
return u.toString();
} catch { } catch {
return href; return href;
} }