A "set and forget" script. Sorts by Size (descending) and scans Media Quality on ALL Bunkr domains (present and future).
// ==UserScript==
// @name Bunkr Universal Suite (Auto-Sort & Scan)
// @namespace http://tampermonkey.net/
// @version 5.4
// @description A "set and forget" script. Sorts by Size (descending) and scans Media Quality on ALL Bunkr domains (present and future).
// @author You
// @license MIT
// @match *://bunkr.cr/*
// @match *://bunkr.is/*
// @match *://bunkr.to/*
// @match *://bunkr.ru/*
// @match *://bunkr.site/*
// @match *://bunkr.ph/*
// @match *://bunkr.media/*
// @match *://bunkr.black/*
// @include *://bunkr.*/*
// @include *://*.bunkr.*/*
// @include *://bunkrr.*/*
// @include *://*.bunkrr.*/*
// @include *://bunkr-albums.io/*
// @include *://*.bunkr-albums.io/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bunkr.cr
// @grant GM_xmlhttpRequest
// ==/UserScript==
// --- CHANGELOG ---
// v5.4 (2026-02-17): Added @include headers to support wildcard TLDs for broader compatibility per user feedback. (Coded by Gemini)
// v5.3 (2026-02-15): Added support for 'bunkr-albums.io' and ensuring all TLD variations are caught. (Coded by Gemini)
// v5.2 (2026-02-15): Added Changelog and License per user request. (Coded by Gemini)
// v5.1 (2026-02-15): Added MIT License and fixed namespace. (Coded by Gemini)
// v5.0 (2026-02-15): Rewrote Scanner to use Metadata Method (scrapes og:video tags) for reliability. Added Universal Domain Support. (Coded by Gemini)
// v4.0 (2026-02-15): Attempted Metadata scraping method. (Coded by Gemini)
// v3.1 (2026-02-15): Enhanced button injection for SPA navigation. (Coded by Gemini)
// v3.0 (2026-02-15): Attempted Download Link Method for probing. (Coded by Gemini)
// v2.0 (2026-02-15): Attempted Active Probe method. (Coded by Gemini)
// v1.1 (2026-02-15): Added Strict Mode for resolution detection. (Coded by Gemini)
// v1.0 (2026-02-15): Initial separate Quality Scanner script. (Coded by Gemini)
// v0.x (2026-02-15): Initial sorting prototypes. (Coded by Gemini)
// -----------------
(function() {
'use strict';
// --- CONFIGURATION ---
const CONFIG = {
SORT_DELAY: 500, // Delay between sort clicks
BUTTON_CHECK: 1000, // How often to check if "Scan" button is missing (ms)
};
// --- PART 1: AUTO-SORT LOGIC ---
let hasSorted = false;
let lastUrl = location.href;
function trySorting() {
// Reset sort trigger if URL changes (SPA navigation)
if (location.href !== lastUrl) {
lastUrl = location.href;
hasSorted = false;
}
if (hasSorted) return;
// Find the "Size" sort button by text content
const allElements = document.getElementsByTagName("*");
for (let el of allElements) {
if (el.textContent.trim() === "Size" && el.offsetParent !== null && el.tagName !== 'SCRIPT') {
const style = window.getComputedStyle(el);
// Check if already active (usually blue text or bg)
const isActive = el.classList.contains('active') ||
el.classList.contains('bg-blue-600') ||
el.classList.contains('text-blue-600') ||
(style.backgroundColor !== 'rgba(0, 0, 0, 0)' && style.backgroundColor !== 'transparent');
if (!isActive) {
el.click(); // Click once (Ascending)
setTimeout(() => el.click(), CONFIG.SORT_DELAY); // Click again (Descending)
hasSorted = true;
console.log("[Bunkr Suite] Sorted by Size Descending");
} else {
hasSorted = true; // Already active
}
break;
}
}
}
// --- PART 2: SCANNER UI ---
function createScanButton() {
if (document.getElementById('bunkr-scan-btn')) return;
const btn = document.createElement('button');
btn.id = 'bunkr-scan-btn';
btn.innerHTML = "🔍 Scan Quality";
btn.style.cssText = `
position: fixed;
top: 15px;
right: 80px;
z-index: 2147483647;
background: #e11d48;
color: white;
padding: 10px 16px;
border: 2px solid #ffffff;
border-radius: 8px;
cursor: pointer;
font-weight: bold;
font-size: 14px;
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
transition: all 0.2s;
`;
btn.onclick = startScan;
document.body.appendChild(btn);
}
// --- PART 3: SCANNER ENGINE (Metadata Method) ---
async function startScan() {
const btn = document.getElementById('bunkr-scan-btn');
// Select file links (usually contain /v/ for video or /f/ for file)
const links = Array.from(document.querySelectorAll('a[href*="/v/"], a[href*="/f/"]'));
if (links.length === 0) {
btn.innerText = "❌ No Files";
setTimeout(() => btn.innerText = "🔍 Scan Quality", 2000);
return;
}
btn.disabled = true;
btn.style.backgroundColor = "#555";
let successCount = 0;
for (let i = 0; i < links.length; i++) {
const link = links[i];
btn.innerText = `Scanning ${i+1}/${links.length}...`;
// Skip if already scanned
if (link.getAttribute('data-scanned') === 'true') continue;
try {
// Fetch page metadata (Fastest method, bypasses download blocks)
const pageData = await fetchPageMetadata(link.href);
if (pageData) {
injectInfo(link, pageData);
successCount++;
}
link.setAttribute('data-scanned', 'true');
} catch (e) {
console.warn("[Bunkr Suite] Scan error:", e);
}
// Throttle requests slightly to be safe
await new Promise(r => setTimeout(r, 150));
}
btn.innerText = `✅ Done (${successCount} found)`;
btn.style.backgroundColor = "#10b981";
setTimeout(() => {
btn.disabled = false;
btn.innerText = "🔍 Scan Quality";
btn.style.backgroundColor = "#e11d48";
}, 4000);
}
function fetchPageMetadata(pageUrl) {
return new Promise((resolve) => {
GM_xmlhttpRequest({
method: "GET",
url: pageUrl,
onload: function(res) {
const html = res.responseText;
// Regex for og:video tags (Standard Bunkr metadata)
const wMatch = html.match(/property="og:video:width"\s+content="(\d+)"/i);
const hMatch = html.match(/property="og:video:height"\s+content="(\d+)"/i);
// Regex for Duration (Used for bitrate calc)
const durMatch = html.match(/Duration:\s*(\d{1,2}):(\d{2})(?::(\d{2}))?/i);
let durationSec = 0;
if (durMatch) {
if (durMatch[3]) { // HH:MM:SS
durationSec = (parseInt(durMatch[1]) * 3600) + (parseInt(durMatch[2]) * 60) + parseInt(durMatch[3]);
} else { // MM:SS
durationSec = (parseInt(durMatch[1]) * 60) + parseInt(durMatch[2]);
}
}
if (wMatch && hMatch) {
resolve({
w: parseInt(wMatch[1]),
h: parseInt(hMatch[2]),
dur: durationSec
});
} else {
resolve(null);
}
},
onerror: () => resolve(null)
});
});
}
function injectInfo(linkElement, meta) {
// Find the "card" container
const card = linkElement.closest('div.relative') || linkElement.parentElement;
// Extract File Size from the card text
let sizeMB = 0;
const sizeMatch = card.innerText.match(/(\d+(\.\d+)?)\s*(MB|GB)/i);
if (sizeMatch) {
let val = parseFloat(sizeMatch[1]);
if (sizeMatch[3].toUpperCase() === 'GB') val *= 1024;
sizeMB = val;
}
// Calculate Bitrate
let bitrateStr = "";
let color = "#4ade80"; // Default Green
if (sizeMB > 0 && meta.dur > 0) {
const kbps = Math.round((sizeMB * 8192) / meta.dur);
bitrateStr = ` | ${kbps} kbps`;
// Color Coding
if (kbps > 5000) color = "#4ade80"; // Green (Excellent)
else if (kbps > 2000) color = "#facc15"; // Yellow (Good)
else color = "#f87171"; // Red (Low)
}
// Format Resolution
let res = `${meta.w}x${meta.h}`;
if ((meta.w===1920 || meta.h===1920) && (meta.w===1080 || meta.h===1080)) res = "1080p";
else if ((meta.w===1280 || meta.h===1280) && (meta.w===720 || meta.h===720)) res = "720p";
else if (meta.w>=3840 || meta.h>=3840) res = "4K";
// Create Badge
const badge = document.createElement('div');
badge.style.cssText = `
position: absolute;
bottom: 50px;
left: 5px;
background: rgba(0,0,0,0.95);
color: ${color};
font-size: 11px;
padding: 4px 6px;
border-radius: 4px;
z-index: 999;
font-family: monospace;
font-weight: bold;
border: 1px solid ${color};
pointer-events: none;
`;
badge.innerText = `📺 ${res}${bitrateStr}`;
if (getComputedStyle(card).position === 'static') card.style.position = 'relative';
card.appendChild(badge);
}
// --- MAIN LOOPS ---
setInterval(trySorting, 2000); // Run Sort Check
setInterval(createScanButton, 1000); // Run Button Check
})();