Add content script and popup for channel whitelist management
- Implement content script to identify member-only videos on YouTube - Create popup interface for managing a channel whitelist - Add necessary icons and manifest configuration
57
content.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
console.log("[MemberFilter] content script loaded");
|
||||||
|
|
||||||
|
const seen = new WeakSet();
|
||||||
|
|
||||||
|
function findMemberVideos(root = document) {
|
||||||
|
const badges = root.querySelectorAll(
|
||||||
|
'badge-shape, ytd-badge-supported-renderer'
|
||||||
|
);
|
||||||
|
|
||||||
|
badges.forEach(badge => {
|
||||||
|
if (!badge.textContent?.includes("Nur für Kanalmitglieder")) return;
|
||||||
|
|
||||||
|
const video =
|
||||||
|
badge.closest("ytd-rich-item-renderer, ytd-video-renderer, ytd-grid-video-renderer");
|
||||||
|
|
||||||
|
if (!video) return;
|
||||||
|
if (seen.has(video)) return;
|
||||||
|
|
||||||
|
seen.add(video);
|
||||||
|
|
||||||
|
// ---- extract data ----
|
||||||
|
|
||||||
|
const titleEl = video.querySelector("#video-title");
|
||||||
|
const channelEl =
|
||||||
|
video.querySelector("ytd-channel-name a") ||
|
||||||
|
video.querySelector(".ytd-channel-name a");
|
||||||
|
|
||||||
|
const title = titleEl?.textContent?.trim() ?? "(no title)";
|
||||||
|
const url = titleEl?.href ?? "(no url)";
|
||||||
|
const channel = channelEl?.textContent?.trim() ?? "(no channel)";
|
||||||
|
|
||||||
|
console.group("[MemberFilter] Member-only video found");
|
||||||
|
console.log("Title :", title);
|
||||||
|
console.log("Channel:", channel);
|
||||||
|
console.log("URL :", url);
|
||||||
|
console.log("Node :", video);
|
||||||
|
console.groupEnd();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial scan
|
||||||
|
findMemberVideos();
|
||||||
|
|
||||||
|
// Observe dynamic changes
|
||||||
|
const observer = new MutationObserver(mutations => {
|
||||||
|
for (const m of mutations) {
|
||||||
|
for (const node of m.addedNodes) {
|
||||||
|
if (!(node instanceof HTMLElement)) continue;
|
||||||
|
findMemberVideos(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true
|
||||||
|
});
|
||||||
BIN
icons/anti-bs-icon-1024.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
icons/anti-bs-icon-128.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
icons/anti-bs-icon-16.png
Normal file
|
After Width: | Height: | Size: 409 B |
BIN
icons/anti-bs-icon-256.png
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
BIN
icons/anti-bs-icon-32.png
Normal file
|
After Width: | Height: | Size: 872 B |
BIN
icons/anti-bs-icon-48.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
icons/anti-bs-icon-512.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
icons/anti-bs-icon-64.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
23
manifest.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "ANTI-BS",
|
||||||
|
"description": "for to long youtubers thought we are stupid, now we just ignore them. let them know we are not.",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"author": "Chipperfluff (Jack)",
|
||||||
|
"repo": "<placeholder for now>",
|
||||||
|
"org": "Chipperfluff",
|
||||||
|
|
||||||
|
"permissions": ["storage"],
|
||||||
|
"host_permissions": ["https://www.youtube.com/*"],
|
||||||
|
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": ["https://www.youtube.com/*"],
|
||||||
|
"js": ["content.js"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
"action": {
|
||||||
|
"default_popup": "popup.html"
|
||||||
|
}
|
||||||
|
}
|
||||||
14
popup.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
min-width: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
18
popup.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Member Filter</title>
|
||||||
|
<link rel="stylesheet" href="popup.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h3>Channel Whitelist</h3>
|
||||||
|
|
||||||
|
<input id="channelInput" placeholder="Channel name" />
|
||||||
|
<button id="addBtn">Add</button>
|
||||||
|
|
||||||
|
<ul id="channelList"></ul>
|
||||||
|
|
||||||
|
<script src="popup.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
42
popup.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const input = document.getElementById("channelInput");
|
||||||
|
const addBtn = document.getElementById("addBtn");
|
||||||
|
const list = document.getElementById("channelList");
|
||||||
|
|
||||||
|
function loadChannels() {
|
||||||
|
chrome.storage.local.get({ whitelist: [] }, ({ whitelist }) => {
|
||||||
|
list.innerHTML = "";
|
||||||
|
whitelist.forEach((name, index) => {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.textContent = name;
|
||||||
|
|
||||||
|
const remove = document.createElement("button");
|
||||||
|
remove.textContent = "x";
|
||||||
|
remove.onclick = () => removeChannel(index);
|
||||||
|
|
||||||
|
li.appendChild(remove);
|
||||||
|
list.appendChild(li);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addChannel() {
|
||||||
|
const name = input.value.trim();
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
chrome.storage.local.get({ whitelist: [] }, ({ whitelist }) => {
|
||||||
|
whitelist.push(name);
|
||||||
|
chrome.storage.local.set({ whitelist }, loadChannels);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeChannel(index) {
|
||||||
|
chrome.storage.local.get({ whitelist: [] }, ({ whitelist }) => {
|
||||||
|
whitelist.splice(index, 1);
|
||||||
|
chrome.storage.local.set({ whitelist }, loadChannels);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addBtn.onclick = addChannel;
|
||||||
|
loadChannels();
|
||||||