update
This commit is contained in:
100
browser_extension/tracking/content.js
Normal file
100
browser_extension/tracking/content.js
Normal file
@@ -0,0 +1,100 @@
|
||||
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,
|
||||
thumbnail_url: thumbnail || `https://img.youtube.com/vi/${match[1]}/hqdefault.jpg`,
|
||||
youtube_url: `https://www.youtube.com/watch?v=${match[1]}`,
|
||||
};
|
||||
}
|
||||
|
||||
function collectVideos(elements) {
|
||||
const videos = [];
|
||||
for (const el of elements) {
|
||||
const video = extractVideoFromCard(el);
|
||||
if (!video) continue;
|
||||
if (sentUrls.has(video.youtube_url)) continue;
|
||||
sentUrls.add(video.youtube_url);
|
||||
videos.push(video);
|
||||
}
|
||||
return videos;
|
||||
}
|
||||
|
||||
// --- Debounced Batch-Versand ---
|
||||
|
||||
let pendingVideos = [];
|
||||
let sendTimer = null;
|
||||
|
||||
async function queueVideos(videos) {
|
||||
pendingVideos.push(...videos);
|
||||
if (!sendTimer) {
|
||||
sendTimer = setTimeout(async () => {
|
||||
if (pendingVideos.length > 0) {
|
||||
const stored = await browser.storage.local.get("profileId");
|
||||
const profileId = stored.profileId || null;
|
||||
const batch = pendingVideos.map((v) => ({ ...v, profile_id: profileId }));
|
||||
console.log(`[YT-Erfasser] ${batch.length} Videos senden (Profil: ${profileId})`);
|
||||
browser.runtime.sendMessage(batch);
|
||||
}
|
||||
pendingVideos = [];
|
||||
sendTimer = null;
|
||||
}, 250);
|
||||
}
|
||||
}
|
||||
|
||||
// --- IntersectionObserver: nur sichtbare Cards erfassen ---
|
||||
|
||||
const visibilityObserver = new IntersectionObserver((entries) => {
|
||||
const cards = entries.filter((e) => e.isIntersecting).map((e) => e.target);
|
||||
if (cards.length > 0) {
|
||||
queueVideos(collectVideos(cards));
|
||||
}
|
||||
}, { 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);
|
||||
Reference in New Issue
Block a user