update
This commit is contained in:
@@ -1,2 +1,9 @@
|
|||||||
// TODO: Nachrichten vom Content Script empfangen und an Server senden
|
const SERVER_URL = "http://localhost:8000/videos";
|
||||||
console.log("Background Script geladen");
|
|
||||||
|
browser.runtime.onMessage.addListener((video) => {
|
||||||
|
fetch(SERVER_URL, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(video),
|
||||||
|
}).catch(() => {});
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,2 +1,144 @@
|
|||||||
// TODO: YouTube-DOM auslesen und Videodaten extrahieren
|
console.log("[YT-Erfasser] Content Script geladen");
|
||||||
console.log("YouTube Video Erfasser geladen");
|
|
||||||
|
const sentUrls = new Set();
|
||||||
|
|
||||||
|
function getVideoId(url) {
|
||||||
|
const match = url.match(/[?&]v=([^&]+)/);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getThumbnailUrl(videoId) {
|
||||||
|
return `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DOM Extractors ---
|
||||||
|
|
||||||
|
function extractFromLockupViewModel(element) {
|
||||||
|
const div = element.querySelector(".yt-lockup-view-model");
|
||||||
|
const classStr = div?.className || element.className || "";
|
||||||
|
const idMatch = classStr.match(/content-id-([\w-]+)/);
|
||||||
|
let videoId = idMatch ? idMatch[1] : null;
|
||||||
|
|
||||||
|
if (!videoId) {
|
||||||
|
const link = element.querySelector('a[href*="/watch?v="]');
|
||||||
|
if (link) videoId = getVideoId(link.href);
|
||||||
|
}
|
||||||
|
if (!videoId) return null;
|
||||||
|
|
||||||
|
const title =
|
||||||
|
element.querySelector("a.yt-lockup-metadata-view-model__title > span")
|
||||||
|
?.textContent?.trim() || "";
|
||||||
|
if (!title) return null;
|
||||||
|
|
||||||
|
const youtuber =
|
||||||
|
element.querySelector('a.yt-core-attributed-string__link[href^="/@"]')
|
||||||
|
?.textContent?.trim() || "Unbekannt";
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
youtuber,
|
||||||
|
thumbnail_url: getThumbnailUrl(videoId),
|
||||||
|
youtube_url: `https://www.youtube.com/watch?v=${videoId}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFromVideoRenderer(element) {
|
||||||
|
const titleLink = element.querySelector("a#video-title");
|
||||||
|
if (!titleLink) return null;
|
||||||
|
|
||||||
|
const videoId = getVideoId(titleLink.href);
|
||||||
|
if (!videoId) return null;
|
||||||
|
|
||||||
|
const title =
|
||||||
|
titleLink.textContent?.trim() ||
|
||||||
|
element.querySelector("#video-title yt-formatted-string")
|
||||||
|
?.textContent?.trim() || "";
|
||||||
|
if (!title) return null;
|
||||||
|
|
||||||
|
const youtuber =
|
||||||
|
element.querySelector("ytd-channel-name a")?.textContent?.trim() ||
|
||||||
|
element.querySelector(".ytd-channel-name a")?.textContent?.trim() ||
|
||||||
|
"Unbekannt";
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
youtuber,
|
||||||
|
thumbnail_url: getThumbnailUrl(videoId),
|
||||||
|
youtube_url: `https://www.youtube.com/watch?v=${videoId}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractVideoFromCard(element) {
|
||||||
|
const lockup = element.matches?.("yt-lockup-view-model")
|
||||||
|
? element
|
||||||
|
: element.querySelector("yt-lockup-view-model");
|
||||||
|
if (lockup) return extractFromLockupViewModel(lockup);
|
||||||
|
|
||||||
|
const renderer = element.matches?.("ytd-video-renderer")
|
||||||
|
? element
|
||||||
|
: element.querySelector("ytd-video-renderer");
|
||||||
|
if (renderer) return extractFromVideoRenderer(renderer);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Processing ---
|
||||||
|
|
||||||
|
function processCard(element) {
|
||||||
|
const video = extractVideoFromCard(element);
|
||||||
|
if (!video) return;
|
||||||
|
if (sentUrls.has(video.youtube_url)) return;
|
||||||
|
sentUrls.add(video.youtube_url);
|
||||||
|
console.log("[YT-Erfasser]", video.title, "-", video.youtuber);
|
||||||
|
browser.runtime.sendMessage(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
function scanExistingCards() {
|
||||||
|
document
|
||||||
|
.querySelectorAll("yt-lockup-view-model, ytd-video-renderer")
|
||||||
|
.forEach((el) => processCard(el));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- MutationObserver (debounced) ---
|
||||||
|
|
||||||
|
let pendingElements = [];
|
||||||
|
let debounceTimer = null;
|
||||||
|
|
||||||
|
function processPendingElements() {
|
||||||
|
const elements = pendingElements;
|
||||||
|
pendingElements = [];
|
||||||
|
debounceTimer = null;
|
||||||
|
|
||||||
|
for (const el of elements) {
|
||||||
|
if (el.matches?.("yt-lockup-view-model, ytd-video-renderer")) {
|
||||||
|
processCard(el);
|
||||||
|
}
|
||||||
|
el.querySelectorAll?.("yt-lockup-view-model, ytd-video-renderer").forEach(
|
||||||
|
(card) => processCard(card)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new MutationObserver((mutations) => {
|
||||||
|
for (const mutation of mutations) {
|
||||||
|
for (const node of mutation.addedNodes) {
|
||||||
|
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
||||||
|
pendingElements.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pendingElements.length > 0 && !debounceTimer) {
|
||||||
|
debounceTimer = setTimeout(processPendingElements, 250);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
|
||||||
|
// --- SPA Navigation ---
|
||||||
|
|
||||||
|
document.addEventListener("yt-navigate-finish", () => {
|
||||||
|
setTimeout(scanExistingCards, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- Init ---
|
||||||
|
|
||||||
|
scanExistingCards();
|
||||||
|
|||||||
Reference in New Issue
Block a user