79 lines
2.4 KiB
JavaScript
79 lines
2.4 KiB
JavaScript
console.log("[YT-Erfasser] Content Script geladen");
|
|
|
|
const sentUrls = new Set();
|
|
|
|
function extractVideoFromCard(element) {
|
|
const link = element.querySelector('a[href*="/watch?v="]');
|
|
if (!link) return null;
|
|
|
|
const match = link.href.match(/[?&]v=([^&]+)/);
|
|
if (!match) return null;
|
|
|
|
const title = element.querySelector("h3 a")?.textContent?.trim();
|
|
if (!title) return null;
|
|
|
|
const thumbnail = element.querySelector('a[href*="/watch?v="] img')?.src;
|
|
const youtuber = element.querySelector('a[href^="/@"]')?.textContent?.trim() || "Unbekannt";
|
|
|
|
return {
|
|
title,
|
|
youtuber,
|
|
thumbnailUrl: thumbnail || `https://img.youtube.com/vi/${match[1]}/hqdefault.jpg`,
|
|
youtubeUrl: `https://www.youtube.com/watch?v=${match[1]}`,
|
|
};
|
|
}
|
|
|
|
async function sendVideo(video) {
|
|
const stored = await browser.storage.local.get("profileId");
|
|
const profileId = stored.profileId || null;
|
|
const payload = { ...video, profileId };
|
|
console.log(`[YT-Erfasser] Video senden (Profil: ${profileId})`, payload.title);
|
|
browser.runtime.sendMessage(payload);
|
|
}
|
|
|
|
// --- IntersectionObserver: nur sichtbare Cards erfassen ---
|
|
|
|
const visibilityObserver = new IntersectionObserver((entries) => {
|
|
for (const entry of entries) {
|
|
if (!entry.isIntersecting) continue;
|
|
const video = extractVideoFromCard(entry.target);
|
|
if (!video) continue;
|
|
if (sentUrls.has(video.youtubeUrl)) continue;
|
|
sentUrls.add(video.youtubeUrl);
|
|
sendVideo(video);
|
|
}
|
|
}, { threshold: 0.5 });
|
|
|
|
function observeCard(el) {
|
|
visibilityObserver.observe(el);
|
|
}
|
|
|
|
// --- MutationObserver: neue Cards registrieren ---
|
|
|
|
const mutationObserver = new MutationObserver((mutations) => {
|
|
for (const mutation of mutations) {
|
|
for (const node of mutation.addedNodes) {
|
|
if (node.nodeType !== Node.ELEMENT_NODE) continue;
|
|
if (node.matches?.("ytd-rich-item-renderer, ytd-video-renderer")) {
|
|
observeCard(node);
|
|
}
|
|
node.querySelectorAll?.("ytd-rich-item-renderer, ytd-video-renderer").forEach(observeCard);
|
|
}
|
|
}
|
|
});
|
|
|
|
mutationObserver.observe(document.body, { childList: true, subtree: true });
|
|
|
|
// --- SPA Navigation ---
|
|
|
|
document.addEventListener("yt-navigate-finish", () => {
|
|
sentUrls.clear();
|
|
setTimeout(() => {
|
|
document.querySelectorAll("ytd-rich-item-renderer, ytd-video-renderer").forEach(observeCard);
|
|
}, 500);
|
|
});
|
|
|
|
// --- Init ---
|
|
|
|
document.querySelectorAll("ytd-rich-item-renderer, ytd-video-renderer").forEach(observeCard);
|