// ==UserScript==
// @name Remove & perma block instagram reels from Facebook
// @namespace IG GONE
// @version 1.2
// @description Automatically skip URLs that have been previously skipped and click "Next" if Instagram content is detected on Facebook Reels. Displays total skipped reels count in a popup and allows exporting skipped URLs to a text file using IndexedDB for storage.
// @author Projekt Darkside - Brno city Bohemia
// @match https://www.facebook.com/*
// @grant none
// ==/UserScript==
(async function() {
'use strict';
const reelDurationMinutes = 2;
let db;
// Vícejazyčná podpora pro tlačítko "Další karta"
const multilangNextButton = {
'en': 'Next card',
'cs': 'Další karta',
'sk': 'Ďalšia karta',
'fr': 'Carte suivante',
'es': 'Siguiente tarjeta',
'de': 'Nächste Karte',
'it': 'Prossima carta',
'pt': 'Próximo cartão',
'ru': 'Следующая карта',
'ja': '次のカード',
};
// Vícejazyčná podpora pro text v badge
const multilangBadgeText = {
'en': 'Skipped (count) IG reels, this script saved you (time) minutes.',
'cs': 'Přeskočeno (count) IG reelů, tento skript vám ušetřil (time) minut.',
'sk': 'Preskočených (count) IG reelov, tento skript vám ušetril (time) minút.',
'fr': '(count) IG reels ignorés, ce script vous a fait gagner (time) minutes.',
'es': 'Saltado (count) IG reels, este script te ahorró (time) minutos.',
'de': '(count) IG-Reels übersprungen, dieses Skript hat Ihnen (time) Minuten gespart.',
'it': 'Saltato (count) IG reels, questo script ti ha fatto risparmiare (time) minuti.',
'pt': 'Ignorado (count) IG reels, este script te poupou (time) minutos.',
'ru': 'Пропущено (count) IG рилов, этот скрипт сэкономил вам (time) минут.',
'ja': 'スキップされた (count) IGリール、このスクリプトはあなたの時間を (time) 分節約しました。',
};
async function openDatabase() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('InstagramReelsDB', 1);
request.onupgradeneeded = function(event) {
db = event.target.result;
if (!db.objectStoreNames.contains('skippedURLs')) {
db.createObjectStore('skippedURLs', { keyPath: 'url' });
}
};
request.onsuccess = function(event) {
db = event.target.result;
resolve(db);
};
request.onerror = function(event) {
reject('Error opening database: ' + event.target.errorCode);
};
});
}
async function saveSkippedURL(url) {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['skippedURLs'], 'readwrite');
const objectStore = transaction.objectStore('skippedURLs');
const request = objectStore.add({ url: url });
request.onsuccess = function(event) {
updateSkippedCountBadge();
resolve(event.target.result);
};
request.onerror = function(event) {
if (event.target.error.name === 'ConstraintError') {
resolve(false); // URL již existuje
} else {
reject('Error saving URL: ' + event.target.error);
}
};
});
}
async function getSkippedURLs() {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['skippedURLs']);
const objectStore = transaction.objectStore('skippedURLs');
const request = objectStore.getAll();
request.onsuccess = function(event) {
resolve(event.target.result);
};
request.onerror = function(event) {
reject('Error getting URLs: ' + event.target.error);
};
});
}
async function isURLSkipped(url) {
return new Promise((resolve, reject) => {
const transaction = db.transaction(['skippedURLs']);
const objectStore = transaction.objectStore('skippedURLs');
const request = objectStore.get(url);
request.onsuccess = function(event) {
resolve(!!event.target.result);
};
request.onerror = function(event) {
reject('Error checking URL: ' + event.target.error);
};
});
}
function getBadgeText(count, time) {
let language = detectBrowserLanguage().split('-')[0];
let template = multilangBadgeText[language] || multilangBadgeText['en'];
return template.replace('(count)', count).replace('(time)', time);
}
async function updateSkippedCountBadge() {
let skippedURLs = await getSkippedURLs();
let count = skippedURLs.length;
let savedTime = count * reelDurationMinutes;
let badge = document.getElementById('skippedCountBadge');
if (!badge) {
badge = document.createElement('div');
badge.id = 'skippedCountBadge';
badge.style.cssText = 'position: fixed; bottom: 10px; left: 10px; background-color: #007bff; color: #fff; border-radius: 10px; padding: 10px; cursor: pointer; z-index: 9999;';
badge.title = 'Celkový počet přeskočených Instagram Reels a ušetřený čas';
badge.onclick = exportSkippedURLs;
document.body.appendChild(badge);
}
badge.textContent = getBadgeText(count, savedTime);
}
function showAndAutoHideBadge() {
let badge = document.getElementById('skippedCountBadge');
if (badge) {
badge.style.display = 'block';
setTimeout(() => {
badge.style.display = 'none';
}, 4000);
}
}
async function exportSkippedURLs() {
let skippedURLs = await getSkippedURLs();
let urls = skippedURLs.map(item => item.url);
let blob = new Blob([urls.join('\n')], { type: 'text/plain' });
let url = URL.createObjectURL(blob);
let a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'skipped_urls.txt';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
}
async function skipInstagramContent() {
if (window.location.href.includes("/reel/")) {
let isSkipped = await isURLSkipped(window.location.href);
if (isSkipped) {
let nextButton = findNextButton();
if (nextButton) {
nextButton.click();
}
return;
}
let foundInstagramContent = false;
document.querySelectorAll('*').forEach(function(node) {
if (node.offsetParent !== null && node.innerText && node.innerText.includes("Instagram")) {
foundInstagramContent = true;
}
});
document.querySelectorAll('img').forEach(function(img) {
if (img.offsetParent !== null && img.src.includes('https://static.xx.fbcdn.net/rsrc.php/v3/yy/r/1M3EBv90kJA.png')) {
foundInstagramContent = true;
}
});
if (foundInstagramContent) {
let nextButton = findNextButton();
if (nextButton) {
await saveSkippedURL(window.location.href);
nextButton.click();
}
}
}
}
function findNextButton() {
let language = detectBrowserLanguage().split('-')[0];
let ariaLabel = multilangNextButton[language];
if (ariaLabel) {
return document.querySelector(`[aria-label="${ariaLabel}"]`);
} else {
return null;
}
}
function detectBrowserLanguage() {
return navigator.language || navigator.userLanguage || 'en';
}
window.addEventListener('load', async () => {
await openDatabase();
await updateSkippedCountBadge();
});
window.addEventListener('beforeunload', async () => {
await updateSkippedCountBadge();
});
const observer = new MutationObserver(skipInstagramContent);
observer.observe(document.body, { childList: true, subtree: true });
skipInstagramContent();
})();